I’m working on some application where the user is going to interact with several widgets in order to filter data that will be shown on a map, created with Bokeh.
When the application is launched, everything looks fine. However, as soon as the user apply some filter, the map loses the square aspect ratio, making it difficult to read.
I’ve tried playing with x_range, y_range, but I couldn’t figure it out how to get the job done.
Is there some built-in command to force Bokeh to keep the original aspect ratio of the map? With Folium it is possible to set the center location of the map; is there something similar with Bokeh?
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, WMTSTileSource
import numpy as np
import param
import pandas as pd
import panel as pn
pn.extension()
def lat_wgs84_to_web_mercator(lat):
lat = np.array(lat)
k = 6378137
return np.log(np.tan((90 + lat) * np.pi/360.0)) * k
def lon_wgs84_to_web_mercator(lon):
lon = np.array(lon)
k = 6378137
return lon * (k * np.pi/180.0)
def add_margin(_min, _max, margin=0.05):
d = _max - _min
o = d * margin
return _min - o, _max + o
class Fig(pn.viewable.Viewer):
year = param.Selector(default="All", objects=["All", "2024", "2025"])
data = param.DataFrame(pd.DataFrame.from_dict({
"lat": lat_wgs84_to_web_mercator([48.846222, 41.902783, 48.208174, 40.851798, 38.722252, 37.983917]),
"lon": lon_wgs84_to_web_mercator([2.346414, 12.496365, 16.373819, 14.268120, -9.139337, 23.729360]),
"year": ["2024", "2025", "2024", "2025", "2024", "2025"],
}))
figure = param.ClassSelector(class_=figure)
def __init__(self, **params):
super().__init__(**params)
self.figure = figure(
x_axis_type="mercator", y_axis_type="mercator",
sizing_mode="stretch_width"
# x_range=add_margin(self.data["lon"].min(), self.data["lon"].max())
)
self.figure.grid.visible = False
self.figure.add_tile("CartoDB.Voyager", retina=True)
self.update()
@param.depends("year", watch=True)
def update(self):
if len(self.figure.renderers) == 1:
self.figure.scatter(
"lon", "lat", source=ColumnDataSource(data=self.data),
size=10, color="red", marker="star")
else:
if self.year != "All":
data = self.data[self.data["year"] == self.year]
else:
data = self.data
self.figure.renderers[1].data_source.data.update(data)
def __panel__(self):
return pn.Column(self.param.year, self.figure)
f = Fig()
f