geoviews
geoviews copied to clipboard
Linked brushing with link_selections doesn't work
Using holoviews.selection.link_selections
with a GeoDataFrame plot + histogram plot, brushing only works in the histogram but not in the map plot.
Tested with holoviews 1.14.2, geopandas 0.9.0, bokeh 2.3.0.
A complete example is available at: https://mybinder.org/v2/gh/anitagraser/movingpandas-examples/main?filepath=tech-demos/linked-brushing.ipynb (particularly the Geopandas GeoDataFrame.hvplot section; the other sections are for comparison)
Alternatively, you can also see the issue here: https://youtu.be/OgJH0xUie8k?t=95
I can replicate the issue using your binder link, thanks. I can't tell if it has to do with it being a GeoDataFrame, because your first and second examples differ not only in using GeoPandas but also because the second one uses GeoViews, which has to project the points for display. I would suspect that it's more about the projections not being handled in the linking than about GeoPandas, and indeed it doesn't work when I change your second example to replace gdf_map = gdf.hvplot(geo=True, tiles='CartoLight', width=400, height=300)
with a standard-Pandas GeoViews version:
gdf.loc[:, 'x'], gdf.loc[:, 'y'] = (gdf.geometry.x, gdf.geometry.y)
gdf_map = pd.DataFrame(gdf).hvplot.points(x='x',y='y', geo=True, tiles='CartoLight', width=400, height=300)
But that didn't really clear anything up, because the Pandas GeoViews version behaves differently. Before making a selection on the map the Pandas GeoViews version is fine: After, the geo plot looks blank: It's not actually blank; if you zoom out, you can see a blue dot where the original data points are, and a tiny black dot at Null Island (0,0 lat/lon): If you zoom in on Null Island, it's clear that the selection box is drawn as usual, but very, very tiny and not in the right spot: So there definitely seem to be projection-related issues with linking, and there may also be some separate GeoDataFrame issue (can't tell yet). @philippjfr ?
The original issue can be reproduced with the following example:
import geopandas as gpd
import geoviews as gv
import holoviews as hv
from holoviews.selection import link_selections
import pandas as pd
gv.extension('bokeh')
gdf = gpd.read_file('../data/ais.gpkg', rows=1000)
pts = gv.Points(gdf, vdims=['SOG'])
df = pd.DataFrame(gdf)
subdf = df.where((df.SOG>0) & (df.SOG<50))
ds = hv.Dataset(subdf)
hist = hv.operation.histogram(ds, dimension='SOG', bins=20).opts(width=400, height=300)
link_selections(pts + hist)
I get this warning when trying to box-select points on the map:
linked_selection aborted because it could not display selection for all elements: One or more dimensions in the expression ((dim('Longitude')>=11838135905465984)&(dim('Longitude')<=11858164865448021))&((dim('Latitude')>=5768261393401959)&(dim('Latitude')<=5768874941228565)) could not resolve on ':Dataset [Timestamp,MMSI,NavStatus,SOG,COG,Name,ShipType,geometry,x,y]' Ensure all dimensions referenced by the expression are present on the supplied object on ':Histogram [SOG] (SOG_count)'.
As nicely explained by the warning, the Latitude/Longitude dimensions aren't found in the histogram object so the linked selection that is based on dimension ranges fails going from the points to the histogram. But as theSOG
dimension is found in both objects dimensions, the linked selection going the other way works as expected.
It is the creation of the gv.Points
object that derives the Latitude and Longitude dimensions from the GeoDataFrame geometry column. This is also what happens when an hvplot object is created:
One immediate workaround is to setup an ID in the GeoDataFrame and use the index_cols
List parameter offered by link_selections
to link the plots based on indexes instead of dimension ranges.
import geopandas as gpd
import hvplot.pandas
import numpy as np
import pandas as pd
gdf = gpd.read_file('../data/ais.gpkg', rows=1000)
gdf['id'] = np.arange(len(gdf))
gdf_map = gdf.hvplot(geo=True, tiles='CartoLight', width=400, height=300)
gdf_hist = pd.DataFrame(gdf).where((gdf.SOG>0) & (gdf.SOG<50)).hvplot.hist("SOG", bins=20, width=400, height=300)
link_selections(gdf_map + gdf_hist, index_cols=['id'])