From 13d03631a82dc76c186636c909b0e2a855ad6172 Mon Sep 17 00:00:00 2001 From: Lingbo Liu Date: Fri, 8 Aug 2025 13:21:39 -0400 Subject: [PATCH 1/4] add label and scrolling control to geospatial view node add label and scrolling control to geospatial view node --- .../leaflet/1.9.3/images/marker-shadow.png | Bin 0 -> 618 bytes knime_extension/src/nodes/visualize.py | 121 ++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 knime_extension/libs/leaflet/1.9.3/images/marker-shadow.png diff --git a/knime_extension/libs/leaflet/1.9.3/images/marker-shadow.png b/knime_extension/libs/leaflet/1.9.3/images/marker-shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..9fd2979532a19a15b824ce763c76e04a8dafadfb GIT binary patch literal 618 zcmV-w0+s!VP)ke9$Lam@{1K@O ze*LXqlKQHiv=gx+V^Cbb2?z@ISBQ*3amF;9UJ3SBg(N|710TLamQmYZ&Qjn2LuO<* zCZlB4n%@pc&7NNnY1}x+NWpHlq`OJEo|`aYN9<`RBUB+79g;>dgb6YlfN#kGL?lO_ z!6~M^7sOnbsUkKk<@Ysie&`G>ruxH&Mgy&8;i=A zB9OO!xR{AyODw>DS-q5YM{0ExFEAzt zm>RdS+ssW(-8|?xr0(?$vBVB*%(xDLtq3Hf0I5yFm<_g=W2`QWAax{1rWVH=I!VrP zs(rTFX@W#t$hXNvbgX`gK&^w_YD;CQ!B@e0QbLIWaKAXQe2-kkloo;{iF#6}z!4=W zi$giRj1{ zt;2w`VSCF#WE&*ev7jpsC=6175@(~nTE2;7M-L((0bH@yG}-TB$R~WXd?tA$s3|%y zA`9$sA(>F%J3ioz<-LJl*^o1|w84l>HBR`>3l9c8$5Xr@xCiIQ7{x$fMCzOk_-M=% z+{a_Q#;42`#KfUte@$NT77uaTz?b-fBe)1s5XE$yA79fm?KqM^VgLXD07*qoM6N<$ Ef<_J(9smFU literal 0 HcmV?d00001 diff --git a/knime_extension/src/nodes/visualize.py b/knime_extension/src/nodes/visualize.py index e4dc1e77..613a85d2 100644 --- a/knime_extension/src/nodes/visualize.py +++ b/knime_extension/src/nodes/visualize.py @@ -1,6 +1,7 @@ import geopandas as gp import knime_extension as knext import util.knime_utils as knut +import pandas as pd category = knext.category( @@ -357,6 +358,60 @@ class BaseMapSettings: ) +@knext.parameter_group(label="Label Settings") +class LabelSettings: + """Settings for displaying labels on points and polygons.""" + + show_labels = knext.BoolParameter( + "Show labels", + "If checked, labels will be displayed on the map for points and polygons.", + default_value=False, + ) + + label_column = knext.ColumnParameter( + "Label column", + "Select the column to use as labels. This column should contain text values.", + column_filter=knut.is_string, + include_row_key=False, + include_none_column=True, + ).rule(knext.OneOf(show_labels, [True]), knext.Effect.SHOW) + + label_color = knext.StringParameter( + "Label color", + "Select the color for the labels.", + default_value="black", + enum=[ + "black", + "white", + "red", + "blue", + "green", + "orange", + "purple", + "brown", + "gray", + ], + is_advanced=True, + ).rule(knext.OneOf(show_labels, [True]), knext.Effect.SHOW) + + label_size = knext.IntParameter( + "Label font size", + "Select the font size for the labels.", + default_value=12, + min_value=8, + max_value=24, + is_advanced=True, + ).rule(knext.OneOf(show_labels, [True]), knext.Effect.SHOW) + + label_weight = knext.StringParameter( + "Label font weight", + "Select the font weight for the labels.", + default_value="normal", + enum=["normal", "bold"], + is_advanced=True, + ).rule(knext.OneOf(show_labels, [True]), knext.Effect.SHOW) + + @knext.node( name="Geospatial View", node_type=knext.NodeType.VISUALIZER, @@ -418,6 +473,14 @@ class ViewNode: legend_settings = LegendSettings() + label_settings = LabelSettings() + + disable_scroll_zoom = knext.BoolParameter( + "Disable scroll zoom", + "If checked, disables scroll wheel zoom on the map.", + default_value=False, + ) + def configure(self, configure_context, input_schema): self.geo_col = knut.column_exists_or_preset( configure_context, self.geo_col, input_schema, knut.is_geo @@ -437,6 +500,12 @@ def execute(self, exec_context: knext.ExecutionContext, input_table): selected_col_names.add(self.color_settings.color_col) if "none" not in str(self.size_settings.size_col).lower(): selected_col_names.add(self.size_settings.size_col) + if ( + self.label_settings.show_labels + and self.label_settings.label_column is not None + ): + selected_col_names.add(self.label_settings.label_column) + filtered_table = input_table[list(selected_col_names)] gdf = gp.GeoDataFrame(filtered_table.to_pandas(), geometry=self.geo_col) @@ -562,6 +631,58 @@ def execute(self, exec_context: knext.ExecutionContext, input_table): kws["style_kwds"] = {"stroke": False} map = gdf.explore(**kws) + # Disable scroll zoom if enabled + if self.disable_scroll_zoom: + # Disable scroll wheel zoom directly without showing toggle button + map.options["scrollWheelZoom"] = False + + # Add labels if enabled + if ( + self.label_settings.show_labels + and self.label_settings.label_column is not None + ): + import folium + + for idx, row in gdf.iterrows(): + if pd.notna(row[self.label_settings.label_column]): + # Get geometry representative point for label placement + if row[self.geo_col].geom_type == "Point": + coords = [row[self.geo_col].y, row[self.geo_col].x] + else: + # Use representative_point() instead of centroid to ensure point is inside geometry + rep_point = row[self.geo_col].representative_point() + coords = [rep_point.y, rep_point.x] + + # Create a simple text label that will be added to the map + # We'll use a marker with a custom icon that contains just text + text_html = f""" +
+ {row[self.label_settings.label_column]} +
+ """ + + # Create a transparent marker with text + icon = folium.DivIcon( + html=text_html, + icon_size=(100, 20), + icon_anchor=(50, 10), + ) + + folium.Marker( + location=coords, + icon=icon, + popup=row[self.label_settings.label_column], + shadow=False, + ).add_to(map) + # replace css and JavaScript paths html = map.get_root().render() html = replace_external_js_css_paths( From dadd7291641ff7ba526fa330c7d71a0b0075678f Mon Sep 17 00:00:00 2001 From: Tobias Koetter Date: Fri, 29 Aug 2025 16:05:25 +0200 Subject: [PATCH 2/4] Update extension version to 2.1.0 --- knime_extension/knime.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/knime_extension/knime.yml b/knime_extension/knime.yml index 316bce35..99b13afe 100644 --- a/knime_extension/knime.yml +++ b/knime_extension/knime.yml @@ -4,7 +4,7 @@ author: Lingbo Liu,Xiaokang Fu,Tobias Koetter vendor: SDL, Harvard, Cambridge US description: Geospatial Analytics Extension for KNIME # Human readable bundle name / description long_description: KNIME nodes for processing, analyzing and visualizing Geospatial data. -version: 2.0.0 # Version of this Python node extension +version: 2.1.0 # Version of this Python node extension license_file: LICENSE.TXT # Best practice: put your LICENSE.TXT next to the knime.yml; otherwise you would need to change to path/to/LICENSE.txt extension_module: src/geospatial_ext # The .py Python module containing the nodes of your extension pixi_toml_path: pixi.toml # This is necessary for bundling, but not needed during development From 6c6d5dc9c504255ab2f9643f80566bd60e8afa6c Mon Sep 17 00:00:00 2001 From: Tobias Koetter Date: Fri, 29 Aug 2025 16:05:31 +0200 Subject: [PATCH 3/4] Add since version to support backward compatible loading --- knime_extension/src/nodes/visualize.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/knime_extension/src/nodes/visualize.py b/knime_extension/src/nodes/visualize.py index 613a85d2..1155e5df 100644 --- a/knime_extension/src/nodes/visualize.py +++ b/knime_extension/src/nodes/visualize.py @@ -358,7 +358,7 @@ class BaseMapSettings: ) -@knext.parameter_group(label="Label Settings") +@knext.parameter_group(label="Label Settings", since_version="2.1.0") class LabelSettings: """Settings for displaying labels on points and polygons.""" @@ -479,6 +479,7 @@ class ViewNode: "Disable scroll zoom", "If checked, disables scroll wheel zoom on the map.", default_value=False, + since_version="2.1.0", ) def configure(self, configure_context, input_schema): From 8f3bb4265f08931de08d967666fad8f9cb03de4f Mon Sep 17 00:00:00 2001 From: Lingbo Liu Date: Fri, 5 Sep 2025 10:43:03 -0400 Subject: [PATCH 4/4] move disable_scroll_zoom to base map setting move disable_scroll_zoom to base map setting --- knime_extension/src/nodes/visualize.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/knime_extension/src/nodes/visualize.py b/knime_extension/src/nodes/visualize.py index 1155e5df..3759cf53 100644 --- a/knime_extension/src/nodes/visualize.py +++ b/knime_extension/src/nodes/visualize.py @@ -357,6 +357,13 @@ class BaseMapSettings: ], ) + disable_scroll_zoom = knext.BoolParameter( + "Disable scroll zoom", + "If checked, disables scroll wheel zoom on the map.", + default_value=False, + since_version="2.1.0", + ) + @knext.parameter_group(label="Label Settings", since_version="2.1.0") class LabelSettings: @@ -475,13 +482,6 @@ class ViewNode: label_settings = LabelSettings() - disable_scroll_zoom = knext.BoolParameter( - "Disable scroll zoom", - "If checked, disables scroll wheel zoom on the map.", - default_value=False, - since_version="2.1.0", - ) - def configure(self, configure_context, input_schema): self.geo_col = knut.column_exists_or_preset( configure_context, self.geo_col, input_schema, knut.is_geo @@ -633,7 +633,7 @@ def execute(self, exec_context: knext.ExecutionContext, input_table): map = gdf.explore(**kws) # Disable scroll zoom if enabled - if self.disable_scroll_zoom: + if self.basemap_setting.disable_scroll_zoom: # Disable scroll wheel zoom directly without showing toggle button map.options["scrollWheelZoom"] = False