Hi,
I am using ReactiveHTML to develop a spcialized class using Leaflet. I could not use ipyleaflet, as I needed to introduce additional functions on the events of the map.
Now, what I can’t wrap my head around is this;
Beacuse the leaflet map is actually hold in a variable in the _script
in the class. Is it possible to access to the map instance (state.map) from the panel objects or somehow link with a js callback from panel widgets, like the run_button
. What I want to do is add some layers etc to the map when the run_button
is clicked.
My reasoning of having the run_button
as a panel widget, instead of also declaring it in the LeafletMap class is it needs to run some background jobs on the server.
I am posting my simplest code (which was based on a sample of Marc):
from panel.reactive import ReactiveHTML
import param
import panel as pn
CSS = ['https://unpkg.com/leaflet@1.8.0/dist/leaflet.css',
]
JS = {
'leaflet': 'https://unpkg.com/leaflet@1.8.0/dist/leaflet.js',
}
pn.extension(css_files=CSS, js_files=JS)
class LeafletMap(ReactiveHTML):
attribution = param.String(doc="Tile source attribution.")
center = param.XYCoordinates(default=(43, 34), doc="The center of the map.")
# data = param.DataFrame(doc="The heatmap data to plot, should have 'x', 'y' and 'value' columns.")
tile_url = param.String(doc="Tile source URL with {x}, {y} and {z} parameter")
zoom = param.Integer(5, bounds=(0, 21), doc="The map zoom-level")
lat_input = param.Number(44.0, step=0.1, softbounds=(-90, 90), doc="Latitude of start point")
lon_input = param.Number(34.0, step=0.1, softbounds=(-180, 180), doc="Longitude of start point")
_template = """
<div id="map" style="width: 100%; height: 100%; position: absolute;"></div>
"""
_scripts = {
'render': """
state.map = L.map(map).setView(data.center, data.zoom);
state.map.on('zoom', (e) => { data.zoom = state.map.getZoom() })
state.marker = L.marker({lat:data.lat_input, lng:data.lon_input}, {draggable:true, title:'Baslangic Noktasi'}).addTo(state.map)
state.marker.on('moveend', (e) => {
data.lat_input = e.target.getLatLng().lat
data.lon_input = e.target.getLatLng().lng
})
state.tileLayer = L.tileLayer(data.tile_url, {
attribution: data.attribution,
maxZoom: 21,
tileSize: 512,
zoomOffset: -1,
}).addTo(state.map);
""",
'after_layout': """
state.map.invalidateSize()
""",
'lon_input': "state.marker.setLatLng({lat:state.marker.getLatLng().lat, lng:data.lon_input})",
'lat_input': "state.marker.setLatLng({lat:data.lat_input, lng:state.marker.getLatLng().lng})",
'zoom': "state.map.setZoom(data.zoom)",
}
__css__ = CSS
__javascript__ = list(JS.values())
map = LeafletMap(
attribution='Tiles © Esri — Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012',
min_height=500,
tile_url='https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',
sizing_mode='stretch_both',
value='mag',
zoom=5,
height=700,
width = 1200
)
map_controls = pn.Column(pn.pane.Markdown("## Settings"),
map.controls(parameters=['lat_input', 'lon_input'],
widgets={
'lat_input': {'widget_type':pn.widgets.FloatInput, 'format':'000.000000', 'sizing_mode':"stretch_width"},
'lon_input': {'widget_type':pn.widgets.FloatInput, 'format':'000.000000', 'sizing_mode':"stretch_width"},
}, show_name=False)
)
ACCENT_COLOR = pn.template.FastListTemplate.accent_base_color
run_button = pn.widgets.Button(name='Run', button_type='primary', width=50, disabled=False)
template = pn.template.FastListTemplate(
site="Site", title="Map Screen",
sidebar=[pn.pane.Markdown("## "), map_controls, run_button],
main=[map],
accent_base_color=ACCENT_COLOR
)
template.servable()