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

PMTilesMapLibreTooltip does not work with folium > 0.14.0 #16

Closed
prusswan opened this issue Dec 15, 2023 · 3 comments · Fixed by #17
Closed

PMTilesMapLibreTooltip does not work with folium > 0.14.0 #16

prusswan opened this issue Dec 15, 2023 · 3 comments · Fixed by #17

Comments

@prusswan
Copy link
Contributor

Very likely related to python-visualization/folium#1690 which "inspired" the example/fix below.

python-visualization/folium@29429e8 had a similar change by changing Layer to MacroElement

import folium

from folium_pmtiles.vector import PMTilesMapLibreLayer, PMTilesMapLibreTooltip

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

# not used, only included this for troubleshooting
class PMTilesMapLibreLayer2(JSCSSMixin, Layer):
    """Based of
    https://github.com/python-visualization/folium/blob/56d3665fdc9e7280eae1df1262450e53ec4f5a60/folium/plugins/vectorgrid_protobuf.py
    """

    _template = Template(
        """
            {% macro script(this, kwargs) -%}
            let protocol = new pmtiles.Protocol();
            maplibregl.addProtocol("pmtiles", protocol.tile);

           {{ this._parent.get_name() }}.createPane('overlay');
           {{ this._parent.get_name() }}.getPane('overlay').style.zIndex = 650;
           {{ this._parent.get_name() }}.getPane('overlay').style.pointerEvents = 'none';

            var {{ this.get_name() }} = L.maplibreGL({
            pane: 'overlay',
            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",
        ),
    ]

    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)

        self.url = url
        self._name = "PMTilesVector"
        if tooltip is not None:
            self.add_child(tooltip)
        if style is not None:
            self.style = style
        else:
            self.style = {}


#class PMTilesMapLibreTooltip2(JSCSSMixin, Layer):
class PMTilesMapLibreTooltip2(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) -%}
                var {{ this.get_name() }} = {{ this._parent.get_name() }}.getMaplibreMap();
                const popup = new maplibregl.Popup({
                    closeButton: false,
                    closeOnClick: false
                });
                {{ this.get_name() }}.on('load', () => {
                    {{ this.get_name() }}.on('mousemove', (e) => { 
                        {{ this.get_name() }}.getCanvas().style.cursor = 'pointer';
                        const { x, y } = e.point;
                        const r = 2; // radius around the point
                        const features = {{ this.get_name() }}.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({{ this.get_name() }});
                        } else {
                            popup.remove();
                        }
                    });
                    {{ this.get_name() }}.on('mouseleave', () => {popup.remove();});
                });
            {%- endmacro %}
            """
    )

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

m = folium.Map(location=[43.7798, 11.24148], zoom_start=13, tiles="cartodb positron")

tooltip = PMTilesMapLibreTooltip2()
pmtiles_url = "https://pmtiles.jtmiclat.me/protomaps(vector)ODbL_firenze.pmtiles"
pmtiles_layer = PMTilesMapLibreLayer(
    "folium_layer_name",
    layer_name="pmtiles layer name ",
    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"},
            },
            {
                "id": "roads",
                "source": "example_source",
                "source-layer": "roads",
                "type": "line",
                "paint": {"line-color": "black"},
            },
        ],
    },
    tooltip=tooltip,
)

m.add_child(pmtiles_layer)
folium.LayerControl().add_to(m)
m
@jtmiclat
Copy link
Owner

jtmiclat commented Dec 20, 2023

@prusswan Thanks for the report! Merged your suggested fix and published a newer version as 0.4.2!

@prusswan
Copy link
Contributor Author

prusswan commented Dec 21, 2023

@jtmiclat Turns out that there was more than one issue involving LayerControl (see opengeos/leafmap#645). I tried to get it to work with leafmap first, if the changes make sense, I can submit a pull request for this repo as well.

@jtmiclat
Copy link
Owner

@prusswan Feel free to make a new PR ! I'll review it when I get the chance

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 a pull request may close this issue.

2 participants