Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed namespace and event handling issue with PMTilesLayer, PMTilesMapLibreTooltip #645

Merged

Conversation

prusswan
Copy link
Contributor

  • allows multiple PMTilesLayer to be added/toggled via LayerControl without JS errors (protocol variable is re-declared, each layer should have a unique pane)
  • allows tooltips to show after PMTilesLayer has been disabled and re-enabled via LayerControl

Below snippet is what I used for debugging/testing in Jupyter:

from folium.elements import JSCSSMixin
from folium.map import Layer
from jinja2 import Template
from branca.element import MacroElement

class PMTilesMapLibreTooltipFixed(JSCSSMixin, MacroElement):
    _template = Template(
        """
            {% macro header(this, kwargs) %}
            <style>
            .maplibregl-popup {
                font: 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;
                z-index: 651;
            }
            .feature-row{
                margin-bottom: 0.5em;
                &:not(:last-of-type) {
                    border-bottom: 1px solid black;
                }
            }
            </style>
            {% endmacro %}

            {% macro script(this, kwargs) -%}
                console.log("tooltip this", "{{ this.get_name() }}");
                console.log("parents", "{{this._parent.get_name() }}", 
                    "{{this._parent._parent.get_name() }}") // leaflet map object
                
                var {{ this.get_name() }} = {{ this._parent.get_name() }}.getMaplibreMap();
                //var {{ this.get_name() }} = {{ this._parent.get_name() }};
                const popup_{{ this.get_name() }} = new maplibregl.Popup({
                //const popup = new maplibregl.Popup({
                    closeButton: false,
                    closeOnClick: false
                });
                
                function setToolTipForPMTilesMapLibreLayer_{{ this.get_name() }}(maplibreLayer) {
                    console.log("setting tooltip {{ this.get_name() }}", maplibreLayer);
                    var mlMap = maplibreLayer.getMaplibreMap();
                    var popup = popup_{{ this.get_name() }};

                    mlMap.on('mousemove', (e) => { 
                        mlMap.getCanvas().style.cursor = 'pointer';
                        const { x, y } = e.point;
                        const r = 2; // radius around the point
                        const features = mlMap.queryRenderedFeatures([
                            [x - r, y - r],
                            [x + r, y + r],
                        ]);

                        const {lng, lat}  = e.lngLat;
                        const coordinates = [lng, lat]
                        const html = features.map(f=>`
                        <div class="feature-row">
                            <span>
                                <strong>${f.layer['source-layer']}</strong>
                                <span style="fontSize: 0.8em" }> (${f.geometry.type})</span>
                            </span>
                            <table>
                                ${Object.entries(f.properties).map(([key, value]) =>`<tr><td>${key}</td><td style="text-align: right">${value}</td></tr>`).join("")}
                            </table>
                        </div>
                        `).join("")
                        if(features.length){
                            popup.setLngLat(e.lngLat).setHTML(html).addTo(mlMap);
                        } else {
                            popup.remove();
                        }
                    });
                    mlMap.on('mouseleave', () => {popup.remove();});                    
                }
                
                // maplibre map object
                {{ this.get_name() }}.on("load", (e) => {
                    setTooltipForPMTilesMapLibreLayer_{{ this.get_name() }}({{ this._parent.get_name() }});
                })
                
                // leaflet map object
                {{ this._parent._parent.get_name() }}.on("layeradd", (e) => {
                    setTooltipForPMTilesMapLibreLayer_{{ this.get_name() }}({{ this._parent.get_name() }});
                });                
                
            {%- endmacro %}
            """
    )

    def __init__(self, name=None, **kwargs):
        #super().__init__(name=name if name else "PMTilesTooltip", **kwargs)
        super().__init__(**kwargs)

