From 511004f1ac99b4de8500e405d69407e1c62b3018 Mon Sep 17 00:00:00 2001 From: prusswan Date: Fri, 22 Dec 2023 01:16:04 +0800 Subject: [PATCH 1/3] attach tooltip event handler to both (leaflet) map.layeradd and (maplibre) map.load events --- folium_pmtiles/vector.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/folium_pmtiles/vector.py b/folium_pmtiles/vector.py index 8e659b5..1438093 100644 --- a/folium_pmtiles/vector.py +++ b/folium_pmtiles/vector.py @@ -120,19 +120,22 @@ class PMTilesMapLibreTooltip(JSCSSMixin, MacroElement): } {% endmacro %} - {% macro script(this, kwargs) -%} var {{ this.get_name() }} = {{ this._parent.get_name() }}.getMaplibreMap(); - const popup = new maplibregl.Popup({ + const popup_{{ this.get_name() }} = 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'; + + function setTooltipForPMTilesMapLibreLayer_{{ 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 = {{ this.get_name() }}.queryRenderedFeatures([ + const features = mlMap.queryRenderedFeatures([ [x - r, y - r], [x + r, y + r], ]); @@ -151,12 +154,22 @@ class PMTilesMapLibreTooltip(JSCSSMixin, MacroElement): `).join("") if(features.length){ - popup.setLngLat(e.lngLat).setHTML(html).addTo({{ this.get_name() }}); + popup.setLngLat(e.lngLat).setHTML(html).addTo(mlMap); } else { popup.remove(); } }); - {{ this.get_name() }}.on('mouseleave', () => {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 %} """ From 597b0e404609df20207197ba8b6b55f79a152da1 Mon Sep 17 00:00:00 2001 From: prusswan Date: Fri, 22 Dec 2023 01:45:04 +0800 Subject: [PATCH 2/3] allow multiple PMTilesMapLibreLayers to be added (check if pmtiles protocol already exists, use separate overlay panes) --- folium_pmtiles/vector.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/folium_pmtiles/vector.py b/folium_pmtiles/vector.py index 1438093..9fa50de 100644 --- a/folium_pmtiles/vector.py +++ b/folium_pmtiles/vector.py @@ -59,17 +59,20 @@ class PMTilesMapLibreLayer(JSCSSMixin, Layer): _template = Template( """ {% macro script(this, kwargs) -%} - let protocol = new pmtiles.Protocol(); - maplibregl.addProtocol("pmtiles", protocol.tile); + if (!("pmtiles" in maplibregl.config.REGISTERED_PROTOCOLS)) { + var 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'; + // 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'; var {{ this.get_name() }} = L.maplibreGL({ - pane: 'overlay', - style: {{ this.style|tojson}}, - interactive: true, + pane: 'overlay_{{ this.get_name() }}', + style: {{ this.style|tojson}}, + interactive: true, }).addTo({{ this._parent.get_name() }}); {%- endmacro %} From ee7254a728eb6e8e3ee768ef9d054ebe0d3bdb49 Mon Sep 17 00:00:00 2001 From: Jt Miclat Date: Sat, 23 Dec 2023 00:50:27 +0800 Subject: [PATCH 3/3] add new example for layer control --- example/pmtiles_layer_control.ipynb | 395 ++++++++++++++++++++++++++ example/pmtiles_vector_maplibre.ipynb | 82 ++++-- 2 files changed, 448 insertions(+), 29 deletions(-) create mode 100644 example/pmtiles_layer_control.ipynb diff --git a/example/pmtiles_layer_control.ipynb b/example/pmtiles_layer_control.ipynb new file mode 100644 index 0000000..6d8897d --- /dev/null +++ b/example/pmtiles_layer_control.ipynb @@ -0,0 +1,395 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![Open in Google Colab](https://img.shields.io/badge/Open_in_Google_Colab-000000?style=for-the-badge&logo=googlecolab&logoColor=%23F9AB00)](https://colab.research.google.com/github/jtmiclat/folium-pmtiles/blob/master/example/pmtiles_layer_control.ipynb) [![Open in Github.dev](https://img.shields.io/badge/Open_in_Github.dev-000000?style=for-the-badge&logo=GitHub&logoColor=ffffff)](https://github.dev/jtmiclat/folium-pmtiles/blob/master/example/pmtiles_layer_control.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install folium folium-pmtiles" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
Make this Notebook Trusted to load map: File -> Trust Notebook
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import folium\n", + "\n", + "from folium_pmtiles.vector import PMTilesMapLibreLayer, PMTilesMapLibreTooltip\n", + "\n", + "m = folium.Map(location=[43.7798, 11.24148], zoom_start=12, tiles=\"cartodb positron\")\n", + "tooltip_1 = PMTilesMapLibreTooltip()\n", + "pmtiles_url_1 = \"https://pmtiles.jtmiclat.me/protomaps(vector)ODbL_firenze.pmtiles\"\n", + "pmtiles_layer_1 = PMTilesMapLibreLayer(\n", + " \"map\",\n", + " layer_name=\"map\",\n", + " overlay=True,\n", + " style={\n", + " \"version\": 8,\n", + " \"sources\": {\n", + " \"example_source\": {\n", + " \"type\": \"vector\",\n", + " \"url\": \"pmtiles://\" + pmtiles_url_1,\n", + " \"attribution\": 'Protomaps © OpenStreetMap',\n", + " }\n", + " },\n", + " \"layers\": [\n", + " {\n", + " \"id\": \"buildings\",\n", + " \"source\": \"example_source\",\n", + " \"source-layer\": \"landuse\",\n", + " \"type\": \"fill\",\n", + " \"paint\": {\"fill-color\": \"steelblue\"},\n", + " },\n", + " {\n", + " \"id\": \"roads\",\n", + " \"source\": \"example_source\",\n", + " \"source-layer\": \"roads\",\n", + " \"type\": \"line\",\n", + " \"paint\": {\"line-color\": \"black\"},\n", + " },\n", + " ],\n", + " },\n", + " tooltip=tooltip_1,\n", + ")\n", + "tooltip_2 = PMTilesMapLibreTooltip()\n", + "pmtiles_url_2 = \"https://data.source.coop/vida/google-microsoft-open-buildings/pmtiles/go_ms_building_footprints.pmtiles\"\n", + "pmtiles_layer_2 = PMTilesMapLibreLayer(\n", + " \"footprints\",\n", + " layer_name=\"footprints\",\n", + " overlay=True,\n", + " style={\n", + " \"version\": 8,\n", + " \"sources\": {\n", + " \"example_source\": {\n", + " \"type\": \"vector\",\n", + " \"url\": \"pmtiles://\" + pmtiles_url_2,\n", + " \"attribution\": 'Protomaps © OpenStreetMap',\n", + " }\n", + " },\n", + " \"layers\": [\n", + " {\n", + " \"id\": \"building_footprints\",\n", + " \"source\": \"example_source\",\n", + " \"source-layer\": \"building_footprints\",\n", + " \"type\": \"fill\",\n", + " \"paint\": {\"fill-color\": \"red\"},\n", + " },\n", + " ],\n", + " },\n", + " tooltip=tooltip_2,\n", + ")\n", + "m.add_child(pmtiles_layer_1)\n", + "m.add_child(pmtiles_layer_2)\n", + "m.add_child(folium.LayerControl())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "folium-pmtiles", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/example/pmtiles_vector_maplibre.ipynb b/example/pmtiles_vector_maplibre.ipynb index 0b816ed..286d576 100644 --- a/example/pmtiles_vector_maplibre.ipynb +++ b/example/pmtiles_vector_maplibre.ipynb @@ -19,7 +19,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -39,7 +39,7 @@ " <style>html, body {width: 100%;height: 100%;margin: 0;padding: 0;}</style>\n", " <style>#map {position:absolute;top:0;bottom:0;right:0;left:0;}</style>\n", " <script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.js"></script>\n", - " <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>\n", + " <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>\n", " <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"></script>\n", " <script src="https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.js"></script>\n", " <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.css"/>\n", @@ -52,7 +52,7 @@ " <meta name="viewport" content="width=device-width,\n", " initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />\n", " <style>\n", - " #map_8ceea388252b5185b95d0ea5c2320ce3 {\n", + " #map_1887f346f0fa7654dc4374319a8019d1 {\n", " position: relative;\n", " width: 100.0%;\n", " height: 100.0%;\n", @@ -84,14 +84,14 @@ "<body>\n", " \n", " \n", - " <div class="folium-map" id="map_8ceea388252b5185b95d0ea5c2320ce3" ></div>\n", + " <div class="folium-map" id="map_1887f346f0fa7654dc4374319a8019d1" ></div>\n", " \n", "</body>\n", "<script>\n", " \n", " \n", - " var map_8ceea388252b5185b95d0ea5c2320ce3 = L.map(\n", - " "map_8ceea388252b5185b95d0ea5c2320ce3",\n", + " var map_1887f346f0fa7654dc4374319a8019d1 = L.map(\n", + " "map_1887f346f0fa7654dc4374319a8019d1",\n", " {\n", " center: [43.7798, 11.24148],\n", " crs: L.CRS.EPSG3857,\n", @@ -105,34 +105,44 @@ "\n", " \n", " \n", - " var tile_layer_5a2feba28e385044e79a0f410db9a067 = L.tileLayer(\n", - " "https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png",\n", - " {"attribution": "\\u0026copy; \\u003ca target=\\"_blank\\" href=\\"http://www.openstreetmap.org/copyright\\"\\u003eOpenStreetMap\\u003c/a\\u003e contributors \\u0026copy; \\u003ca target=\\"_blank\\" href=\\"http://cartodb.com/attributions\\"\\u003eCartoDB\\u003c/a\\u003e, CartoDB \\u003ca target=\\"_blank\\" href =\\"http://cartodb.com/attributions\\"\\u003eattributions\\u003c/a\\u003e", "detectRetina": false, "maxNativeZoom": 18, "maxZoom": 18, "minZoom": 0, "noWrap": false, "opacity": 1, "subdomains": "abc", "tms": false}\n", - " ).addTo(map_8ceea388252b5185b95d0ea5c2320ce3);\n", + " var tile_layer_0c6a1519150883e2ac35c7b2e890b92a = L.tileLayer(\n", + " "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",\n", + " {"attribution": "\\u0026copy; \\u003ca href=\\"https://www.openstreetmap.org/copyright\\"\\u003eOpenStreetMap\\u003c/a\\u003e contributors \\u0026copy; \\u003ca href=\\"https://carto.com/attributions\\"\\u003eCARTO\\u003c/a\\u003e", "detectRetina": false, "maxNativeZoom": 20, "maxZoom": 20, "minZoom": 0, "noWrap": false, "opacity": 1, "subdomains": "abcd", "tms": false}\n", + " );\n", + " \n", + " \n", + " tile_layer_0c6a1519150883e2ac35c7b2e890b92a.addTo(map_1887f346f0fa7654dc4374319a8019d1);\n", " \n", - " let protocol = new pmtiles.Protocol();\n", - " maplibregl.addProtocol("pmtiles", protocol.tile);\n", + " if (!("pmtiles" in maplibregl.config.REGISTERED_PROTOCOLS)) {\n", + " var protocol = new pmtiles.Protocol();\n", + " maplibregl.addProtocol("pmtiles", protocol.tile);\n", + " }\n", "\n", - " map_8ceea388252b5185b95d0ea5c2320ce3.createPane('overlay');\n", - " map_8ceea388252b5185b95d0ea5c2320ce3.getPane('overlay').style.zIndex = 650;\n", - " map_8ceea388252b5185b95d0ea5c2320ce3.getPane('overlay').style.pointerEvents = 'none';\n", + " // see: https://github.com/maplibre/maplibre-gl-leaflet/issues/19\n", + " map_1887f346f0fa7654dc4374319a8019d1.createPane('overlay_pm_tiles_vector_7e1f7bf220fea60684e7aa06138bd9ab');\n", + " map_1887f346f0fa7654dc4374319a8019d1.getPane('overlay_pm_tiles_vector_7e1f7bf220fea60684e7aa06138bd9ab').style.zIndex = 650;\n", + " map_1887f346f0fa7654dc4374319a8019d1.getPane('overlay_pm_tiles_vector_7e1f7bf220fea60684e7aa06138bd9ab').style.pointerEvents = 'none';\n", "\n", - " var pm_tiles_vector_4fdb6d0235d2b8dab13fcabaaa150826 = L.maplibreGL({\n", - " pane: 'overlay',\n", - " style: {"layers": [{"id": "buildings", "paint": {"fill-color": "steelblue"}, "source": "example_source", "source-layer": "landuse", "type": "fill"}, {"id": "roads", "paint": {"line-color": "black"}, "source": "example_source", "source-layer": "roads", "type": "line"}], "sources": {"example_source": {"attribution": "\\u003ca href=\\"https://protomaps.com\\"\\u003eProtomaps\\u003c/a\\u003e \\u00a9 \\u003ca href=\\"https://openstreetmap.org/copyright\\"\\u003eOpenStreetMap\\u003c/a\\u003e", "type": "vector", "url": "pmtiles://https://pmtiles.jtmiclat.me/protomaps(vector)ODbL_firenze.pmtiles"}}, "version": 8},\n", - " interactive: true,\n", - " }).addTo(map_8ceea388252b5185b95d0ea5c2320ce3);\n", - " var macro_element_5b7f9c4652c49e795ed7432c4302c347 = pm_tiles_vector_4fdb6d0235d2b8dab13fcabaaa150826.getMaplibreMap();\n", - " const popup = new maplibregl.Popup({\n", + " var pm_tiles_vector_7e1f7bf220fea60684e7aa06138bd9ab = L.maplibreGL({\n", + " pane: 'overlay_pm_tiles_vector_7e1f7bf220fea60684e7aa06138bd9ab',\n", + " style: {"layers": [{"id": "buildings", "paint": {"fill-color": "steelblue"}, "source": "example_source", "source-layer": "landuse", "type": "fill"}, {"id": "roads", "paint": {"line-color": "black"}, "source": "example_source", "source-layer": "roads", "type": "line"}], "sources": {"example_source": {"attribution": "\\u003ca href=\\"https://protomaps.com\\"\\u003eProtomaps\\u003c/a\\u003e \\u00a9 \\u003ca href=\\"https://openstreetmap.org/copyright\\"\\u003eOpenStreetMap\\u003c/a\\u003e", "type": "vector", "url": "pmtiles://https://pmtiles.jtmiclat.me/protomaps(vector)ODbL_firenze.pmtiles"}}, "version": 8},\n", + " interactive: true,\n", + " }).addTo(map_1887f346f0fa7654dc4374319a8019d1);\n", + " var pm_tiles_tooltip_d84cbdeefdeb156dc90ea2121edf9529 = pm_tiles_vector_7e1f7bf220fea60684e7aa06138bd9ab.getMaplibreMap();\n", + " const popup_pm_tiles_tooltip_d84cbdeefdeb156dc90ea2121edf9529 = new maplibregl.Popup({\n", " closeButton: false,\n", " closeOnClick: false\n", " });\n", - " macro_element_5b7f9c4652c49e795ed7432c4302c347.on('load', () => {\n", - " macro_element_5b7f9c4652c49e795ed7432c4302c347.on('mousemove', null, (e) => { \n", - " macro_element_5b7f9c4652c49e795ed7432c4302c347.getCanvas().style.cursor = 'pointer';\n", + "\n", + " function setTooltipForPMTilesMapLibreLayer_pm_tiles_tooltip_d84cbdeefdeb156dc90ea2121edf9529(maplibreLayer) {\n", + " var mlMap = maplibreLayer.getMaplibreMap();\n", + " var popup = popup_pm_tiles_tooltip_d84cbdeefdeb156dc90ea2121edf9529;\n", + "\n", + " mlMap.on('mousemove', (e) => {\n", + " mlMap.getCanvas().style.cursor = 'pointer';\n", " const { x, y } = e.point;\n", " const r = 2; // radius around the point\n", - " const features = macro_element_5b7f9c4652c49e795ed7432c4302c347.queryRenderedFeatures([\n", + " const features = mlMap.queryRenderedFeatures([\n", " [x - r, y - r],\n", " [x + r, y + r],\n", " ]);\n", @@ -151,20 +161,34 @@ " </div>\n", " `).join("")\n", " if(features.length){\n", - " popup.setLngLat(e.lngLat).setHTML(html).addTo(macro_element_5b7f9c4652c49e795ed7432c4302c347);\n", + " popup.setLngLat(e.lngLat).setHTML(html).addTo(mlMap);\n", " } else {\n", " popup.remove();\n", " }\n", " });\n", + " mlMap.on('mouseleave', () => {popup.remove();});\n", + " }\n", + "\n", + " // maplibre map object\n", + " pm_tiles_tooltip_d84cbdeefdeb156dc90ea2121edf9529.on("load", (e) => {\n", + " setTooltipForPMTilesMapLibreLayer_pm_tiles_tooltip_d84cbdeefdeb156dc90ea2121edf9529(pm_tiles_vector_7e1f7bf220fea60684e7aa06138bd9ab);\n", + " })\n", + "\n", + " // leaflet map object\n", + " map_1887f346f0fa7654dc4374319a8019d1.on("layeradd", (e) => {\n", + " setTooltipForPMTilesMapLibreLayer_pm_tiles_tooltip_d84cbdeefdeb156dc90ea2121edf9529(pm_tiles_vector_7e1f7bf220fea60684e7aa06138bd9ab);\n", " });\n", + " \n", + " pm_tiles_vector_7e1f7bf220fea60684e7aa06138bd9ab.addTo(map_1887f346f0fa7654dc4374319a8019d1);\n", + " \n", "</script>\n", "</html>\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;\" allowfullscreen webkitallowfullscreen mozallowfullscreen>" ], "text/plain": [ - "" + "" ] }, - "execution_count": 4, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" }