I have a Panel dashboard that looks like this:
The user selects one or more file inputs, sets some fields via param.Selector() widgets, and draws boxes on the map via hv.streams.BoxEdit. When the user pushes the ‘Run’ button, the input data is queried for points that intersect the drawn boxes and results are streamed back to the map via hv.streams.Buffer and a corresponding hv.DynamicMap(hv.Points, streams=[dfstream]). This all works very well.
The map in my dashboard is controlled by an instance of this class:
class Mapview(param.Parameterized):
opts = dict(width=1200,height=750,xaxis=None,yaxis=None,show_grid=False)
tiles = gv.tile_sources.CartoEco().apply.opts(**opts)
extents = param.Parameter(default=(-168, -60, 168, 83), precedence=-1)
tiles.extents = extents.default
box_polygons = gv.Polygons([]).opts(fill_alpha=0.1)
box_colours = ['red','blue','green','orange','purple']
box_stream = hv.streams.BoxEdit(source=box_polygons, num_objects=5, styles={'fill_color': box_colours})
template_df = pd.DataFrame({'x_meters': [], 'y_meters': []}, columns=['x_meters', 'y_meters'])
dfstream = hv.streams.Buffer(template_df, index=False, length=10000)
points = hv.DynamicMap(hv.Points, streams=[dfstream]).opts(
size=5, color='green', fill_alpha=0.3, line_alpha=0.4)
def show_map(self):
return self.tiles * self.box_polygons * self.points
Because my dashboard is initialized with empty hv.Points and gv.Polygons objects (there are no points on the map & no boxes have been drawn but the placeholder objects have been created empty), Geoviews auto-zooms the map to these empty datasets and the map appears zoomed in on 0,0. To solve this problem I set the .extents property on my gv.tile_source to zoom to a global extent. The problem is that when the query results are streamed back to the hv.streams.Buffer the map is refreshed and is reset back to the extents I defined above.
I either want the map to stay zoomed where it is (i.e. where the user put it) or I want to be able to control the map zoom programmatically but I haven’t been able to accomplish either. For the latter I tried adding a new class param.Parameter e and modifying my Mapview.show_map() method like this:
Note: I don’t think I should have to set watch=True but without it the function is not triggered when e is set as shown below.
@param.depends('e',watch=True)
def show_map(self):
print('show_map() has been called.')
try:
if self.e[0] is None:
print('Using global extent')
return (self.tiles * self.box_polygons * self.points).options(xlim=(-20000000,20000000),ylim=(-6000000,6000000))
else:
print(f'Using new extents: {self.e[0]},{self.e[2]},{self.e[1]},{self.e[3]}')
return (self.tiles * self.box_polygons * self.points).options(xlim=(self.e[0],self.e[2]),ylim=(self.e[1],self.e[3]))
except:
print('Problem in show_map()')
and then setting e like this after the user pushes the run button.
boxes = self.mapview.box_stream.data
xmin = min(boxes['x0'])
xmax = max(boxes['x1'])
ymin = min(boxes['y0'])
ymax = max(boxes['y1'])
new_extent = box(xmin,ymin,xmax,ymax)
self.mapview.e = new_extent.bounds
But it doesn’t zoom to the desired extent and also skews (reprojects?) the map to look like this:
How can I prevent the map from zooming to the initial extent after data is sent to the hv.streams.Buffer? Or, how can I get the map to zoom where I want it to and not skew? I’d be happy to provide the full Jupyter Notebook code but didn’t want to pollute this post by making it even longer.










) if you switch tabs above the map after you get results you will see a summary table which describes which birds were found in which boxes; you can sort the columns by clicking them. In this summary table, the values of the ‘identifier’ column come from the field the user selects as the ID field; in the example below I have used 

