Skip to content

Commit e13d1e7

Browse files
committed
selection sync between table and map and reflecting table filtered view on map
1 parent 2a1d0cb commit e13d1e7

File tree

1 file changed

+33
-33
lines changed

1 file changed

+33
-33
lines changed

pydelmod/dataui.py

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ class DataUI(param.Parameterized):
120120
show_map_markers = param.Boolean(
121121
default=False, doc="Show map markers for selected category"
122122
)
123+
map_default_span = param.Number(default=15000, doc="Default span for map zoom")
124+
123125
query = param.String(
124126
default="",
125127
doc='Query to filter stations. See <a href="https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.query.html">Pandas Query</a> for details. E.g. max_year <= 2023',
@@ -146,6 +148,10 @@ def __init__(
146148
if isinstance(self.dfcat, gpd.GeoDataFrame):
147149
self.tmap = gv.tile_sources.CartoLight()
148150
self.build_map_of_features(self.dfmapcat, crs=self.crs)
151+
if hasattr(self, "station_select"):
152+
self.station_select.source = self.map_features
153+
else:
154+
self.station_select = streams.Selection1D(source=self.map_features)
149155
else:
150156
warnings.warn(
151157
"No geolocation data found in catalog. Not displaying map of stations."
@@ -215,10 +221,6 @@ def build_map_of_features(self, dfmap, crs):
215221
self.map_features = self.map_features.opts(
216222
active_tools=["wheel_zoom"], responsive=True
217223
)
218-
if hasattr(self, "station_select"):
219-
self.station_select.source = self.map_features
220-
else:
221-
self.station_select = streams.Selection1D(source=self.map_features)
222224
return self.map_features
223225

224226
def update_map_features(
@@ -229,6 +231,7 @@ def update_map_features(
229231
marker_by,
230232
query,
231233
filters,
234+
selection,
232235
):
233236
query = query.strip()
234237
dfs = self._get_map_catalog()
@@ -244,6 +247,11 @@ def update_map_features(
244247
]
245248
else:
246249
dfs = dfs.iloc[self.display_table.current_view.index]
250+
merged_view = self.display_table.selected_dataframe.merge(
251+
self.display_table.current_view.reset_index(drop=True).reset_index(),
252+
how="inner",
253+
)
254+
current_selection = merged_view["index"].to_list()
247255

248256
try:
249257
if len(query) > 0:
@@ -257,7 +265,6 @@ def update_map_features(
257265
self.map_color_category = color_by
258266
self.show_map_colors = show_color_by
259267
self.build_map_of_features(dfs, self.crs)
260-
self.map_features.data = dfs
261268
if isinstance(self.map_features, gv.Points):
262269
if show_marker_by:
263270
self.map_features = self.map_features.opts(
@@ -267,12 +274,13 @@ def update_map_features(
267274
)
268275
else:
269276
self.map_features = self.map_features.opts(marker="circle")
270-
return self.tmap * self.map_features.opts(default_span=15000)
277+
self.map_features = self.map_features.opts(
278+
default_span=self.map_default_span, # for max zoom this is the default span in meters
279+
selected=current_selection,
280+
)
281+
return self.map_features
271282

272-
def show_data_catalog(self, index=slice(None)):
273-
# called when map selects stations
274-
if index == []:
275-
index = slice(None)
283+
def select_data_catalog(self, index=[]):
276284
# select rows from self.dfcat where station_id is in dfs station_ids
277285
if self.station_id_column and self.station_id_column in self.dfcat.columns:
278286
dfs = (
@@ -282,24 +290,19 @@ def show_data_catalog(self, index=slice(None)):
282290
.first()
283291
.reset_index()
284292
)
285-
dfs = self.dfcat[
293+
selected_indices = self.dfcat[
286294
self.dfcat[self.station_id_column].isin(dfs[self.station_id_column])
287-
]
288-
else:
289-
dfs = self.dfcat.iloc[index]
290-
dfs = dfs[self.dataui_manager.get_table_columns()]
291-
# return a UI with controls to plot and show data
292-
return self.update_data_table(dfs)
293-
294-
def update_data_table(self, dfs):
295-
if not hasattr(self, "display_table"):
296-
print("Warning: display_table not found")
295+
].index.to_list()
297296
else:
298-
self.display_table.value = dfs
299-
return self.display_table
297+
dfs = self.map_features.dframe().iloc[index]
298+
selected_indices = self.dfcat.reset_index().merge(dfs)["index"].to_list()
299+
self.display_table.param.set_param(
300+
selection=selected_indices
301+
) # set the selection in the table without triggering the callback
300302

301303
def create_data_table(self, dfs):
302304
column_width_map = self.dataui_manager.get_table_column_width_map()
305+
hidden_columns = set(dfs.columns) - set(column_width_map.keys())
303306
self.display_table = pn.widgets.Tabulator(
304307
dfs,
305308
disabled=True,
@@ -308,6 +311,7 @@ def create_data_table(self, dfs):
308311
sizing_mode="stretch_width",
309312
header_filters=self.dataui_manager.get_table_filters(),
310313
page_size=200,
314+
hidden_columns=list(hidden_columns),
311315
)
312316

313317
self.plot_button = pn.widgets.Button(
@@ -328,22 +332,15 @@ def create_data_table(self, dfs):
328332
self.download_button,
329333
pn.layout.HSpacer(),
330334
)
331-
if hasattr(self, "station_select"):
332-
show_data_catalog_bound = pn.bind(
333-
self.show_data_catalog, index=self.station_select.param.index
334-
)
335-
else:
336-
show_data_catalog_bound = pn.bind(self.show_data_catalog)
337335
gspec[0, 0:5] = self.table_panel
338-
gspec[1:5, 0:10] = fullscreen.FullScreen(pn.Row(show_data_catalog_bound))
336+
gspec[1:5, 0:10] = fullscreen.FullScreen(pn.Row(self.display_table))
339337
gspec[6:15, 0:10] = fullscreen.FullScreen(self.plots_panel)
340338
return gspec
341339

342340
def update_plots(self, event):
343341
try:
344342
self.plots_panel.loading = True
345343
dfselected = self.display_table.value.iloc[self.display_table.selection]
346-
# self.update_map_zoom(event)
347344
plot_panel = self.dataui_manager.create_panel(dfselected)
348345
if isinstance(self.plots_panel.objects[0], pn.Tabs):
349346
tabs = self.plots_panel.objects[0]
@@ -434,7 +431,7 @@ def create_view(self):
434431
self.param.map_marker_category,
435432
self.param.query,
436433
)
437-
self.map_function = pn.panel(
434+
self.map_function = hv.DynamicMap(
438435
pn.bind(
439436
self.update_map_features,
440437
show_color_by=self.param.show_map_colors,
@@ -443,15 +440,18 @@ def create_view(self):
443440
marker_by=self.param.map_marker_category,
444441
query=self.param.query,
445442
filters=self.display_table.param.filters,
443+
selection=self.display_table.param.selection,
446444
)
447445
)
446+
self.station_select.source = self.map_function
447+
self.station_select.param.watch_values(self.select_data_catalog, "index")
448448
map_tooltip = pn.widgets.TooltipIcon(
449449
value="""Map of geographical features. Click on a feature to see data available in the table. <br/>
450450
See <a href="https://docs.bokeh.org/en/latest/docs/user_guide/interaction/tools.html">Bokeh Tools</a> for toolbar operation"""
451451
)
452452
map_view = fullscreen.FullScreen(
453453
pn.Column(
454-
self.map_function,
454+
self.tmap * self.map_function,
455455
map_tooltip,
456456
min_width=450,
457457
min_height=550,

0 commit comments

Comments
 (0)