class PMTilesMapLibreLayerFixed(JSCSSMixin, Layer):
    """Based of
    https://github.com/python-visualization/folium/blob/56d3665fdc9e7280eae1df1262450e53ec4f5a60/folium/plugins/vectorgrid_protobuf.py
    """

    _template = Template(
        """
            {% macro script(this, kwargs) -%}
            var protocol = new pmtiles.Protocol();
            maplibregl.addProtocol("pmtiles", protocol.tile);
            
            // see: https://github.com/maplibre/maplibre-gl-leaflet/issues/19

           {{ this._parent.get_name() }}.createPane('overlay_{{ this.get_name() }}');
           {{ this._parent.get_name() }}.getPane('overlay_{{ this.get_name() }}').style.zIndex = 650;
           {{ this._parent.get_name() }}.getPane('overlay_{{ this.get_name() }}').style.pointerEvents = 'none';
           
            console.log("maplibre layer recreated")
            
            

            var {{ this.get_name() }} = L.maplibreGL({
            pane: 'overlay_{{ this.get_name() }}',
            style: {{ this.style|tojson}},
            interactive: true,
            }).addTo({{ this._parent.get_name() }});

            {%- endmacro %}
            """
    )
    default_css = [
        ("maplibre_css", "https://unpkg.com/[email protected]/dist/maplibre-gl.css")
    ]

    default_js = [
        ("pmtiles", "https://unpkg.com/[email protected]/dist/index.js"),
        ("maplibre-lib", "https://unpkg.com/[email protected]/dist/maplibre-gl.js"),
        (
            "maplibre-leaflet",
            "https://unpkg.com/@maplibre/[email protected]/leaflet-maplibre-gl.js",
            #"https://unpkg.com/@maplibre/[email protected]/leaflet-maplibre-gl.js",
        ),
    ]

    def __init__(self, url, layer_name=None, style=None, tooltip=None, **kwargs):
        self.layer_name = layer_name if layer_name else "PMTilesVector"

        super().__init__(name=self.layer_name, **kwargs)
        print("maplibre layer init", layer_name)

        self.url = url
        self._name = "PMTilesVector"
        print("self", self)
        if tooltip is not None:
            self.add_child(tooltip)
        if style is not None:
            self.style = style
        else:
            self.style = {}
        
m = folium.Map(location=[43.7798, 11.24148], zoom_start=13, tiles="cartodb positron")

tooltip = PMTilesMapLibreTooltipFixed()
tooltip2 = PMTilesMapLibreTooltipFixed()

pmtiles_url = "https://pmtiles.jtmiclat.me/protomaps(vector)ODbL_firenze.pmtiles"
pmtiles_layer = PMTilesMapLibreLayerFixed(
    "folium_layer_name",
    layer_name="maplibre layer (buildings)",
    #show=False, 
    overlay=True,
    style={
        "version": 8,
        "sources": {
            "example_source": {
                "type": "vector",
                "url": "pmtiles://" + pmtiles_url,
                "attribution": '<a href="https://protomaps.com">Protomaps</a> © <a href="https://openstreetmap.org/copyright">OpenStreetMap</a>',
            }
        },
        "layers": [
            {
                "id": "buildings",
                "source": "example_source",
                "source-layer": "landuse",
                "type": "fill",
                "paint": {"fill-color": "steelblue"},
            },
        ],
    },
    tooltip=tooltip,
)

pmtiles_layer2 = PMTilesMapLibreLayerFixed(
    "folium_layer_name2",
    layer_name="maplibre layer2 (roads)",
    #show=False, 
    overlay=True,
    style={
        "version": 8,
        "sources": {
            "example_source": {
                "type": "vector",
                "url": "pmtiles://" + pmtiles_url,
                "attribution": '<a href="https://protomaps.com">Protomaps</a> © <a href="https://openstreetmap.org/copyright">OpenStreetMap</a>',
            }
        },
        "layers": [
            {
                "id": "roads",
                "source": "example_source",
                "source-layer": "roads",
                "type": "line",
                "paint": {"line-color": "black"},
            },
        ],
    },
    tooltip=tooltip2,
)

m.add_child(pmtiles_layer)
m.add_child(pmtiles_layer2)
#pmtiles_layer.add_child(tooltip2)

folium.LayerControl().add_to(m)
m

…pLibreTooltip:

* allows multiple PMTilesLayer to be added/toggled via LayerControl without JS errors (protocol variable is re-declared, each layer should have a unique pane)
* allows tooltips to show after PMTilesLayer has been disabled and re-enabled via LayerControl
@giswqs
Copy link
Member

giswqs commented Dec 20, 2023

It works like a charm! Thank you for your contribution.

image

@giswqs giswqs merged commit 35362ab into opengeos:master Dec 20, 2023
12 checks passed
sthagen pushed a commit to sthagen/giswqs-leafmap that referenced this pull request Jul 10, 2024
* allows multiple PMTilesLayer to be added/toggled via LayerControl without JS errors (protocol variable is re-declared, each layer should have a unique pane)
* allows tooltips to show after PMTilesLayer has been disabled and re-enabled via LayerControl
kuzja111 pushed a commit to kuzja111/leafmap that referenced this pull request Jul 25, 2024
* allows multiple PMTilesLayer to be added/toggled via LayerControl without JS errors (protocol variable is re-declared, each layer should have a unique pane)
* allows tooltips to show after PMTilesLayer has been disabled and re-enabled via LayerControl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants