Leaflet with ReactiveHTML

I recently shared the showcase Works with IPyLeaflet. Since then I discovered that Panel Ipywidgets does not support adding LayerGroup to Ipyleaflet map · Issue #2710.

So I figured I would test the alternative of combining Leaflet and ReactiveHTML. There is actually already a Leaflet HeatMap Example in the Gallery

The below showcases an interactive Leaflet map with one tile, 3 markers, one layer, a control and click events propagated to the python backend.

import numpy as np
import panel as pn
import holoviews as hv
import xarray as xr
import hvplot.xarray
import param

pn.extension(sizing_mode="stretch_width")

class LeafletMap(pn.reactive.ReactiveHTML):
    marker1_clicks = param.Integer()
    marker2_clicks = param.Integer()
    marker3_clicks = param.Integer()

    _template = """<div id="mapid" style="height:100%"></div>"""

    __css__ = ["https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"]
    __javascript__ = ["https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"]

    _scripts = {
        'render': """
var mymap = L.map(mapid).setView([50, 0], 6);
state.map=mymap

var tileLayer = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
    attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
    maxZoom: 18,
    id: 'mapbox/streets-v11',
    tileSize: 512,
    zoomOffset: -1,
    accessToken: 'YOUR_MAPBOX_TOKEN_HERE'
}).addTo(mymap)

var marker1 = L.marker([48, -2], name='marker1');
marker1.on('click', (e) => { data.marker1_clicks =  data.marker1_clicks + 1 })

var marker2 = L.marker([50, 0], name='marker2');
marker2.on('click', (e) => { data.marker2_clicks =  data.marker2_clicks + 1 })

var marker3 = L.marker([52, 2], name='marker3');
marker3.on('click', (e) => { data.marker3_clicks = data.marker3_clicks + 1 })

var layer1 = L.layerGroup([marker1, marker2, marker3]);
layer1.addTo(mymap)
L.control.layers({"tile1": tileLayer},{"layer1": layer1}).addTo(mymap);
""",
        # Need to resize leaflet map to size of outer container
        'after_layout': """
            state.map.invalidateSize();console.log("invalidated");
        """,
    }

map = LeafletMap(height=400)
ACCENT_BASE_COLOR = "#4099da"

template = pn.template.FastListTemplate(
    site="Awesome Panel",
    title="Leaflet with ReactiveHTML",
    logo="https://panel.holoviz.org/_static/logo_stacked.png",
    header_background=ACCENT_BASE_COLOR,
    accent_base_color=ACCENT_BASE_COLOR,
    sidebar=[pn.Param(map, parameters=["marker1_clicks", "marker2_clicks", "marker3_clicks"])],
    main=[map],
).servable()
2 Likes

Hi @Marc ,

I just run your code, but unfortunately no basemap there, is there any conflicting issue. I use panel 1.0. kindly please advise.

thanks

I had previously used my own personal MAPBOX token. It stopped working.

You need to insert your own mapbox token in the code above where it says YOUR_MAPBOX_TOKEN_HERE.