From 268110ac76bb927fdee177f0582745b97eefb273 Mon Sep 17 00:00:00 2001 From: Stefan Kuethe Date: Tue, 23 Jan 2024 16:38:29 +0100 Subject: [PATCH 01/11] Show all props in tooltip if no prop is supplied --- docs/examples/geopandas/app.py | 3 ++- maplibre/srcjs/index.js | 7 ++++++- srcjs/pymaplibregl.js | 10 +++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/examples/geopandas/app.py b/docs/examples/geopandas/app.py index bafebd69..c53d0e36 100644 --- a/docs/examples/geopandas/app.py +++ b/docs/examples/geopandas/app.py @@ -29,7 +29,8 @@ def create_map(): m = Map(map_options) m.add_layer(wilderness_layer) - m.add_tooltip(LAYER_ID, "NAME") + # m.add_tooltip(LAYER_ID, "NAME") + m.add_tooltip(LAYER_ID, None) return m diff --git a/maplibre/srcjs/index.js b/maplibre/srcjs/index.js index c1121f06..cfedabd1 100644 --- a/maplibre/srcjs/index.js +++ b/maplibre/srcjs/index.js @@ -64,7 +64,12 @@ const popup = new maplibregl.Popup(popupOptions); this._map.on("mousemove", layerId, (e) => { const feature = e.features[0]; - const text = feature.properties[property]; + let text; + if (property === null) { + text = Object.keys(feature.properties).map((key) => `${key}: ${feature.properties[key]}`).join("
"); + } else { + text = feature.properties[property]; + } popup.setLngLat(e.lngLat).setHTML(text).addTo(this._map); }); this._map.on("mouseleave", layerId, () => { diff --git a/srcjs/pymaplibregl.js b/srcjs/pymaplibregl.js index 52e0e4c6..fdb6878e 100644 --- a/srcjs/pymaplibregl.js +++ b/srcjs/pymaplibregl.js @@ -79,7 +79,15 @@ export default class PyMapLibreGL { const popup = new maplibregl.Popup(popupOptions); this._map.on("mousemove", layerId, (e) => { const feature = e.features[0]; - const text = feature.properties[property]; + let text; + if (property === null) { + text = Object.keys(feature.properties) + .map((key) => `${key}: ${feature.properties[key]}`) + .join("
"); + } else { + text = feature.properties[property]; + } + popup.setLngLat(e.lngLat).setHTML(text).addTo(this._map); }); From de99f37da77dd446f8c0f0809b80e435a13cbe69 Mon Sep 17 00:00:00 2001 From: Stefan Kuethe Date: Wed, 24 Jan 2024 11:35:42 +0100 Subject: [PATCH 02/11] Refactor --- maplibre/srcjs/index.js | 154 +--------------------------------------- package.json | 5 +- srcjs/pymaplibregl.js | 21 +++--- 3 files changed, 16 insertions(+), 164 deletions(-) diff --git a/maplibre/srcjs/index.js b/maplibre/srcjs/index.js index cfedabd1..3d17d077 100644 --- a/maplibre/srcjs/index.js +++ b/maplibre/srcjs/index.js @@ -1,153 +1 @@ -(() => { - // srcjs/pymaplibregl.js - var PyMapLibreGL = class { - constructor(mapOptions) { - this._id = mapOptions.container; - this._map = new maplibregl.Map(mapOptions); - this._map.on("mouseover", () => { - this._map.getCanvas().style.cursor = "pointer"; - }); - this._map.on("mouseout", () => { - this._map.getCanvas().style.cursor = ""; - }); - this._map.addControl(new maplibregl.NavigationControl()); - } - getMap() { - return this._map; - } - applyMapMethod(name, params) { - this._map[name](...params); - } - addControl(type, options, position) { - this._map.addControl(new maplibregl[type](options), position); - } - addMarker({ lngLat, popup, options }) { - const marker = new maplibregl.Marker(options).setLngLat(lngLat); - if (popup) { - const popup_ = new maplibregl.Popup(popup.options).setHTML(popup.text); - marker.setPopup(popup_); - } - marker.addTo(this._map); - } - addLayer(layer) { - this._map.addLayer(layer); - if (typeof Shiny !== "undefined") { - this._map.on("click", layer.id, (e) => { - console.log(e, e.features[0]); - const layerId_ = layer.id.replaceAll("-", "_"); - const inputName = `${this._id}_layer_${layerId_}`; - const feature = { - props: e.features[0].properties, - layer_id: layer.id - }; - console.log(inputName, feature); - Shiny.onInputChange(inputName, feature); - }); - } - } - addPopup(layerId, property) { - const popupOptions = { - closeButton: false - }; - const popup = new maplibregl.Popup(popupOptions); - this._map.on("click", layerId, (e) => { - const feature = e.features[0]; - const text = feature.properties[property]; - popup.setLngLat(e.lngLat).setHTML(text).addTo(this._map); - }); - } - addTooltip(layerId, property) { - const popupOptions = { - closeButton: false, - closeOnClick: false - }; - const popup = new maplibregl.Popup(popupOptions); - this._map.on("mousemove", layerId, (e) => { - const feature = e.features[0]; - let text; - if (property === null) { - text = Object.keys(feature.properties).map((key) => `${key}: ${feature.properties[key]}`).join("
"); - } else { - text = feature.properties[property]; - } - popup.setLngLat(e.lngLat).setHTML(text).addTo(this._map); - }); - this._map.on("mouseleave", layerId, () => { - popup.remove(); - }); - } - setSourceData(sourceId, data) { - this._map.getSource(sourceId).setData(data); - } - render(calls) { - calls.forEach(([name, params]) => { - if ([ - "addLayer", - "addPopup", - "addTooltip", - "addMarker", - "addPopup", - "addControl", - "setSourceData" - ].includes(name)) { - console.log("Custom method", name, params); - this[name](...params); - return; - } - console.log("Map method", name); - this.applyMapMethod(name, params); - }); - } - }; - - // srcjs/index.js - var version = "0.1.0"; - console.log("pymaplibregl", version); - if (typeof Shiny === "undefined") { - window.pymaplibregl = function({ mapOptions, calls }) { - const id = "pymaplibregl"; - const container = document.getElementById(id); - const pyMapLibreGL = new PyMapLibreGL( - Object.assign({ container: container.id }, mapOptions) - ); - const map = pyMapLibreGL.getMap(); - map.on("load", () => { - pyMapLibreGL.render(calls); - }); - }; - } - if (typeof Shiny !== "undefined") { - class MapLibreGLOutputBinding extends Shiny.OutputBinding { - find(scope) { - return scope.find(".shiny-maplibregl-output"); - } - renderValue(el, payload) { - console.log("id:", el.id, "payload:", payload); - const pyMapLibreGL = new PyMapLibreGL( - Object.assign({ container: el.id }, payload.mapData.mapOptions) - ); - const map = pyMapLibreGL.getMap(); - map.on("load", () => { - pyMapLibreGL.render(payload.mapData.calls); - }); - map.on("click", (e) => { - console.log(e); - const inputName = `${el.id}`; - const data = { coords: e.lngLat, point: e.point }; - console.log(inputName, data); - Shiny.onInputChange(inputName, data); - }); - const messageHandlerName = `pymaplibregl-${el.id}`; - console.log(messageHandlerName); - Shiny.addCustomMessageHandler(messageHandlerName, ({ id, calls }) => { - console.log(id, calls); - pyMapLibreGL.render(calls); - }); - } - } - Shiny.outputBindings.register( - new MapLibreGLOutputBinding(), - "shiny-maplibregl-output" - ); - } -})(); +(()=>{function c(i,e){return e===null?Object.keys(i.properties).map(o=>`${o}: ${i.properties[o]}`).join("
"):i.properties[e]}var p=class{constructor(e){this._id=e.container,this._map=new maplibregl.Map(e),this._map.on("mouseover",()=>{this._map.getCanvas().style.cursor="pointer"}),this._map.on("mouseout",()=>{this._map.getCanvas().style.cursor=""}),this._map.addControl(new maplibregl.NavigationControl)}getMap(){return this._map}applyMapMethod(e,t){this._map[e](...t)}addControl(e,t,o){this._map.addControl(new maplibregl[e](t),o)}addMarker({lngLat:e,popup:t,options:o}){let n=new maplibregl.Marker(o).setLngLat(e);if(t){let a=new maplibregl.Popup(t.options).setHTML(t.text);n.setPopup(a)}n.addTo(this._map)}addLayer(e){this._map.addLayer(e),typeof Shiny<"u"&&this._map.on("click",e.id,t=>{console.log(t,t.features[0]);let o=e.id.replaceAll("-","_"),n=`${this._id}_layer_${o}`,a={props:t.features[0].properties,layer_id:e.id};console.log(n,a),Shiny.onInputChange(n,a)})}addPopup(e,t){let o={closeButton:!1},n=new maplibregl.Popup(o);this._map.on("click",e,a=>{let s=a.features[0].properties[t];n.setLngLat(a.lngLat).setHTML(s).addTo(this._map)})}addTooltip(e,t){let o={closeButton:!1,closeOnClick:!1},n=new maplibregl.Popup(o);this._map.on("mousemove",e,a=>{let r=a.features[0],s=c(r,t);n.setLngLat(a.lngLat).setHTML(s).addTo(this._map)}),this._map.on("mouseleave",e,()=>{n.remove()})}setSourceData(e,t){this._map.getSource(e).setData(t)}render(e){e.forEach(([t,o])=>{if(["addLayer","addPopup","addTooltip","addMarker","addPopup","addControl","setSourceData"].includes(t)){console.log("Custom method",t,o),this[t](...o);return}console.log("Map method",t),this.applyMapMethod(t,o)})}};var u="0.1.0";console.log("pymaplibregl",u);typeof Shiny>"u"&&(window.pymaplibregl=function({mapOptions:i,calls:e}){let t="pymaplibregl",o=document.getElementById(t),n=new p(Object.assign({container:o.id},i));n.getMap().on("load",()=>{n.render(e)})});if(typeof Shiny<"u"){class i extends Shiny.OutputBinding{find(t){return t.find(".shiny-maplibregl-output")}renderValue(t,o){console.log("id:",t.id,"payload:",o);let n=new p(Object.assign({container:t.id},o.mapData.mapOptions)),a=n.getMap();a.on("load",()=>{n.render(o.mapData.calls)}),a.on("click",s=>{console.log(s);let l=`${t.id}`,d={coords:s.lngLat,point:s.point};console.log(l,d),Shiny.onInputChange(l,d)});let r=`pymaplibregl-${t.id}`;console.log(r),Shiny.addCustomMessageHandler(r,({id:s,calls:l})=>{console.log(s,l),n.render(l)})}}Shiny.outputBindings.register(new i,"shiny-maplibregl-output")}})(); diff --git a/package.json b/package.json index c4f6b08a..30aa8932 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pymaplibregl", - "version": "0.1.0", + "version": "0.1.1", "description": "...", "main": "index.js", "directories": { @@ -8,7 +8,8 @@ "test": "tests" }, "scripts": { - "build": "esbuild srcjs/index.js --bundle --outfile=maplibre/srcjs/index.js", + "build": "esbuild srcjs/index.js --bundle --minify --outfile=maplibre/srcjs/index.js", + "build-dev": "esbuild srcjs/index.js --bundle --outfile=maplibre/srcjs/index.js", "build-ipywidget": "esbuild srcjs/ipywidget.js --bundle --format=esm --outfile=maplibre/srcjs/ipywidget.js", "build-rwidget": "esbuild srcjs/rwidget.js --bundle --minify --outfile=../r-maplibregl/inst/htmlwidgets/maplibre.js", "prettier": "prettier srcjs --write", diff --git a/srcjs/pymaplibregl.js b/srcjs/pymaplibregl.js index fdb6878e..7e8ea22d 100644 --- a/srcjs/pymaplibregl.js +++ b/srcjs/pymaplibregl.js @@ -1,5 +1,16 @@ // import { getCustomMapMethods } from "./mapmethods"; +function getTextFromFeature(feature, property) { + if (property === null) { + const text = Object.keys(feature.properties) + .map((key) => `${key}: ${feature.properties[key]}`) + .join("
"); + return text; + } + + return feature.properties[property]; +} + export default class PyMapLibreGL { constructor(mapOptions) { this._id = mapOptions.container; @@ -79,15 +90,7 @@ export default class PyMapLibreGL { const popup = new maplibregl.Popup(popupOptions); this._map.on("mousemove", layerId, (e) => { const feature = e.features[0]; - let text; - if (property === null) { - text = Object.keys(feature.properties) - .map((key) => `${key}: ${feature.properties[key]}`) - .join("
"); - } else { - text = feature.properties[property]; - } - + const text = getTextFromFeature(feature, property); popup.setLngLat(e.lngLat).setHTML(text).addTo(this._map); }); From b78a26612db3bd849b32938a14801349ed4dc4a3 Mon Sep 17 00:00:00 2001 From: Stefan Kuethe Date: Wed, 24 Jan 2024 11:48:39 +0100 Subject: [PATCH 03/11] Make prop optional --- docs/examples/geopandas/app.py | 5 +++-- maplibre/map.py | 8 ++++---- maplibre/srcjs/index.js | 2 +- srcjs/pymaplibregl.js | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/examples/geopandas/app.py b/docs/examples/geopandas/app.py index c53d0e36..2d01af49 100644 --- a/docs/examples/geopandas/app.py +++ b/docs/examples/geopandas/app.py @@ -29,8 +29,9 @@ def create_map(): m = Map(map_options) m.add_layer(wilderness_layer) - # m.add_tooltip(LAYER_ID, "NAME") - m.add_tooltip(LAYER_ID, None) + m.add_tooltip(LAYER_ID, "NAME") + # m.add_tooltip(LAYER_ID) + # m.add_popup(LAYER_ID, "NAME") return m diff --git a/maplibre/map.py b/maplibre/map.py index ede455fb..96aa35d0 100644 --- a/maplibre/map.py +++ b/maplibre/map.py @@ -158,21 +158,21 @@ def add_marker(self, marker: Marker) -> None: """ self.add_call("addMarker", marker.to_dict()) - def add_popup(self, layer_id: str, prop: str) -> None: + def add_popup(self, layer_id: str, prop: str = None) -> None: """Add a popup to the map Args: layer_id (str): The layer to which the popup is added. - prop (str): The property of the source to be displayed. + prop (str): The property of the source to be displayed. If `None`, all properties will be displayed. """ self.add_call("addPopup", layer_id, prop) - def add_tooltip(self, layer_id: str, prop: str) -> None: + def add_tooltip(self, layer_id: str, prop: str = None) -> None: """Add a tooltip to the map Args: layer_id (str): The layer to which the tooltip is added. - prop (str): The property of the source to be displayed. + prop (str): The property of the source to be displayed. If `None`, all properties will be displayed. """ self.add_call("addTooltip", layer_id, prop) diff --git a/maplibre/srcjs/index.js b/maplibre/srcjs/index.js index 3d17d077..d86dea7f 100644 --- a/maplibre/srcjs/index.js +++ b/maplibre/srcjs/index.js @@ -1 +1 @@ -(()=>{function c(i,e){return e===null?Object.keys(i.properties).map(o=>`${o}: ${i.properties[o]}`).join("
"):i.properties[e]}var p=class{constructor(e){this._id=e.container,this._map=new maplibregl.Map(e),this._map.on("mouseover",()=>{this._map.getCanvas().style.cursor="pointer"}),this._map.on("mouseout",()=>{this._map.getCanvas().style.cursor=""}),this._map.addControl(new maplibregl.NavigationControl)}getMap(){return this._map}applyMapMethod(e,t){this._map[e](...t)}addControl(e,t,o){this._map.addControl(new maplibregl[e](t),o)}addMarker({lngLat:e,popup:t,options:o}){let n=new maplibregl.Marker(o).setLngLat(e);if(t){let a=new maplibregl.Popup(t.options).setHTML(t.text);n.setPopup(a)}n.addTo(this._map)}addLayer(e){this._map.addLayer(e),typeof Shiny<"u"&&this._map.on("click",e.id,t=>{console.log(t,t.features[0]);let o=e.id.replaceAll("-","_"),n=`${this._id}_layer_${o}`,a={props:t.features[0].properties,layer_id:e.id};console.log(n,a),Shiny.onInputChange(n,a)})}addPopup(e,t){let o={closeButton:!1},n=new maplibregl.Popup(o);this._map.on("click",e,a=>{let s=a.features[0].properties[t];n.setLngLat(a.lngLat).setHTML(s).addTo(this._map)})}addTooltip(e,t){let o={closeButton:!1,closeOnClick:!1},n=new maplibregl.Popup(o);this._map.on("mousemove",e,a=>{let r=a.features[0],s=c(r,t);n.setLngLat(a.lngLat).setHTML(s).addTo(this._map)}),this._map.on("mouseleave",e,()=>{n.remove()})}setSourceData(e,t){this._map.getSource(e).setData(t)}render(e){e.forEach(([t,o])=>{if(["addLayer","addPopup","addTooltip","addMarker","addPopup","addControl","setSourceData"].includes(t)){console.log("Custom method",t,o),this[t](...o);return}console.log("Map method",t),this.applyMapMethod(t,o)})}};var u="0.1.0";console.log("pymaplibregl",u);typeof Shiny>"u"&&(window.pymaplibregl=function({mapOptions:i,calls:e}){let t="pymaplibregl",o=document.getElementById(t),n=new p(Object.assign({container:o.id},i));n.getMap().on("load",()=>{n.render(e)})});if(typeof Shiny<"u"){class i extends Shiny.OutputBinding{find(t){return t.find(".shiny-maplibregl-output")}renderValue(t,o){console.log("id:",t.id,"payload:",o);let n=new p(Object.assign({container:t.id},o.mapData.mapOptions)),a=n.getMap();a.on("load",()=>{n.render(o.mapData.calls)}),a.on("click",s=>{console.log(s);let l=`${t.id}`,d={coords:s.lngLat,point:s.point};console.log(l,d),Shiny.onInputChange(l,d)});let r=`pymaplibregl-${t.id}`;console.log(r),Shiny.addCustomMessageHandler(r,({id:s,calls:l})=>{console.log(s,l),n.render(l)})}}Shiny.outputBindings.register(new i,"shiny-maplibregl-output")}})(); +(()=>{function c(i,e){return e===null?Object.keys(i.properties).map(o=>`${o}: ${i.properties[o]}`).join("
"):i.properties[e]}var r=class{constructor(e){this._id=e.container,this._map=new maplibregl.Map(e),this._map.on("mouseover",()=>{this._map.getCanvas().style.cursor="pointer"}),this._map.on("mouseout",()=>{this._map.getCanvas().style.cursor=""}),this._map.addControl(new maplibregl.NavigationControl)}getMap(){return this._map}applyMapMethod(e,t){this._map[e](...t)}addControl(e,t,o){this._map.addControl(new maplibregl[e](t),o)}addMarker({lngLat:e,popup:t,options:o}){let n=new maplibregl.Marker(o).setLngLat(e);if(t){let a=new maplibregl.Popup(t.options).setHTML(t.text);n.setPopup(a)}n.addTo(this._map)}addLayer(e){this._map.addLayer(e),typeof Shiny<"u"&&this._map.on("click",e.id,t=>{console.log(t,t.features[0]);let o=e.id.replaceAll("-","_"),n=`${this._id}_layer_${o}`,a={props:t.features[0].properties,layer_id:e.id};console.log(n,a),Shiny.onInputChange(n,a)})}addPopup(e,t){let o={closeButton:!1},n=new maplibregl.Popup(o);this._map.on("click",e,a=>{let p=a.features[0],s=c(p,t);n.setLngLat(a.lngLat).setHTML(s).addTo(this._map)})}addTooltip(e,t){let o={closeButton:!1,closeOnClick:!1},n=new maplibregl.Popup(o);this._map.on("mousemove",e,a=>{let p=a.features[0],s=c(p,t);n.setLngLat(a.lngLat).setHTML(s).addTo(this._map)}),this._map.on("mouseleave",e,()=>{n.remove()})}setSourceData(e,t){this._map.getSource(e).setData(t)}render(e){e.forEach(([t,o])=>{if(["addLayer","addPopup","addTooltip","addMarker","addPopup","addControl","setSourceData"].includes(t)){console.log("Custom method",t,o),this[t](...o);return}console.log("Map method",t),this.applyMapMethod(t,o)})}};var u="0.1.0";console.log("pymaplibregl",u);typeof Shiny>"u"&&(window.pymaplibregl=function({mapOptions:i,calls:e}){let t="pymaplibregl",o=document.getElementById(t),n=new r(Object.assign({container:o.id},i));n.getMap().on("load",()=>{n.render(e)})});if(typeof Shiny<"u"){class i extends Shiny.OutputBinding{find(t){return t.find(".shiny-maplibregl-output")}renderValue(t,o){console.log("id:",t.id,"payload:",o);let n=new r(Object.assign({container:t.id},o.mapData.mapOptions)),a=n.getMap();a.on("load",()=>{n.render(o.mapData.calls)}),a.on("click",s=>{console.log(s);let l=`${t.id}`,d={coords:s.lngLat,point:s.point};console.log(l,d),Shiny.onInputChange(l,d)});let p=`pymaplibregl-${t.id}`;console.log(p),Shiny.addCustomMessageHandler(p,({id:s,calls:l})=>{console.log(s,l),n.render(l)})}}Shiny.outputBindings.register(new i,"shiny-maplibregl-output")}})(); diff --git a/srcjs/pymaplibregl.js b/srcjs/pymaplibregl.js index 7e8ea22d..ecc1bf39 100644 --- a/srcjs/pymaplibregl.js +++ b/srcjs/pymaplibregl.js @@ -77,7 +77,8 @@ export default class PyMapLibreGL { const popup = new maplibregl.Popup(popupOptions); this._map.on("click", layerId, (e) => { const feature = e.features[0]; - const text = feature.properties[property]; + // const text = feature.properties[property]; + const text = getTextFromFeature(feature, property); popup.setLngLat(e.lngLat).setHTML(text).addTo(this._map); }); } From 803c4ea0da75485638c94bd12f1339ba21b1993f Mon Sep 17 00:00:00 2001 From: Stefan Kuethe Date: Wed, 24 Jan 2024 11:51:54 +0100 Subject: [PATCH 04/11] Update docs --- maplibre/map.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maplibre/map.py b/maplibre/map.py index 96aa35d0..2646ad5a 100644 --- a/maplibre/map.py +++ b/maplibre/map.py @@ -163,7 +163,7 @@ def add_popup(self, layer_id: str, prop: str = None) -> None: Args: layer_id (str): The layer to which the popup is added. - prop (str): The property of the source to be displayed. If `None`, all properties will be displayed. + prop (str): The property of the source to be displayed. If `None`, all properties are displayed. """ self.add_call("addPopup", layer_id, prop) @@ -172,7 +172,7 @@ def add_tooltip(self, layer_id: str, prop: str = None) -> None: Args: layer_id (str): The layer to which the tooltip is added. - prop (str): The property of the source to be displayed. If `None`, all properties will be displayed. + prop (str): The property of the source to be displayed. If `None`, all properties are displayed. """ self.add_call("addTooltip", layer_id, prop) From 0571b890273f7d82fdfafd75accc0e7c631e1cc9 Mon Sep 17 00:00:00 2001 From: Stefan Kuethe Date: Wed, 24 Jan 2024 12:28:48 +0100 Subject: [PATCH 05/11] Support mustache templates --- docs/examples/geopandas/app.py | 4 ++-- maplibre/map.py | 12 ++++++++---- maplibre/srcjs/index.js | 13 ++++++++++++- package-lock.json | 15 +++++++++++++-- package.json | 3 +++ srcjs/pymaplibregl.js | 15 ++++++++++----- 6 files changed, 48 insertions(+), 14 deletions(-) diff --git a/docs/examples/geopandas/app.py b/docs/examples/geopandas/app.py index 2d01af49..6ea348a3 100644 --- a/docs/examples/geopandas/app.py +++ b/docs/examples/geopandas/app.py @@ -29,9 +29,9 @@ def create_map(): m = Map(map_options) m.add_layer(wilderness_layer) - m.add_tooltip(LAYER_ID, "NAME") + # m.add_tooltip(LAYER_ID, "NAME", template="NAME: {{ NAME }}") # m.add_tooltip(LAYER_ID) - # m.add_popup(LAYER_ID, "NAME") + m.add_popup(LAYER_ID, template="Name: {{ NAME }}") return m diff --git a/maplibre/map.py b/maplibre/map.py index 2646ad5a..90f29660 100644 --- a/maplibre/map.py +++ b/maplibre/map.py @@ -158,23 +158,27 @@ def add_marker(self, marker: Marker) -> None: """ self.add_call("addMarker", marker.to_dict()) - def add_popup(self, layer_id: str, prop: str = None) -> None: + def add_popup(self, layer_id: str, prop: str = None, template: str = None) -> None: """Add a popup to the map Args: layer_id (str): The layer to which the popup is added. prop (str): The property of the source to be displayed. If `None`, all properties are displayed. + template (str): A mustache template. If supplied, `prop` is ignored. """ - self.add_call("addPopup", layer_id, prop) + self.add_call("addPopup", layer_id, prop, template) - def add_tooltip(self, layer_id: str, prop: str = None) -> None: + def add_tooltip( + self, layer_id: str, prop: str = None, template: str = None + ) -> None: """Add a tooltip to the map Args: layer_id (str): The layer to which the tooltip is added. prop (str): The property of the source to be displayed. If `None`, all properties are displayed. + template (str): A mustache template. If supplied, `prop` is ignored. """ - self.add_call("addTooltip", layer_id, prop) + self.add_call("addTooltip", layer_id, prop, template) def set_filter(self, layer_id: str, filter_: list): """Update the filter of a layer diff --git a/maplibre/srcjs/index.js b/maplibre/srcjs/index.js index d86dea7f..04cabad5 100644 --- a/maplibre/srcjs/index.js +++ b/maplibre/srcjs/index.js @@ -1 +1,12 @@ -(()=>{function c(i,e){return e===null?Object.keys(i.properties).map(o=>`${o}: ${i.properties[o]}`).join("
"):i.properties[e]}var r=class{constructor(e){this._id=e.container,this._map=new maplibregl.Map(e),this._map.on("mouseover",()=>{this._map.getCanvas().style.cursor="pointer"}),this._map.on("mouseout",()=>{this._map.getCanvas().style.cursor=""}),this._map.addControl(new maplibregl.NavigationControl)}getMap(){return this._map}applyMapMethod(e,t){this._map[e](...t)}addControl(e,t,o){this._map.addControl(new maplibregl[e](t),o)}addMarker({lngLat:e,popup:t,options:o}){let n=new maplibregl.Marker(o).setLngLat(e);if(t){let a=new maplibregl.Popup(t.options).setHTML(t.text);n.setPopup(a)}n.addTo(this._map)}addLayer(e){this._map.addLayer(e),typeof Shiny<"u"&&this._map.on("click",e.id,t=>{console.log(t,t.features[0]);let o=e.id.replaceAll("-","_"),n=`${this._id}_layer_${o}`,a={props:t.features[0].properties,layer_id:e.id};console.log(n,a),Shiny.onInputChange(n,a)})}addPopup(e,t){let o={closeButton:!1},n=new maplibregl.Popup(o);this._map.on("click",e,a=>{let p=a.features[0],s=c(p,t);n.setLngLat(a.lngLat).setHTML(s).addTo(this._map)})}addTooltip(e,t){let o={closeButton:!1,closeOnClick:!1},n=new maplibregl.Popup(o);this._map.on("mousemove",e,a=>{let p=a.features[0],s=c(p,t);n.setLngLat(a.lngLat).setHTML(s).addTo(this._map)}),this._map.on("mouseleave",e,()=>{n.remove()})}setSourceData(e,t){this._map.getSource(e).setData(t)}render(e){e.forEach(([t,o])=>{if(["addLayer","addPopup","addTooltip","addMarker","addPopup","addControl","setSourceData"].includes(t)){console.log("Custom method",t,o),this[t](...o);return}console.log("Map method",t),this.applyMapMethod(t,o)})}};var u="0.1.0";console.log("pymaplibregl",u);typeof Shiny>"u"&&(window.pymaplibregl=function({mapOptions:i,calls:e}){let t="pymaplibregl",o=document.getElementById(t),n=new r(Object.assign({container:o.id},i));n.getMap().on("load",()=>{n.render(e)})});if(typeof Shiny<"u"){class i extends Shiny.OutputBinding{find(t){return t.find(".shiny-maplibregl-output")}renderValue(t,o){console.log("id:",t.id,"payload:",o);let n=new r(Object.assign({container:t.id},o.mapData.mapOptions)),a=n.getMap();a.on("load",()=>{n.render(o.mapData.calls)}),a.on("click",s=>{console.log(s);let l=`${t.id}`,d={coords:s.lngLat,point:s.point};console.log(l,d),Shiny.onInputChange(l,d)});let p=`pymaplibregl-${t.id}`;console.log(p),Shiny.addCustomMessageHandler(p,({id:s,calls:l})=>{console.log(s,l),n.render(l)})}}Shiny.outputBindings.register(new i,"shiny-maplibregl-output")}})(); +(()=>{var B=Object.prototype.toString,_=Array.isArray||function(e){return B.call(e)==="[object Array]"};function R(r){return typeof r=="function"}function D(r){return _(r)?"array":typeof r}function P(r){return r.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function x(r,e){return r!=null&&typeof r=="object"&&e in r}function H(r,e){return r!=null&&typeof r!="object"&&r.hasOwnProperty&&r.hasOwnProperty(e)}var N=RegExp.prototype.test;function W(r,e){return N.call(r,e)}var q=/\S/;function F(r){return!W(q,r)}var G={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function K(r){return String(r).replace(/[&<>"'`=\/]/g,function(t){return G[t]})}var V=/\s*/,z=/\s+/,j=/\s*=/,J=/\s*\}/,Q=/#|\^|\/|>|\{|&|=|!/;function X(r,e){if(!r)return[];var t=!1,n=[],a=[],s=[],o=!1,i=!1,p="",u=0;function h(){if(o&&!i)for(;s.length;)delete a[s.pop()];else s=[];o=!1,i=!1}var v,m,O;function U(y){if(typeof y=="string"&&(y=y.split(z,2)),!_(y)||y.length!==2)throw new Error("Invalid tags: "+y);v=new RegExp(P(y[0])+"\\s*"),m=new RegExp("\\s*"+P(y[1])),O=new RegExp("\\s*"+P("}"+y[1]))}U(e||g.tags);for(var l=new E(r),w,f,d,k,L,b;!l.eos();){if(w=l.pos,d=l.scanUntil(v),d)for(var M=0,A=d.length;M"?L=[f,d,w,l.pos,p,u,t]:L=[f,d,w,l.pos],u++,a.push(L),f==="#"||f==="^")n.push(L);else if(f==="/"){if(b=n.pop(),!b)throw new Error('Unopened section "'+d+'" at '+w);if(b[1]!==d)throw new Error('Unclosed section "'+b[1]+'" at '+w)}else f==="name"||f==="{"||f==="&"?i=!0:f==="="&&U(d)}if(h(),b=n.pop(),b)throw new Error('Unclosed section "'+b[1]+'" at '+l.pos);return Z(Y(a))}function Y(r){for(var e=[],t,n,a=0,s=r.length;a0?n[n.length-1][4]:e;break;default:t.push(a)}return e}function E(r){this.string=r,this.tail=r,this.pos=0}E.prototype.eos=function(){return this.tail===""};E.prototype.scan=function(e){var t=this.tail.match(e);if(!t||t.index!==0)return"";var n=t[0];return this.tail=this.tail.substring(n.length),this.pos+=n.length,n};E.prototype.scanUntil=function(e){var t=this.tail.search(e),n;switch(t){case-1:n=this.tail,this.tail="";break;case 0:n="";break;default:n=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=n.length,n};function C(r,e){this.view=r,this.cache={".":this.view},this.parent=e}C.prototype.push=function(e){return new C(e,this)};C.prototype.lookup=function(e){var t=this.cache,n;if(t.hasOwnProperty(e))n=t[e];else{for(var a=this,s,o,i,p=!1;a;){if(e.indexOf(".")>0)for(s=a.view,o=e.split("."),i=0;s!=null&&i"?u=this.renderPartial(i,t,n,s):p==="&"?u=this.unescapedValue(i,t):p==="name"?u=this.escapedValue(i,t,s):p==="text"&&(u=this.rawValue(i)),u!==void 0&&(o+=u);return o};c.prototype.renderSection=function(e,t,n,a,s){var o=this,i="",p=t.lookup(e[1]);function u(m){return o.render(m,t,n,s)}if(p){if(_(p))for(var h=0,v=p.length;h0||!n)&&(s[o]=a+s[o]);return s.join(` +`)};c.prototype.renderPartial=function(e,t,n,a){if(n){var s=this.getConfigTags(a),o=R(n)?n(e[1]):n[e[1]];if(o!=null){var i=e[6],p=e[5],u=e[4],h=o;p==0&&u&&(h=this.indentPartial(o,u,i));var v=this.parse(h,s);return this.renderTokens(v,t,n,h,a)}}};c.prototype.unescapedValue=function(e,t){var n=t.lookup(e[1]);if(n!=null)return n};c.prototype.escapedValue=function(e,t,n){var a=this.getConfigEscape(n)||g.escape,s=t.lookup(e[1]);if(s!=null)return typeof s=="number"&&a===g.escape?String(s):a(s)};c.prototype.rawValue=function(e){return e[1]};c.prototype.getConfigTags=function(e){return _(e)?e:e&&typeof e=="object"?e.tags:void 0};c.prototype.getConfigEscape=function(e){if(e&&typeof e=="object"&&!_(e))return e.escape};var g={name:"mustache.js",version:"4.2.0",tags:["{{","}}"],clearCache:void 0,escape:void 0,parse:void 0,render:void 0,Scanner:void 0,Context:void 0,Writer:void 0,set templateCache(r){T.templateCache=r},get templateCache(){return T.templateCache}},T=new c;g.clearCache=function(){return T.clearCache()};g.parse=function(e,t){return T.parse(e,t)};g.render=function(e,t,n,a){if(typeof e!="string")throw new TypeError('Invalid template! Template should be a "string" but "'+D(e)+'" was given as the first argument for mustache#render(template, view, partials)');return T.render(e,t,n,a)};g.escape=K;g.Scanner=E;g.Context=C;g.Writer=c;var I=g;function $(r,e,t){return t!==null?I.render(t,r.properties):e===null?Object.keys(r.properties).map(a=>`${a}: ${r.properties[a]}`).join("
"):r.properties[e]}var S=class{constructor(e){this._id=e.container,this._map=new maplibregl.Map(e),this._map.on("mouseover",()=>{this._map.getCanvas().style.cursor="pointer"}),this._map.on("mouseout",()=>{this._map.getCanvas().style.cursor=""}),this._map.addControl(new maplibregl.NavigationControl)}getMap(){return this._map}applyMapMethod(e,t){this._map[e](...t)}addControl(e,t,n){this._map.addControl(new maplibregl[e](t),n)}addMarker({lngLat:e,popup:t,options:n}){let a=new maplibregl.Marker(n).setLngLat(e);if(t){let s=new maplibregl.Popup(t.options).setHTML(t.text);a.setPopup(s)}a.addTo(this._map)}addLayer(e){this._map.addLayer(e),typeof Shiny<"u"&&this._map.on("click",e.id,t=>{console.log(t,t.features[0]);let n=e.id.replaceAll("-","_"),a=`${this._id}_layer_${n}`,s={props:t.features[0].properties,layer_id:e.id};console.log(a,s),Shiny.onInputChange(a,s)})}addPopup(e,t,n=null){let a={closeButton:!1},s=new maplibregl.Popup(a);this._map.on("click",e,o=>{let i=o.features[0],p=$(i,t,n);s.setLngLat(o.lngLat).setHTML(p).addTo(this._map)})}addTooltip(e,t,n=null){let a={closeButton:!1,closeOnClick:!1},s=new maplibregl.Popup(a);this._map.on("mousemove",e,o=>{let i=o.features[0],p=$(i,t,n);s.setLngLat(o.lngLat).setHTML(p).addTo(this._map)}),this._map.on("mouseleave",e,()=>{s.remove()})}setSourceData(e,t){this._map.getSource(e).setData(t)}render(e){e.forEach(([t,n])=>{if(["addLayer","addPopup","addTooltip","addMarker","addPopup","addControl","setSourceData"].includes(t)){console.log("Custom method",t,n),this[t](...n);return}console.log("Map method",t),this.applyMapMethod(t,n)})}};var ee="0.1.0";console.log("pymaplibregl",ee);typeof Shiny>"u"&&(window.pymaplibregl=function({mapOptions:r,calls:e}){let t="pymaplibregl",n=document.getElementById(t),a=new S(Object.assign({container:n.id},r));a.getMap().on("load",()=>{a.render(e)})});if(typeof Shiny<"u"){class r extends Shiny.OutputBinding{find(t){return t.find(".shiny-maplibregl-output")}renderValue(t,n){console.log("id:",t.id,"payload:",n);let a=new S(Object.assign({container:t.id},n.mapData.mapOptions)),s=a.getMap();s.on("load",()=>{a.render(n.mapData.calls)}),s.on("click",i=>{console.log(i);let p=`${t.id}`,u={coords:i.lngLat,point:i.point};console.log(p,u),Shiny.onInputChange(p,u)});let o=`pymaplibregl-${t.id}`;console.log(o),Shiny.addCustomMessageHandler(o,({id:i,calls:p})=>{console.log(i,p),a.render(p)})}}Shiny.outputBindings.register(new r,"shiny-maplibregl-output")}})(); +/*! Bundled license information: + +mustache/mustache.mjs: + (*! + * mustache.js - Logic-less {{mustache}} templates with JavaScript + * http://github.com/janl/mustache.js + *) +*/ diff --git a/package-lock.json b/package-lock.json index da69116b..92d81e05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,16 @@ { "name": "pymaplibregl", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pymaplibregl", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", + "dependencies": { + "mustache": "^4.2.0" + }, "devDependencies": { "esbuild": "0.19.10", "prettier": "3.1.1" @@ -419,6 +422,14 @@ "@esbuild/win32-x64": "0.19.10" } }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/prettier": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", diff --git a/package.json b/package.json index 30aa8932..beb7d000 100644 --- a/package.json +++ b/package.json @@ -20,5 +20,8 @@ "devDependencies": { "esbuild": "0.19.10", "prettier": "3.1.1" + }, + "dependencies": { + "mustache": "^4.2.0" } } diff --git a/srcjs/pymaplibregl.js b/srcjs/pymaplibregl.js index ecc1bf39..0774443a 100644 --- a/srcjs/pymaplibregl.js +++ b/srcjs/pymaplibregl.js @@ -1,6 +1,11 @@ +import mustache from "mustache"; // import { getCustomMapMethods } from "./mapmethods"; -function getTextFromFeature(feature, property) { +function getTextFromFeature(feature, property, template) { + if (template !== null) { + return mustache.render(template, feature.properties); + } + if (property === null) { const text = Object.keys(feature.properties) .map((key) => `${key}: ${feature.properties[key]}`) @@ -70,7 +75,7 @@ export default class PyMapLibreGL { } } - addPopup(layerId, property) { + addPopup(layerId, property, template = null) { const popupOptions = { closeButton: false, }; @@ -78,12 +83,12 @@ export default class PyMapLibreGL { this._map.on("click", layerId, (e) => { const feature = e.features[0]; // const text = feature.properties[property]; - const text = getTextFromFeature(feature, property); + const text = getTextFromFeature(feature, property, template); popup.setLngLat(e.lngLat).setHTML(text).addTo(this._map); }); } - addTooltip(layerId, property) { + addTooltip(layerId, property, template = null) { const popupOptions = { closeButton: false, closeOnClick: false, @@ -91,7 +96,7 @@ export default class PyMapLibreGL { const popup = new maplibregl.Popup(popupOptions); this._map.on("mousemove", layerId, (e) => { const feature = e.features[0]; - const text = getTextFromFeature(feature, property); + const text = getTextFromFeature(feature, property, template); popup.setLngLat(e.lngLat).setHTML(text).addTo(this._map); }); From 8ab8ff0a450d53442017cee93f368b8a4cc87f5b Mon Sep 17 00:00:00 2001 From: Stefan Kuethe Date: Wed, 24 Jan 2024 15:15:26 +0100 Subject: [PATCH 06/11] Update jupyter --- docs/examples/jupyter/getting_started.ipynb | 200 +++----------------- maplibre/srcjs/index.js | 8 +- maplibre/srcjs/ipywidget.js | 132 ++----------- package.json | 3 +- srcjs/mapmethods.js | 14 +- srcjs/pymaplibregl.js | 4 + srcjs/utils.js | 18 ++ 7 files changed, 71 insertions(+), 308 deletions(-) create mode 100644 srcjs/utils.js diff --git a/docs/examples/jupyter/getting_started.ipynb b/docs/examples/jupyter/getting_started.ipynb index 388953c0..1d293c23 100644 --- a/docs/examples/jupyter/getting_started.ipynb +++ b/docs/examples/jupyter/getting_started.ipynb @@ -70,14 +70,14 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 27, "id": "e6e2284c-1862-4697-ad94-f535b3682197", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ece4babe53924aa7886553d1b5d53bec", + "model_id": "8424f04853ea4a41ac6a3697349bbd3e", "version_major": 2, "version_minor": 0 }, @@ -85,7 +85,7 @@ "MapWidget(calls=[['addControl', ('ScaleControl', {'unit': 'metric'}, 'bottom-left')], ['addLayer', ({'id': 'ea…" ] }, - "execution_count": 4, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -94,7 +94,7 @@ "m = Map()\n", "m.add_control(ScaleControl(), position=\"bottom-left\")\n", "m.add_layer(earthquake_circles)\n", - "m.add_tooltip(layer_id, \"mag\")\n", + "m.add_tooltip(layer_id)\n", "m.add_marker(Marker(lng_lat=(100.507, 13.745)))\n", "m" ] @@ -109,39 +109,29 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 28, "id": "356960fa-b866-42c8-a58e-0c9a417c28eb", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "60ef57a918734016b9766e41615c1935", + "model_id": "7998e7feb14045d6a8dfc637d6775c98", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "interactive(children=(IntSlider(value=5, description='radius', max=15, min=-5), Output()), _dom_classes=('widg…" + "interactive(children=(IntSlider(value=4, description='radius', max=8, min=1), Output()), _dom_classes=('widget…" ] }, "metadata": {}, "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "(radius)>" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ - "widgets.interact(\n", + "change_radius = widgets.interact(\n", " lambda radius: m.set_paint_property(layer_id, \"circle-radius\", radius),\n", - " radius=5\n", + " radius=(1, 8, 1)\n", ")" ] }, @@ -155,14 +145,14 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 21, "id": "8ecd93a6-f471-4350-a052-7a9171fa1606", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "9b52cdb12c3b4af4ac8bbbfc885aea30", + "model_id": "ca7ad45e679d479795b3c05014c8e6b4", "version_major": 2, "version_minor": 0 }, @@ -172,20 +162,10 @@ }, "metadata": {}, "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "(color)>" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ - "widgets.interact(\n", + "change_color = widgets.interact(\n", " lambda color: m.set_paint_property(layer_id, \"circle-color\", color),\n", " color=[\"green\", \"yellow\", \"orange\", \"red\"]\n", ")" @@ -201,39 +181,29 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 31, "id": "0b73f056-f35a-46bb-a092-d899c64cd67e", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "5a4891d6fb0e418bbfe22c4661363022", + "model_id": "405b4aba579b42248573eaf356f629d4", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "interactive(children=(IntSlider(value=3, description='mag_min', max=9, min=-3), Output()), _dom_classes=('widg…" + "interactive(children=(IntSlider(value=4, description='mag_min', max=7, min=1), Output()), _dom_classes=('widge…" ] }, "metadata": {}, "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "(mag_min)>" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ - "widgets.interact(\n", + "filter_mag = widgets.interact(\n", " lambda mag_min: m.set_filter(layer_id, [\">=\", [\"get\", \"mag\"], mag_min]),\n", - " mag_min=3\n", + " mag_min=(1,7,1)\n", ")" ] }, @@ -247,14 +217,14 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 32, "id": "a9c5ddf7-074e-45b0-8cfe-15750fd0b4d5", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "119b58c9640f4b058fc218ce17203edf", + "model_id": "3957fc738b2643cfa207f9b62edbfdda", "version_major": 2, "version_minor": 0 }, @@ -262,7 +232,7 @@ "Output()" ] }, - "execution_count": 8, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -284,136 +254,10 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "7ad74d91-1137-45b4-8791-83dc3546535e", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Help on _InteractFactory in module ipywidgets.widgets.interaction object:\n", - "\n", - "class _InteractFactory(builtins.object)\n", - " | _InteractFactory(cls, options, kwargs={})\n", - " | \n", - " | Factory for instances of :class:`interactive`.\n", - " | \n", - " | This class is needed to support options like::\n", - " | \n", - " | >>> @interact.options(manual=True)\n", - " | ... def greeting(text=\"World\"):\n", - " | ... print(\"Hello {}\".format(text))\n", - " | \n", - " | Parameters\n", - " | ----------\n", - " | cls : class\n", - " | The subclass of :class:`interactive` to construct.\n", - " | options : dict\n", - " | A dict of options used to construct the interactive\n", - " | function. By default, this is returned by\n", - " | ``cls.default_options()``.\n", - " | kwargs : dict\n", - " | A dict of **kwargs to use for widgets.\n", - " | \n", - " | Methods defined here:\n", - " | \n", - " | __call__(self, _InteractFactory__interact_f=None, **kwargs)\n", - " | Make the given function interactive by adding and displaying\n", - " | the corresponding :class:`interactive` widget.\n", - " | \n", - " | Expects the first argument to be a function. Parameters to this\n", - " | function are widget abbreviations passed in as keyword arguments\n", - " | (``**kwargs``). Can be used as a decorator (see examples).\n", - " | \n", - " | Returns\n", - " | -------\n", - " | f : __interact_f with interactive widget attached to it.\n", - " | \n", - " | Parameters\n", - " | ----------\n", - " | __interact_f : function\n", - " | The function to which the interactive widgets are tied. The `**kwargs`\n", - " | should match the function signature. Passed to :func:`interactive()`\n", - " | **kwargs : various, optional\n", - " | An interactive widget is created for each keyword argument that is a\n", - " | valid widget abbreviation. Passed to :func:`interactive()`\n", - " | \n", - " | Examples\n", - " | --------\n", - " | Render an interactive text field that shows the greeting with the passed in\n", - " | text::\n", - " | \n", - " | # 1. Using interact as a function\n", - " | def greeting(text=\"World\"):\n", - " | print(\"Hello {}\".format(text))\n", - " | interact(greeting, text=\"Jupyter Widgets\")\n", - " | \n", - " | # 2. Using interact as a decorator\n", - " | @interact\n", - " | def greeting(text=\"World\"):\n", - " | print(\"Hello {}\".format(text))\n", - " | \n", - " | # 3. Using interact as a decorator with named parameters\n", - " | @interact(text=\"Jupyter Widgets\")\n", - " | def greeting(text=\"World\"):\n", - " | print(\"Hello {}\".format(text))\n", - " | \n", - " | Render an interactive slider widget and prints square of number::\n", - " | \n", - " | # 1. Using interact as a function\n", - " | def square(num=1):\n", - " | print(\"{} squared is {}\".format(num, num*num))\n", - " | interact(square, num=5)\n", - " | \n", - " | # 2. Using interact as a decorator\n", - " | @interact\n", - " | def square(num=2):\n", - " | print(\"{} squared is {}\".format(num, num*num))\n", - " | \n", - " | # 3. Using interact as a decorator with named parameters\n", - " | @interact(num=5)\n", - " | def square(num=2):\n", - " | print(\"{} squared is {}\".format(num, num*num))\n", - " | \n", - " | __init__(self, cls, options, kwargs={})\n", - " | Initialize self. See help(type(self)) for accurate signature.\n", - " | \n", - " | options(self, **kwds)\n", - " | Change options for interactive functions.\n", - " | \n", - " | Returns\n", - " | -------\n", - " | A new :class:`_InteractFactory` which will apply the\n", - " | options when called.\n", - " | \n", - " | widget(self, f)\n", - " | Return an interactive function widget for the given function.\n", - " | \n", - " | The widget is only constructed, not displayed nor attached to\n", - " | the function.\n", - " | \n", - " | Returns\n", - " | -------\n", - " | An instance of ``self.cls`` (typically :class:`interactive`).\n", - " | \n", - " | Parameters\n", - " | ----------\n", - " | f : function\n", - " | The function to which the interactive widgets are tied.\n", - " | \n", - " | ----------------------------------------------------------------------\n", - " | Data descriptors defined here:\n", - " | \n", - " | __dict__\n", - " | dictionary for instance variables (if defined)\n", - " | \n", - " | __weakref__\n", - " | list of weak references to the object (if defined)\n", - "\n" - ] - } - ], + "outputs": [], "source": [] }, { diff --git a/maplibre/srcjs/index.js b/maplibre/srcjs/index.js index 04cabad5..dc3f5b15 100644 --- a/maplibre/srcjs/index.js +++ b/maplibre/srcjs/index.js @@ -1,7 +1,7 @@ -(()=>{var B=Object.prototype.toString,_=Array.isArray||function(e){return B.call(e)==="[object Array]"};function R(r){return typeof r=="function"}function D(r){return _(r)?"array":typeof r}function P(r){return r.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function x(r,e){return r!=null&&typeof r=="object"&&e in r}function H(r,e){return r!=null&&typeof r!="object"&&r.hasOwnProperty&&r.hasOwnProperty(e)}var N=RegExp.prototype.test;function W(r,e){return N.call(r,e)}var q=/\S/;function F(r){return!W(q,r)}var G={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function K(r){return String(r).replace(/[&<>"'`=\/]/g,function(t){return G[t]})}var V=/\s*/,z=/\s+/,j=/\s*=/,J=/\s*\}/,Q=/#|\^|\/|>|\{|&|=|!/;function X(r,e){if(!r)return[];var t=!1,n=[],a=[],s=[],o=!1,i=!1,p="",u=0;function h(){if(o&&!i)for(;s.length;)delete a[s.pop()];else s=[];o=!1,i=!1}var v,m,O;function U(y){if(typeof y=="string"&&(y=y.split(z,2)),!_(y)||y.length!==2)throw new Error("Invalid tags: "+y);v=new RegExp(P(y[0])+"\\s*"),m=new RegExp("\\s*"+P(y[1])),O=new RegExp("\\s*"+P("}"+y[1]))}U(e||g.tags);for(var l=new E(r),w,f,d,k,L,b;!l.eos();){if(w=l.pos,d=l.scanUntil(v),d)for(var M=0,A=d.length;M"?L=[f,d,w,l.pos,p,u,t]:L=[f,d,w,l.pos],u++,a.push(L),f==="#"||f==="^")n.push(L);else if(f==="/"){if(b=n.pop(),!b)throw new Error('Unopened section "'+d+'" at '+w);if(b[1]!==d)throw new Error('Unclosed section "'+b[1]+'" at '+w)}else f==="name"||f==="{"||f==="&"?i=!0:f==="="&&U(d)}if(h(),b=n.pop(),b)throw new Error('Unclosed section "'+b[1]+'" at '+l.pos);return Z(Y(a))}function Y(r){for(var e=[],t,n,a=0,s=r.length;a0?n[n.length-1][4]:e;break;default:t.push(a)}return e}function E(r){this.string=r,this.tail=r,this.pos=0}E.prototype.eos=function(){return this.tail===""};E.prototype.scan=function(e){var t=this.tail.match(e);if(!t||t.index!==0)return"";var n=t[0];return this.tail=this.tail.substring(n.length),this.pos+=n.length,n};E.prototype.scanUntil=function(e){var t=this.tail.search(e),n;switch(t){case-1:n=this.tail,this.tail="";break;case 0:n="";break;default:n=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=n.length,n};function C(r,e){this.view=r,this.cache={".":this.view},this.parent=e}C.prototype.push=function(e){return new C(e,this)};C.prototype.lookup=function(e){var t=this.cache,n;if(t.hasOwnProperty(e))n=t[e];else{for(var a=this,s,o,i,p=!1;a;){if(e.indexOf(".")>0)for(s=a.view,o=e.split("."),i=0;s!=null&&i"?u=this.renderPartial(i,t,n,s):p==="&"?u=this.unescapedValue(i,t):p==="name"?u=this.escapedValue(i,t,s):p==="text"&&(u=this.rawValue(i)),u!==void 0&&(o+=u);return o};c.prototype.renderSection=function(e,t,n,a,s){var o=this,i="",p=t.lookup(e[1]);function u(m){return o.render(m,t,n,s)}if(p){if(_(p))for(var h=0,v=p.length;h0||!n)&&(s[o]=a+s[o]);return s.join(` -`)};c.prototype.renderPartial=function(e,t,n,a){if(n){var s=this.getConfigTags(a),o=R(n)?n(e[1]):n[e[1]];if(o!=null){var i=e[6],p=e[5],u=e[4],h=o;p==0&&u&&(h=this.indentPartial(o,u,i));var v=this.parse(h,s);return this.renderTokens(v,t,n,h,a)}}};c.prototype.unescapedValue=function(e,t){var n=t.lookup(e[1]);if(n!=null)return n};c.prototype.escapedValue=function(e,t,n){var a=this.getConfigEscape(n)||g.escape,s=t.lookup(e[1]);if(s!=null)return typeof s=="number"&&a===g.escape?String(s):a(s)};c.prototype.rawValue=function(e){return e[1]};c.prototype.getConfigTags=function(e){return _(e)?e:e&&typeof e=="object"?e.tags:void 0};c.prototype.getConfigEscape=function(e){if(e&&typeof e=="object"&&!_(e))return e.escape};var g={name:"mustache.js",version:"4.2.0",tags:["{{","}}"],clearCache:void 0,escape:void 0,parse:void 0,render:void 0,Scanner:void 0,Context:void 0,Writer:void 0,set templateCache(r){T.templateCache=r},get templateCache(){return T.templateCache}},T=new c;g.clearCache=function(){return T.clearCache()};g.parse=function(e,t){return T.parse(e,t)};g.render=function(e,t,n,a){if(typeof e!="string")throw new TypeError('Invalid template! Template should be a "string" but "'+D(e)+'" was given as the first argument for mustache#render(template, view, partials)');return T.render(e,t,n,a)};g.escape=K;g.Scanner=E;g.Context=C;g.Writer=c;var I=g;function $(r,e,t){return t!==null?I.render(t,r.properties):e===null?Object.keys(r.properties).map(a=>`${a}: ${r.properties[a]}`).join("
"):r.properties[e]}var S=class{constructor(e){this._id=e.container,this._map=new maplibregl.Map(e),this._map.on("mouseover",()=>{this._map.getCanvas().style.cursor="pointer"}),this._map.on("mouseout",()=>{this._map.getCanvas().style.cursor=""}),this._map.addControl(new maplibregl.NavigationControl)}getMap(){return this._map}applyMapMethod(e,t){this._map[e](...t)}addControl(e,t,n){this._map.addControl(new maplibregl[e](t),n)}addMarker({lngLat:e,popup:t,options:n}){let a=new maplibregl.Marker(n).setLngLat(e);if(t){let s=new maplibregl.Popup(t.options).setHTML(t.text);a.setPopup(s)}a.addTo(this._map)}addLayer(e){this._map.addLayer(e),typeof Shiny<"u"&&this._map.on("click",e.id,t=>{console.log(t,t.features[0]);let n=e.id.replaceAll("-","_"),a=`${this._id}_layer_${n}`,s={props:t.features[0].properties,layer_id:e.id};console.log(a,s),Shiny.onInputChange(a,s)})}addPopup(e,t,n=null){let a={closeButton:!1},s=new maplibregl.Popup(a);this._map.on("click",e,o=>{let i=o.features[0],p=$(i,t,n);s.setLngLat(o.lngLat).setHTML(p).addTo(this._map)})}addTooltip(e,t,n=null){let a={closeButton:!1,closeOnClick:!1},s=new maplibregl.Popup(a);this._map.on("mousemove",e,o=>{let i=o.features[0],p=$(i,t,n);s.setLngLat(o.lngLat).setHTML(p).addTo(this._map)}),this._map.on("mouseleave",e,()=>{s.remove()})}setSourceData(e,t){this._map.getSource(e).setData(t)}render(e){e.forEach(([t,n])=>{if(["addLayer","addPopup","addTooltip","addMarker","addPopup","addControl","setSourceData"].includes(t)){console.log("Custom method",t,n),this[t](...n);return}console.log("Map method",t),this.applyMapMethod(t,n)})}};var ee="0.1.0";console.log("pymaplibregl",ee);typeof Shiny>"u"&&(window.pymaplibregl=function({mapOptions:r,calls:e}){let t="pymaplibregl",n=document.getElementById(t),a=new S(Object.assign({container:n.id},r));a.getMap().on("load",()=>{a.render(e)})});if(typeof Shiny<"u"){class r extends Shiny.OutputBinding{find(t){return t.find(".shiny-maplibregl-output")}renderValue(t,n){console.log("id:",t.id,"payload:",n);let a=new S(Object.assign({container:t.id},n.mapData.mapOptions)),s=a.getMap();s.on("load",()=>{a.render(n.mapData.calls)}),s.on("click",i=>{console.log(i);let p=`${t.id}`,u={coords:i.lngLat,point:i.point};console.log(p,u),Shiny.onInputChange(p,u)});let o=`pymaplibregl-${t.id}`;console.log(o),Shiny.addCustomMessageHandler(o,({id:i,calls:p})=>{console.log(i,p),a.render(p)})}}Shiny.outputBindings.register(new r,"shiny-maplibregl-output")}})(); +(()=>{var B=Object.prototype.toString,_=Array.isArray||function(e){return B.call(e)==="[object Array]"};function R(n){return typeof n=="function"}function D(n){return _(n)?"array":typeof n}function P(n){return n.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function j(n,e){return n!=null&&typeof n=="object"&&e in n}function F(n,e){return n!=null&&typeof n!="object"&&n.hasOwnProperty&&n.hasOwnProperty(e)}var H=RegExp.prototype.test;function N(n,e){return H.call(n,e)}var W=/\S/;function q(n){return!N(W,n)}var G={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function K(n){return String(n).replace(/[&<>"'`=\/]/g,function(t){return G[t]})}var V=/\s*/,z=/\s+/,I=/\s*=/,J=/\s*\}/,Q=/#|\^|\/|>|\{|&|=|!/;function X(n,e){if(!n)return[];var t=!1,r=[],a=[],s=[],o=!1,i=!1,p="",u=0;function h(){if(o&&!i)for(;s.length;)delete a[s.pop()];else s=[];o=!1,i=!1}var v,m,x;function U(y){if(typeof y=="string"&&(y=y.split(z,2)),!_(y)||y.length!==2)throw new Error("Invalid tags: "+y);v=new RegExp(P(y[0])+"\\s*"),m=new RegExp("\\s*"+P(y[1])),x=new RegExp("\\s*"+P("}"+y[1]))}U(e||g.tags);for(var l=new E(n),w,f,d,k,L,b;!l.eos();){if(w=l.pos,d=l.scanUntil(v),d)for(var M=0,A=d.length;M"?L=[f,d,w,l.pos,p,u,t]:L=[f,d,w,l.pos],u++,a.push(L),f==="#"||f==="^")r.push(L);else if(f==="/"){if(b=r.pop(),!b)throw new Error('Unopened section "'+d+'" at '+w);if(b[1]!==d)throw new Error('Unclosed section "'+b[1]+'" at '+w)}else f==="name"||f==="{"||f==="&"?i=!0:f==="="&&U(d)}if(h(),b=r.pop(),b)throw new Error('Unclosed section "'+b[1]+'" at '+l.pos);return Z(Y(a))}function Y(n){for(var e=[],t,r,a=0,s=n.length;a0?r[r.length-1][4]:e;break;default:t.push(a)}return e}function E(n){this.string=n,this.tail=n,this.pos=0}E.prototype.eos=function(){return this.tail===""};E.prototype.scan=function(e){var t=this.tail.match(e);if(!t||t.index!==0)return"";var r=t[0];return this.tail=this.tail.substring(r.length),this.pos+=r.length,r};E.prototype.scanUntil=function(e){var t=this.tail.search(e),r;switch(t){case-1:r=this.tail,this.tail="";break;case 0:r="";break;default:r=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=r.length,r};function C(n,e){this.view=n,this.cache={".":this.view},this.parent=e}C.prototype.push=function(e){return new C(e,this)};C.prototype.lookup=function(e){var t=this.cache,r;if(t.hasOwnProperty(e))r=t[e];else{for(var a=this,s,o,i,p=!1;a;){if(e.indexOf(".")>0)for(s=a.view,o=e.split("."),i=0;s!=null&&i"?u=this.renderPartial(i,t,r,s):p==="&"?u=this.unescapedValue(i,t):p==="name"?u=this.escapedValue(i,t,s):p==="text"&&(u=this.rawValue(i)),u!==void 0&&(o+=u);return o};c.prototype.renderSection=function(e,t,r,a,s){var o=this,i="",p=t.lookup(e[1]);function u(m){return o.render(m,t,r,s)}if(p){if(_(p))for(var h=0,v=p.length;h0||!r)&&(s[o]=a+s[o]);return s.join(` +`)};c.prototype.renderPartial=function(e,t,r,a){if(r){var s=this.getConfigTags(a),o=R(r)?r(e[1]):r[e[1]];if(o!=null){var i=e[6],p=e[5],u=e[4],h=o;p==0&&u&&(h=this.indentPartial(o,u,i));var v=this.parse(h,s);return this.renderTokens(v,t,r,h,a)}}};c.prototype.unescapedValue=function(e,t){var r=t.lookup(e[1]);if(r!=null)return r};c.prototype.escapedValue=function(e,t,r){var a=this.getConfigEscape(r)||g.escape,s=t.lookup(e[1]);if(s!=null)return typeof s=="number"&&a===g.escape?String(s):a(s)};c.prototype.rawValue=function(e){return e[1]};c.prototype.getConfigTags=function(e){return _(e)?e:e&&typeof e=="object"?e.tags:void 0};c.prototype.getConfigEscape=function(e){if(e&&typeof e=="object"&&!_(e))return e.escape};var g={name:"mustache.js",version:"4.2.0",tags:["{{","}}"],clearCache:void 0,escape:void 0,parse:void 0,render:void 0,Scanner:void 0,Context:void 0,Writer:void 0,set templateCache(n){T.templateCache=n},get templateCache(){return T.templateCache}},T=new c;g.clearCache=function(){return T.clearCache()};g.parse=function(e,t){return T.parse(e,t)};g.render=function(e,t,r,a){if(typeof e!="string")throw new TypeError('Invalid template! Template should be a "string" but "'+D(e)+'" was given as the first argument for mustache#render(template, view, partials)');return T.render(e,t,r,a)};g.escape=K;g.Scanner=E;g.Context=C;g.Writer=c;var $=g;function O(n,e,t){return t!==null?$.render(t,n.properties):e===null?Object.keys(n.properties).map(a=>`${a}: ${n.properties[a]}`).join("
"):n.properties[e]}var S=class{constructor(e){this._id=e.container,this._map=new maplibregl.Map(e),this._map.on("mouseover",()=>{this._map.getCanvas().style.cursor="pointer"}),this._map.on("mouseout",()=>{this._map.getCanvas().style.cursor=""}),this._map.addControl(new maplibregl.NavigationControl)}getMap(){return this._map}applyMapMethod(e,t){this._map[e](...t)}addControl(e,t,r){this._map.addControl(new maplibregl[e](t),r)}addMarker({lngLat:e,popup:t,options:r}){let a=new maplibregl.Marker(r).setLngLat(e);if(t){let s=new maplibregl.Popup(t.options).setHTML(t.text);a.setPopup(s)}a.addTo(this._map)}addLayer(e){this._map.addLayer(e),typeof Shiny<"u"&&this._map.on("click",e.id,t=>{console.log(t,t.features[0]);let r=e.id.replaceAll("-","_"),a=`${this._id}_layer_${r}`,s={props:t.features[0].properties,layer_id:e.id};console.log(a,s),Shiny.onInputChange(a,s)})}addPopup(e,t,r=null){let a={closeButton:!1},s=new maplibregl.Popup(a);this._map.on("click",e,o=>{let i=o.features[0],p=O(i,t,r);s.setLngLat(o.lngLat).setHTML(p).addTo(this._map)})}addTooltip(e,t,r=null){let a={closeButton:!1,closeOnClick:!1},s=new maplibregl.Popup(a);this._map.on("mousemove",e,o=>{let i=o.features[0],p=O(i,t,r);s.setLngLat(o.lngLat).setHTML(p).addTo(this._map)}),this._map.on("mouseleave",e,()=>{s.remove()})}setSourceData(e,t){this._map.getSource(e).setData(t)}render(e){e.forEach(([t,r])=>{if(["addLayer","addPopup","addTooltip","addMarker","addPopup","addControl","setSourceData"].includes(t)){console.log("Custom method",t,r),this[t](...r);return}console.log("Map method",t),this.applyMapMethod(t,r)})}};var ee="0.1.0";console.log("pymaplibregl",ee);typeof Shiny>"u"&&(window.pymaplibregl=function({mapOptions:n,calls:e}){let t="pymaplibregl",r=document.getElementById(t),a=new S(Object.assign({container:r.id},n));a.getMap().on("load",()=>{a.render(e)})});if(typeof Shiny<"u"){class n extends Shiny.OutputBinding{find(t){return t.find(".shiny-maplibregl-output")}renderValue(t,r){console.log("id:",t.id,"payload:",r);let a=new S(Object.assign({container:t.id},r.mapData.mapOptions)),s=a.getMap();s.on("load",()=>{a.render(r.mapData.calls)}),s.on("click",i=>{console.log(i);let p=`${t.id}`,u={coords:i.lngLat,point:i.point};console.log(p,u),Shiny.onInputChange(p,u)});let o=`pymaplibregl-${t.id}`;console.log(o),Shiny.addCustomMessageHandler(o,({id:i,calls:p})=>{console.log(i,p),a.render(p)})}}Shiny.outputBindings.register(new n,"shiny-maplibregl-output")}})(); /*! Bundled license information: mustache/mustache.mjs: diff --git a/maplibre/srcjs/ipywidget.js b/maplibre/srcjs/ipywidget.js index 3a488ce7..46b5d102 100644 --- a/maplibre/srcjs/ipywidget.js +++ b/maplibre/srcjs/ipywidget.js @@ -1,122 +1,12 @@ -// srcjs/ipywidget.js -import maplibregl from "https://esm.sh/maplibre-gl@3.6.2"; +import R from"https://esm.sh/maplibre-gl@3.6.2";var H=Object.prototype.toString,T=Array.isArray||function(e){return H.call(e)==="[object Array]"};function U(n){return typeof n=="function"}function N(n){return T(n)?"array":typeof n}function L(n){return n.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function _(n,e){return n!=null&&typeof n=="object"&&e in n}function $(n,e){return n!=null&&typeof n!="object"&&n.hasOwnProperty&&n.hasOwnProperty(e)}var B=RegExp.prototype.test;function D(n,e){return B.call(n,e)}var z=/\S/;function K(n){return!D(z,n)}var G={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function J(n){return String(n).replace(/[&<>"'`=\/]/g,function(t){return G[t]})}var Q=/\s*/,V=/\s+/,A=/\s*=/,X=/\s*\}/,Y=/#|\^|\/|>|\{|&|=|!/;function Z(n,e){if(!n)return[];var t=!1,r=[],s=[],a=[],o=!1,i=!1,c="",u=0;function p(){if(o&&!i)for(;a.length;)delete s[a.pop()];else a=[];o=!1,i=!1}var g,C,m;function O(y){if(typeof y=="string"&&(y=y.split(V,2)),!T(y)||y.length!==2)throw new Error("Invalid tags: "+y);g=new RegExp(L(y[0])+"\\s*"),C=new RegExp("\\s*"+L(y[1])),m=new RegExp("\\s*"+L("}"+y[1]))}O(e||v.tags);for(var l=new S(n),w,h,d,E,P,b;!l.eos();){if(w=l.pos,d=l.scanUntil(g),d)for(var x=0,q=d.length;x"?P=[h,d,w,l.pos,c,u,t]:P=[h,d,w,l.pos],u++,s.push(P),h==="#"||h==="^")r.push(P);else if(h==="/"){if(b=r.pop(),!b)throw new Error('Unopened section "'+d+'" at '+w);if(b[1]!==d)throw new Error('Unclosed section "'+b[1]+'" at '+w)}else h==="name"||h==="{"||h==="&"?i=!0:h==="="&&O(d)}if(p(),b=r.pop(),b)throw new Error('Unclosed section "'+b[1]+'" at '+l.pos);return te(ee(s))}function ee(n){for(var e=[],t,r,s=0,a=n.length;s0?r[r.length-1][4]:e;break;default:t.push(s)}return e}function S(n){this.string=n,this.tail=n,this.pos=0}S.prototype.eos=function(){return this.tail===""};S.prototype.scan=function(e){var t=this.tail.match(e);if(!t||t.index!==0)return"";var r=t[0];return this.tail=this.tail.substring(r.length),this.pos+=r.length,r};S.prototype.scanUntil=function(e){var t=this.tail.search(e),r;switch(t){case-1:r=this.tail,this.tail="";break;case 0:r="";break;default:r=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=r.length,r};function k(n,e){this.view=n,this.cache={".":this.view},this.parent=e}k.prototype.push=function(e){return new k(e,this)};k.prototype.lookup=function(e){var t=this.cache,r;if(t.hasOwnProperty(e))r=t[e];else{for(var s=this,a,o,i,c=!1;s;){if(e.indexOf(".")>0)for(a=s.view,o=e.split("."),i=0;a!=null&&i"?u=this.renderPartial(i,t,r,a):c==="&"?u=this.unescapedValue(i,t):c==="name"?u=this.escapedValue(i,t,a):c==="text"&&(u=this.rawValue(i)),u!==void 0&&(o+=u);return o};f.prototype.renderSection=function(e,t,r,s,a){var o=this,i="",c=t.lookup(e[1]);function u(C){return o.render(C,t,r,a)}if(c){if(T(c))for(var p=0,g=c.length;p0||!r)&&(a[o]=s+a[o]);return a.join(` +`)};f.prototype.renderPartial=function(e,t,r,s){if(r){var a=this.getConfigTags(s),o=U(r)?r(e[1]):r[e[1]];if(o!=null){var i=e[6],c=e[5],u=e[4],p=o;c==0&&u&&(p=this.indentPartial(o,u,i));var g=this.parse(p,a);return this.renderTokens(g,t,r,p,s)}}};f.prototype.unescapedValue=function(e,t){var r=t.lookup(e[1]);if(r!=null)return r};f.prototype.escapedValue=function(e,t,r){var s=this.getConfigEscape(r)||v.escape,a=t.lookup(e[1]);if(a!=null)return typeof a=="number"&&s===v.escape?String(a):s(a)};f.prototype.rawValue=function(e){return e[1]};f.prototype.getConfigTags=function(e){return T(e)?e:e&&typeof e=="object"?e.tags:void 0};f.prototype.getConfigEscape=function(e){if(e&&typeof e=="object"&&!T(e))return e.escape};var v={name:"mustache.js",version:"4.2.0",tags:["{{","}}"],clearCache:void 0,escape:void 0,parse:void 0,render:void 0,Scanner:void 0,Context:void 0,Writer:void 0,set templateCache(n){M.templateCache=n},get templateCache(){return M.templateCache}},M=new f;v.clearCache=function(){return M.clearCache()};v.parse=function(e,t){return M.parse(e,t)};v.render=function(e,t,r,s){if(typeof e!="string")throw new TypeError('Invalid template! Template should be a "string" but "'+N(e)+'" was given as the first argument for mustache#render(template, view, partials)');return M.render(e,t,r,s)};v.escape=J;v.Scanner=S;v.Context=k;v.Writer=f;var F=v;function j(n,e,t){return t!==null?F.render(t,n.properties):e===null?Object.keys(n.properties).map(s=>`${s}: ${n.properties[s]}`).join("
"):n.properties[e]}function I(n,e){let[t,r]=e;console.log(t,r),n[t](...r)}function W(n,e){return{addTooltip:function(t,r,s=null){let a={closeButton:!1,closeOnClick:!1},o=new n.Popup(a);e.on("mousemove",t,i=>{let c=i.features[0],u=j(c,r,s);o.setLngLat(i.lngLat).setHTML(u).addTo(e)}),e.on("mouseleave",t,()=>{o.remove()})},addControl:function(t,r,s){e.addControl(new n[t](r),s)},addPopup:function(t,r,s=null){let a={closeButton:!1},o=new n.Popup(a);e.on("click",t,i=>{let c=i.features[0],u=j(c,r,s);o.setLngLat(i.lngLat).setHTML(u).addTo(e)})},addMarker:function({lngLat:t,popup:r,options:s}){let a=new n.Marker(s).setLngLat(t);if(r){let o=new n.Popup(r.options).setHTML(r.text);a.setPopup(o)}a.addTo(e)},setSourceData:function(t,r){e.getSource(t).setData(r)}}}function ne(n){let e="pymaplibregl",t=document.createElement("div");return t.id=e,t.style.height=n.get("height"),t}function re(n,e){let t=new R.Map(n);return n.navigationControl===void 0&&(n.navigationControl=!0),n.navigationControl&&t.addControl(new R.NavigationControl),t.on("mouseover",()=>{t.getCanvas().style.cursor="pointer"}),t.on("mouseout",()=>{t.getCanvas().style.cursor=""}),t.on("click",r=>{e.set("lng_lat",r.lngLat),e.save_changes()}),t.once("load",()=>{t.resize()}),t}function pe({model:n,el:e}){console.log("maplibregl",R.version);let t=ne(n),r=Object.assign({container:t},n.get("map_options"));console.log(r);let s=re(r,n),a=W(R,s),o=c=>{c.forEach(u=>{if(Object.keys(a).includes(u[0])){console.log("internal call",u);let[p,g]=u;a[p](...g);return}I(s,u)})},i=n.get("calls");s.on("load",()=>{console.log("init calls",i),o(i),n.set("_rendered",!0),n.save_changes()}),n.on("msg:custom",c=>{console.log("custom msg",c),o(c.calls)}),e.appendChild(t)}export{pe as render}; +/*! Bundled license information: -// srcjs/mapmethods.js -function applyMapMethod(map, call) { - const [methodName, params] = call; - console.log(methodName, params); - map[methodName](...params); -} -function getCustomMapMethods(maplibregl2, map) { - return { - addTooltip: function(layerId, property) { - const popupOptions = { - closeButton: false, - closeOnClick: false - }; - const popup = new maplibregl2.Popup(popupOptions); - map.on("mousemove", layerId, (e) => { - const feature = e.features[0]; - const text = feature.properties[property]; - popup.setLngLat(e.lngLat).setHTML(text).addTo(map); - }); - map.on("mouseleave", layerId, () => { - popup.remove(); - }); - }, - addControl: function(type, options, position) { - map.addControl(new maplibregl2[type](options), position); - }, - addPopup: function(layerId, property) { - const popupOptions = { - closeButton: false - }; - const popup = new maplibregl2.Popup(popupOptions); - map.on("click", layerId, (e) => { - const feature = e.features[0]; - const text = feature.properties[property]; - popup.setLngLat(e.lngLat).setHTML(text).addTo(map); - }); - }, - addMarker: function({ lngLat, popup, options }) { - const marker = new maplibregl2.Marker(options).setLngLat(lngLat); - if (popup) { - const popup_ = new maplibregl2.Popup(popup.options).setHTML(popup.text); - marker.setPopup(popup_); - } - marker.addTo(map); - }, - setSourceData: function(sourceId, data) { - map.getSource(sourceId).setData(data); - } - }; -} - -// srcjs/ipywidget.js -function createContainer(model) { - const id = "pymaplibregl"; - const container = document.createElement("div"); - container.id = id; - container.style.height = model.get("height"); - return container; -} -function createMap(mapOptions, model) { - const map = new maplibregl.Map(mapOptions); - if (mapOptions.navigationControl === void 0) { - mapOptions.navigationControl = true; - } - if (mapOptions.navigationControl) { - map.addControl(new maplibregl.NavigationControl()); - } - map.on("mouseover", () => { - map.getCanvas().style.cursor = "pointer"; - }); - map.on("mouseout", () => { - map.getCanvas().style.cursor = ""; - }); - map.on("click", (e) => { - model.set("lng_lat", e.lngLat); - model.save_changes(); - }); - map.once("load", () => { - map.resize(); - }); - return map; -} -function render({ model, el }) { - console.log("maplibregl", maplibregl.version); - const container = createContainer(model); - const mapOptions = Object.assign( - { container }, - model.get("map_options") - ); - console.log(mapOptions); - const map = createMap(mapOptions, model); - const customMapMethods = getCustomMapMethods(maplibregl, map); - const apply = (calls2) => { - calls2.forEach((call) => { - if (Object.keys(customMapMethods).includes(call[0])) { - console.log("internal call", call); - const [name, params] = call; - customMapMethods[name](...params); - return; - } - applyMapMethod(map, call); - }); - }; - const calls = model.get("calls"); - map.on("load", () => { - console.log("init calls", calls); - apply(calls); - model.set("_rendered", true); - model.save_changes(); - }); - model.on("msg:custom", (msg) => { - console.log("custom msg", msg); - apply(msg.calls); - }); - el.appendChild(container); -} -export { - render -}; +mustache/mustache.mjs: + (*! + * mustache.js - Logic-less {{mustache}} templates with JavaScript + * http://github.com/janl/mustache.js + *) +*/ diff --git a/package.json b/package.json index beb7d000..afbf809e 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "scripts": { "build": "esbuild srcjs/index.js --bundle --minify --outfile=maplibre/srcjs/index.js", "build-dev": "esbuild srcjs/index.js --bundle --outfile=maplibre/srcjs/index.js", - "build-ipywidget": "esbuild srcjs/ipywidget.js --bundle --format=esm --outfile=maplibre/srcjs/ipywidget.js", + "build-ipywidget": "esbuild srcjs/ipywidget.js --bundle --minify --format=esm --outfile=maplibre/srcjs/ipywidget.js", + "build-ipywidget-dev": "esbuild srcjs/ipywidget.js --bundle --format=esm --outfile=maplibre/srcjs/ipywidget.js", "build-rwidget": "esbuild srcjs/rwidget.js --bundle --minify --outfile=../r-maplibregl/inst/htmlwidgets/maplibre.js", "prettier": "prettier srcjs --write", "test": "echo \"Error: no test specified\" && exit 1" diff --git a/srcjs/mapmethods.js b/srcjs/mapmethods.js index 17d094fd..4572f495 100644 --- a/srcjs/mapmethods.js +++ b/srcjs/mapmethods.js @@ -1,3 +1,5 @@ +import { getTextFromFeature } from "./utils"; + function applyMapMethod(map, call) { const [methodName, params] = call; console.log(methodName, params); @@ -8,7 +10,7 @@ function applyMapMethod(map, call) { // Custom map methods function getCustomMapMethods(maplibregl, map) { return { - addTooltip: function (layerId, property) { + addTooltip: function (layerId, property, template = null) { const popupOptions = { closeButton: false, closeOnClick: false, @@ -17,7 +19,9 @@ function getCustomMapMethods(maplibregl, map) { map.on("mousemove", layerId, (e) => { const feature = e.features[0]; - const text = feature.properties[property]; + + // const text = feature.properties[property]; + const text = getTextFromFeature(feature, property, template); popup.setLngLat(e.lngLat).setHTML(text).addTo(map); }); @@ -30,14 +34,16 @@ function getCustomMapMethods(maplibregl, map) { map.addControl(new maplibregl[type](options), position); }, - addPopup: function (layerId, property) { + addPopup: function (layerId, property, template = null) { const popupOptions = { closeButton: false, }; const popup = new maplibregl.Popup(popupOptions); map.on("click", layerId, (e) => { const feature = e.features[0]; - const text = feature.properties[property]; + + // const text = feature.properties[property]; + const text = getTextFromFeature(feature, property, template); popup.setLngLat(e.lngLat).setHTML(text).addTo(map); }); }, diff --git a/srcjs/pymaplibregl.js b/srcjs/pymaplibregl.js index 0774443a..f62427b4 100644 --- a/srcjs/pymaplibregl.js +++ b/srcjs/pymaplibregl.js @@ -1,6 +1,9 @@ import mustache from "mustache"; + // import { getCustomMapMethods } from "./mapmethods"; +import { getTextFromFeature } from "./utils"; +/* function getTextFromFeature(feature, property, template) { if (template !== null) { return mustache.render(template, feature.properties); @@ -15,6 +18,7 @@ function getTextFromFeature(feature, property, template) { return feature.properties[property]; } + */ export default class PyMapLibreGL { constructor(mapOptions) { diff --git a/srcjs/utils.js b/srcjs/utils.js new file mode 100644 index 00000000..cedd917b --- /dev/null +++ b/srcjs/utils.js @@ -0,0 +1,18 @@ +import mustache from "mustache"; + +function getTextFromFeature(feature, property, template) { + if (template !== null) { + return mustache.render(template, feature.properties); + } + + if (property === null) { + const text = Object.keys(feature.properties) + .map((key) => `${key}: ${feature.properties[key]}`) + .join("
"); + return text; + } + + return feature.properties[property]; +} + +export { getTextFromFeature }; From 9b2bfeb8c916b90abf7d0e2b8bbd337f30065379 Mon Sep 17 00:00:00 2001 From: Stefan Kuethe Date: Wed, 24 Jan 2024 15:18:10 +0100 Subject: [PATCH 07/11] Update docs --- docs/jupyter.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/jupyter.md b/docs/jupyter.md index 0ca35638..600601f1 100644 --- a/docs/jupyter.md +++ b/docs/jupyter.md @@ -32,21 +32,21 @@ m.add_marker(Marker(lng_lat=(100.507, 13.745))) m # Change radius -widgets.interact( +_ = widgets.interact( lambda radius: m.set_paint_property(layer_id, "circle-radius", radius), - radius=5 + radius=(1, 8, 1) ) # Change color -widgets.interact( +_ = widgets.interact( lambda color: m.set_paint_property(layer_id, "circle-color", color), color=["green", "yellow", "orange", "red"] ) # Set filter on magnitude -widgets.interact( +_ = widgets.interact( lambda mag_min: m.set_filter(layer_id, [">=", ["get", "mag"], mag_min]), - mag_min=3 + mag_min=(1, 8, 1) ) # Observe map-on-click event From 0d70e426d0e826873213847411c08a95ca4e9083 Mon Sep 17 00:00:00 2001 From: Stefan Kuethe Date: Wed, 24 Jan 2024 15:25:44 +0100 Subject: [PATCH 08/11] Update docs --- README.md | 2 +- maplibre/map.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 63ab8ab1..907a0ced 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ poetry install poetry run pytest -poetry run pytest --doctest-modules maplibre +poetry run pytest --ignore=maplibre/ipywidget.py --doctest-modules maplibre ``` ### JavaScript diff --git a/maplibre/map.py b/maplibre/map.py index 90f29660..8f5938f1 100644 --- a/maplibre/map.py +++ b/maplibre/map.py @@ -177,6 +177,11 @@ def add_tooltip( layer_id (str): The layer to which the tooltip is added. prop (str): The property of the source to be displayed. If `None`, all properties are displayed. template (str): A mustache template. If supplied, `prop` is ignored. + + Examples: + >>> map = Map() + >>> # ... + >>> map.add_tooltip("test-layer", template="Name: {{ name }}") """ self.add_call("addTooltip", layer_id, prop, template) From 262c9709f01d0a409f6bc24d21ec592206d99f7e Mon Sep 17 00:00:00 2001 From: Stefan Kuethe Date: Thu, 25 Jan 2024 09:59:39 +0100 Subject: [PATCH 09/11] Add default null value --- maplibre/srcjs/index.js | 8 ++++---- srcjs/pymaplibregl.js | 23 ++--------------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/maplibre/srcjs/index.js b/maplibre/srcjs/index.js index dc3f5b15..ecbff443 100644 --- a/maplibre/srcjs/index.js +++ b/maplibre/srcjs/index.js @@ -1,7 +1,7 @@ -(()=>{var B=Object.prototype.toString,_=Array.isArray||function(e){return B.call(e)==="[object Array]"};function R(n){return typeof n=="function"}function D(n){return _(n)?"array":typeof n}function P(n){return n.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function j(n,e){return n!=null&&typeof n=="object"&&e in n}function F(n,e){return n!=null&&typeof n!="object"&&n.hasOwnProperty&&n.hasOwnProperty(e)}var H=RegExp.prototype.test;function N(n,e){return H.call(n,e)}var W=/\S/;function q(n){return!N(W,n)}var G={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function K(n){return String(n).replace(/[&<>"'`=\/]/g,function(t){return G[t]})}var V=/\s*/,z=/\s+/,I=/\s*=/,J=/\s*\}/,Q=/#|\^|\/|>|\{|&|=|!/;function X(n,e){if(!n)return[];var t=!1,r=[],a=[],s=[],o=!1,i=!1,p="",u=0;function h(){if(o&&!i)for(;s.length;)delete a[s.pop()];else s=[];o=!1,i=!1}var v,m,x;function U(y){if(typeof y=="string"&&(y=y.split(z,2)),!_(y)||y.length!==2)throw new Error("Invalid tags: "+y);v=new RegExp(P(y[0])+"\\s*"),m=new RegExp("\\s*"+P(y[1])),x=new RegExp("\\s*"+P("}"+y[1]))}U(e||g.tags);for(var l=new E(n),w,f,d,k,L,b;!l.eos();){if(w=l.pos,d=l.scanUntil(v),d)for(var M=0,A=d.length;M"?L=[f,d,w,l.pos,p,u,t]:L=[f,d,w,l.pos],u++,a.push(L),f==="#"||f==="^")r.push(L);else if(f==="/"){if(b=r.pop(),!b)throw new Error('Unopened section "'+d+'" at '+w);if(b[1]!==d)throw new Error('Unclosed section "'+b[1]+'" at '+w)}else f==="name"||f==="{"||f==="&"?i=!0:f==="="&&U(d)}if(h(),b=r.pop(),b)throw new Error('Unclosed section "'+b[1]+'" at '+l.pos);return Z(Y(a))}function Y(n){for(var e=[],t,r,a=0,s=n.length;a0?r[r.length-1][4]:e;break;default:t.push(a)}return e}function E(n){this.string=n,this.tail=n,this.pos=0}E.prototype.eos=function(){return this.tail===""};E.prototype.scan=function(e){var t=this.tail.match(e);if(!t||t.index!==0)return"";var r=t[0];return this.tail=this.tail.substring(r.length),this.pos+=r.length,r};E.prototype.scanUntil=function(e){var t=this.tail.search(e),r;switch(t){case-1:r=this.tail,this.tail="";break;case 0:r="";break;default:r=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=r.length,r};function C(n,e){this.view=n,this.cache={".":this.view},this.parent=e}C.prototype.push=function(e){return new C(e,this)};C.prototype.lookup=function(e){var t=this.cache,r;if(t.hasOwnProperty(e))r=t[e];else{for(var a=this,s,o,i,p=!1;a;){if(e.indexOf(".")>0)for(s=a.view,o=e.split("."),i=0;s!=null&&i"?u=this.renderPartial(i,t,r,s):p==="&"?u=this.unescapedValue(i,t):p==="name"?u=this.escapedValue(i,t,s):p==="text"&&(u=this.rawValue(i)),u!==void 0&&(o+=u);return o};c.prototype.renderSection=function(e,t,r,a,s){var o=this,i="",p=t.lookup(e[1]);function u(m){return o.render(m,t,r,s)}if(p){if(_(p))for(var h=0,v=p.length;h0||!r)&&(s[o]=a+s[o]);return s.join(` -`)};c.prototype.renderPartial=function(e,t,r,a){if(r){var s=this.getConfigTags(a),o=R(r)?r(e[1]):r[e[1]];if(o!=null){var i=e[6],p=e[5],u=e[4],h=o;p==0&&u&&(h=this.indentPartial(o,u,i));var v=this.parse(h,s);return this.renderTokens(v,t,r,h,a)}}};c.prototype.unescapedValue=function(e,t){var r=t.lookup(e[1]);if(r!=null)return r};c.prototype.escapedValue=function(e,t,r){var a=this.getConfigEscape(r)||g.escape,s=t.lookup(e[1]);if(s!=null)return typeof s=="number"&&a===g.escape?String(s):a(s)};c.prototype.rawValue=function(e){return e[1]};c.prototype.getConfigTags=function(e){return _(e)?e:e&&typeof e=="object"?e.tags:void 0};c.prototype.getConfigEscape=function(e){if(e&&typeof e=="object"&&!_(e))return e.escape};var g={name:"mustache.js",version:"4.2.0",tags:["{{","}}"],clearCache:void 0,escape:void 0,parse:void 0,render:void 0,Scanner:void 0,Context:void 0,Writer:void 0,set templateCache(n){T.templateCache=n},get templateCache(){return T.templateCache}},T=new c;g.clearCache=function(){return T.clearCache()};g.parse=function(e,t){return T.parse(e,t)};g.render=function(e,t,r,a){if(typeof e!="string")throw new TypeError('Invalid template! Template should be a "string" but "'+D(e)+'" was given as the first argument for mustache#render(template, view, partials)');return T.render(e,t,r,a)};g.escape=K;g.Scanner=E;g.Context=C;g.Writer=c;var $=g;function O(n,e,t){return t!==null?$.render(t,n.properties):e===null?Object.keys(n.properties).map(a=>`${a}: ${n.properties[a]}`).join("
"):n.properties[e]}var S=class{constructor(e){this._id=e.container,this._map=new maplibregl.Map(e),this._map.on("mouseover",()=>{this._map.getCanvas().style.cursor="pointer"}),this._map.on("mouseout",()=>{this._map.getCanvas().style.cursor=""}),this._map.addControl(new maplibregl.NavigationControl)}getMap(){return this._map}applyMapMethod(e,t){this._map[e](...t)}addControl(e,t,r){this._map.addControl(new maplibregl[e](t),r)}addMarker({lngLat:e,popup:t,options:r}){let a=new maplibregl.Marker(r).setLngLat(e);if(t){let s=new maplibregl.Popup(t.options).setHTML(t.text);a.setPopup(s)}a.addTo(this._map)}addLayer(e){this._map.addLayer(e),typeof Shiny<"u"&&this._map.on("click",e.id,t=>{console.log(t,t.features[0]);let r=e.id.replaceAll("-","_"),a=`${this._id}_layer_${r}`,s={props:t.features[0].properties,layer_id:e.id};console.log(a,s),Shiny.onInputChange(a,s)})}addPopup(e,t,r=null){let a={closeButton:!1},s=new maplibregl.Popup(a);this._map.on("click",e,o=>{let i=o.features[0],p=O(i,t,r);s.setLngLat(o.lngLat).setHTML(p).addTo(this._map)})}addTooltip(e,t,r=null){let a={closeButton:!1,closeOnClick:!1},s=new maplibregl.Popup(a);this._map.on("mousemove",e,o=>{let i=o.features[0],p=O(i,t,r);s.setLngLat(o.lngLat).setHTML(p).addTo(this._map)}),this._map.on("mouseleave",e,()=>{s.remove()})}setSourceData(e,t){this._map.getSource(e).setData(t)}render(e){e.forEach(([t,r])=>{if(["addLayer","addPopup","addTooltip","addMarker","addPopup","addControl","setSourceData"].includes(t)){console.log("Custom method",t,r),this[t](...r);return}console.log("Map method",t),this.applyMapMethod(t,r)})}};var ee="0.1.0";console.log("pymaplibregl",ee);typeof Shiny>"u"&&(window.pymaplibregl=function({mapOptions:n,calls:e}){let t="pymaplibregl",r=document.getElementById(t),a=new S(Object.assign({container:r.id},n));a.getMap().on("load",()=>{a.render(e)})});if(typeof Shiny<"u"){class n extends Shiny.OutputBinding{find(t){return t.find(".shiny-maplibregl-output")}renderValue(t,r){console.log("id:",t.id,"payload:",r);let a=new S(Object.assign({container:t.id},r.mapData.mapOptions)),s=a.getMap();s.on("load",()=>{a.render(r.mapData.calls)}),s.on("click",i=>{console.log(i);let p=`${t.id}`,u={coords:i.lngLat,point:i.point};console.log(p,u),Shiny.onInputChange(p,u)});let o=`pymaplibregl-${t.id}`;console.log(o),Shiny.addCustomMessageHandler(o,({id:i,calls:p})=>{console.log(i,p),a.render(p)})}}Shiny.outputBindings.register(new n,"shiny-maplibregl-output")}})(); +(()=>{var B=Object.prototype.toString,_=Array.isArray||function(e){return B.call(e)==="[object Array]"};function R(r){return typeof r=="function"}function D(r){return _(r)?"array":typeof r}function P(r){return r.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function j(r,e){return r!=null&&typeof r=="object"&&e in r}function F(r,e){return r!=null&&typeof r!="object"&&r.hasOwnProperty&&r.hasOwnProperty(e)}var H=RegExp.prototype.test;function N(r,e){return H.call(r,e)}var W=/\S/;function q(r){return!N(W,r)}var G={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function K(r){return String(r).replace(/[&<>"'`=\/]/g,function(t){return G[t]})}var V=/\s*/,z=/\s+/,I=/\s*=/,J=/\s*\}/,Q=/#|\^|\/|>|\{|&|=|!/;function X(r,e){if(!r)return[];var t=!1,n=[],a=[],s=[],o=!1,i=!1,p="",u=0;function h(){if(o&&!i)for(;s.length;)delete a[s.pop()];else s=[];o=!1,i=!1}var v,m,x;function U(y){if(typeof y=="string"&&(y=y.split(z,2)),!_(y)||y.length!==2)throw new Error("Invalid tags: "+y);v=new RegExp(P(y[0])+"\\s*"),m=new RegExp("\\s*"+P(y[1])),x=new RegExp("\\s*"+P("}"+y[1]))}U(e||g.tags);for(var l=new E(r),w,f,d,k,L,b;!l.eos();){if(w=l.pos,d=l.scanUntil(v),d)for(var M=0,A=d.length;M"?L=[f,d,w,l.pos,p,u,t]:L=[f,d,w,l.pos],u++,a.push(L),f==="#"||f==="^")n.push(L);else if(f==="/"){if(b=n.pop(),!b)throw new Error('Unopened section "'+d+'" at '+w);if(b[1]!==d)throw new Error('Unclosed section "'+b[1]+'" at '+w)}else f==="name"||f==="{"||f==="&"?i=!0:f==="="&&U(d)}if(h(),b=n.pop(),b)throw new Error('Unclosed section "'+b[1]+'" at '+l.pos);return Z(Y(a))}function Y(r){for(var e=[],t,n,a=0,s=r.length;a0?n[n.length-1][4]:e;break;default:t.push(a)}return e}function E(r){this.string=r,this.tail=r,this.pos=0}E.prototype.eos=function(){return this.tail===""};E.prototype.scan=function(e){var t=this.tail.match(e);if(!t||t.index!==0)return"";var n=t[0];return this.tail=this.tail.substring(n.length),this.pos+=n.length,n};E.prototype.scanUntil=function(e){var t=this.tail.search(e),n;switch(t){case-1:n=this.tail,this.tail="";break;case 0:n="";break;default:n=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=n.length,n};function C(r,e){this.view=r,this.cache={".":this.view},this.parent=e}C.prototype.push=function(e){return new C(e,this)};C.prototype.lookup=function(e){var t=this.cache,n;if(t.hasOwnProperty(e))n=t[e];else{for(var a=this,s,o,i,p=!1;a;){if(e.indexOf(".")>0)for(s=a.view,o=e.split("."),i=0;s!=null&&i"?u=this.renderPartial(i,t,n,s):p==="&"?u=this.unescapedValue(i,t):p==="name"?u=this.escapedValue(i,t,s):p==="text"&&(u=this.rawValue(i)),u!==void 0&&(o+=u);return o};c.prototype.renderSection=function(e,t,n,a,s){var o=this,i="",p=t.lookup(e[1]);function u(m){return o.render(m,t,n,s)}if(p){if(_(p))for(var h=0,v=p.length;h0||!n)&&(s[o]=a+s[o]);return s.join(` +`)};c.prototype.renderPartial=function(e,t,n,a){if(n){var s=this.getConfigTags(a),o=R(n)?n(e[1]):n[e[1]];if(o!=null){var i=e[6],p=e[5],u=e[4],h=o;p==0&&u&&(h=this.indentPartial(o,u,i));var v=this.parse(h,s);return this.renderTokens(v,t,n,h,a)}}};c.prototype.unescapedValue=function(e,t){var n=t.lookup(e[1]);if(n!=null)return n};c.prototype.escapedValue=function(e,t,n){var a=this.getConfigEscape(n)||g.escape,s=t.lookup(e[1]);if(s!=null)return typeof s=="number"&&a===g.escape?String(s):a(s)};c.prototype.rawValue=function(e){return e[1]};c.prototype.getConfigTags=function(e){return _(e)?e:e&&typeof e=="object"?e.tags:void 0};c.prototype.getConfigEscape=function(e){if(e&&typeof e=="object"&&!_(e))return e.escape};var g={name:"mustache.js",version:"4.2.0",tags:["{{","}}"],clearCache:void 0,escape:void 0,parse:void 0,render:void 0,Scanner:void 0,Context:void 0,Writer:void 0,set templateCache(r){T.templateCache=r},get templateCache(){return T.templateCache}},T=new c;g.clearCache=function(){return T.clearCache()};g.parse=function(e,t){return T.parse(e,t)};g.render=function(e,t,n,a){if(typeof e!="string")throw new TypeError('Invalid template! Template should be a "string" but "'+D(e)+'" was given as the first argument for mustache#render(template, view, partials)');return T.render(e,t,n,a)};g.escape=K;g.Scanner=E;g.Context=C;g.Writer=c;var $=g;function O(r,e,t){return t!==null?$.render(t,r.properties):e===null?Object.keys(r.properties).map(a=>`${a}: ${r.properties[a]}`).join("
"):r.properties[e]}var S=class{constructor(e){this._id=e.container,this._map=new maplibregl.Map(e),this._map.on("mouseover",()=>{this._map.getCanvas().style.cursor="pointer"}),this._map.on("mouseout",()=>{this._map.getCanvas().style.cursor=""}),this._map.addControl(new maplibregl.NavigationControl)}getMap(){return this._map}applyMapMethod(e,t){this._map[e](...t)}addControl(e,t,n){this._map.addControl(new maplibregl[e](t),n)}addMarker({lngLat:e,popup:t,options:n}){let a=new maplibregl.Marker(n).setLngLat(e);if(t){let s=new maplibregl.Popup(t.options).setHTML(t.text);a.setPopup(s)}a.addTo(this._map)}addLayer(e){this._map.addLayer(e),typeof Shiny<"u"&&this._map.on("click",e.id,t=>{console.log(t,t.features[0]);let n=e.id.replaceAll("-","_"),a=`${this._id}_layer_${n}`,s={props:t.features[0].properties,layer_id:e.id};console.log(a,s),Shiny.onInputChange(a,s)})}addPopup(e,t=null,n=null){let a={closeButton:!1},s=new maplibregl.Popup(a);this._map.on("click",e,o=>{let i=o.features[0],p=O(i,t,n);s.setLngLat(o.lngLat).setHTML(p).addTo(this._map)})}addTooltip(e,t=null,n=null){let a={closeButton:!1,closeOnClick:!1},s=new maplibregl.Popup(a);this._map.on("mousemove",e,o=>{let i=o.features[0],p=O(i,t,n);s.setLngLat(o.lngLat).setHTML(p).addTo(this._map)}),this._map.on("mouseleave",e,()=>{s.remove()})}setSourceData(e,t){this._map.getSource(e).setData(t)}render(e){e.forEach(([t,n])=>{if(["addLayer","addPopup","addTooltip","addMarker","addPopup","addControl","setSourceData"].includes(t)){console.log("Custom method",t,n),this[t](...n);return}console.log("Map method",t),this.applyMapMethod(t,n)})}};var ee="0.1.0";console.log("pymaplibregl",ee);typeof Shiny>"u"&&(window.pymaplibregl=function({mapOptions:r,calls:e}){let t="pymaplibregl",n=document.getElementById(t),a=new S(Object.assign({container:n.id},r));a.getMap().on("load",()=>{a.render(e)})});if(typeof Shiny<"u"){class r extends Shiny.OutputBinding{find(t){return t.find(".shiny-maplibregl-output")}renderValue(t,n){console.log("id:",t.id,"payload:",n);let a=new S(Object.assign({container:t.id},n.mapData.mapOptions)),s=a.getMap();s.on("load",()=>{a.render(n.mapData.calls)}),s.on("click",i=>{console.log(i);let p=`${t.id}`,u={coords:i.lngLat,point:i.point};console.log(p,u),Shiny.onInputChange(p,u)});let o=`pymaplibregl-${t.id}`;console.log(o),Shiny.addCustomMessageHandler(o,({id:i,calls:p})=>{console.log(i,p),a.render(p)})}}Shiny.outputBindings.register(new r,"shiny-maplibregl-output")}})(); /*! Bundled license information: mustache/mustache.mjs: diff --git a/srcjs/pymaplibregl.js b/srcjs/pymaplibregl.js index f62427b4..2538c850 100644 --- a/srcjs/pymaplibregl.js +++ b/srcjs/pymaplibregl.js @@ -1,25 +1,6 @@ -import mustache from "mustache"; - // import { getCustomMapMethods } from "./mapmethods"; import { getTextFromFeature } from "./utils"; -/* -function getTextFromFeature(feature, property, template) { - if (template !== null) { - return mustache.render(template, feature.properties); - } - - if (property === null) { - const text = Object.keys(feature.properties) - .map((key) => `${key}: ${feature.properties[key]}`) - .join("
"); - return text; - } - - return feature.properties[property]; -} - */ - export default class PyMapLibreGL { constructor(mapOptions) { this._id = mapOptions.container; @@ -79,7 +60,7 @@ export default class PyMapLibreGL { } } - addPopup(layerId, property, template = null) { + addPopup(layerId, property = null, template = null) { const popupOptions = { closeButton: false, }; @@ -92,7 +73,7 @@ export default class PyMapLibreGL { }); } - addTooltip(layerId, property, template = null) { + addTooltip(layerId, property = null, template = null) { const popupOptions = { closeButton: false, closeOnClick: false, From 646649d7863d789dc8287f94513052c1ff15bf3d Mon Sep 17 00:00:00 2001 From: Stefan Kuethe Date: Thu, 25 Jan 2024 10:02:48 +0100 Subject: [PATCH 10/11] Add default value --- maplibre/srcjs/ipywidget.js | 2 +- srcjs/mapmethods.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/maplibre/srcjs/ipywidget.js b/maplibre/srcjs/ipywidget.js index 46b5d102..f7dbaabf 100644 --- a/maplibre/srcjs/ipywidget.js +++ b/maplibre/srcjs/ipywidget.js @@ -1,7 +1,7 @@ import R from"https://esm.sh/maplibre-gl@3.6.2";var H=Object.prototype.toString,T=Array.isArray||function(e){return H.call(e)==="[object Array]"};function U(n){return typeof n=="function"}function N(n){return T(n)?"array":typeof n}function L(n){return n.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function _(n,e){return n!=null&&typeof n=="object"&&e in n}function $(n,e){return n!=null&&typeof n!="object"&&n.hasOwnProperty&&n.hasOwnProperty(e)}var B=RegExp.prototype.test;function D(n,e){return B.call(n,e)}var z=/\S/;function K(n){return!D(z,n)}var G={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function J(n){return String(n).replace(/[&<>"'`=\/]/g,function(t){return G[t]})}var Q=/\s*/,V=/\s+/,A=/\s*=/,X=/\s*\}/,Y=/#|\^|\/|>|\{|&|=|!/;function Z(n,e){if(!n)return[];var t=!1,r=[],s=[],a=[],o=!1,i=!1,c="",u=0;function p(){if(o&&!i)for(;a.length;)delete s[a.pop()];else a=[];o=!1,i=!1}var g,C,m;function O(y){if(typeof y=="string"&&(y=y.split(V,2)),!T(y)||y.length!==2)throw new Error("Invalid tags: "+y);g=new RegExp(L(y[0])+"\\s*"),C=new RegExp("\\s*"+L(y[1])),m=new RegExp("\\s*"+L("}"+y[1]))}O(e||v.tags);for(var l=new S(n),w,h,d,E,P,b;!l.eos();){if(w=l.pos,d=l.scanUntil(g),d)for(var x=0,q=d.length;x"?P=[h,d,w,l.pos,c,u,t]:P=[h,d,w,l.pos],u++,s.push(P),h==="#"||h==="^")r.push(P);else if(h==="/"){if(b=r.pop(),!b)throw new Error('Unopened section "'+d+'" at '+w);if(b[1]!==d)throw new Error('Unclosed section "'+b[1]+'" at '+w)}else h==="name"||h==="{"||h==="&"?i=!0:h==="="&&O(d)}if(p(),b=r.pop(),b)throw new Error('Unclosed section "'+b[1]+'" at '+l.pos);return te(ee(s))}function ee(n){for(var e=[],t,r,s=0,a=n.length;s0?r[r.length-1][4]:e;break;default:t.push(s)}return e}function S(n){this.string=n,this.tail=n,this.pos=0}S.prototype.eos=function(){return this.tail===""};S.prototype.scan=function(e){var t=this.tail.match(e);if(!t||t.index!==0)return"";var r=t[0];return this.tail=this.tail.substring(r.length),this.pos+=r.length,r};S.prototype.scanUntil=function(e){var t=this.tail.search(e),r;switch(t){case-1:r=this.tail,this.tail="";break;case 0:r="";break;default:r=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=r.length,r};function k(n,e){this.view=n,this.cache={".":this.view},this.parent=e}k.prototype.push=function(e){return new k(e,this)};k.prototype.lookup=function(e){var t=this.cache,r;if(t.hasOwnProperty(e))r=t[e];else{for(var s=this,a,o,i,c=!1;s;){if(e.indexOf(".")>0)for(a=s.view,o=e.split("."),i=0;a!=null&&i"?u=this.renderPartial(i,t,r,a):c==="&"?u=this.unescapedValue(i,t):c==="name"?u=this.escapedValue(i,t,a):c==="text"&&(u=this.rawValue(i)),u!==void 0&&(o+=u);return o};f.prototype.renderSection=function(e,t,r,s,a){var o=this,i="",c=t.lookup(e[1]);function u(C){return o.render(C,t,r,a)}if(c){if(T(c))for(var p=0,g=c.length;p0||!r)&&(a[o]=s+a[o]);return a.join(` -`)};f.prototype.renderPartial=function(e,t,r,s){if(r){var a=this.getConfigTags(s),o=U(r)?r(e[1]):r[e[1]];if(o!=null){var i=e[6],c=e[5],u=e[4],p=o;c==0&&u&&(p=this.indentPartial(o,u,i));var g=this.parse(p,a);return this.renderTokens(g,t,r,p,s)}}};f.prototype.unescapedValue=function(e,t){var r=t.lookup(e[1]);if(r!=null)return r};f.prototype.escapedValue=function(e,t,r){var s=this.getConfigEscape(r)||v.escape,a=t.lookup(e[1]);if(a!=null)return typeof a=="number"&&s===v.escape?String(a):s(a)};f.prototype.rawValue=function(e){return e[1]};f.prototype.getConfigTags=function(e){return T(e)?e:e&&typeof e=="object"?e.tags:void 0};f.prototype.getConfigEscape=function(e){if(e&&typeof e=="object"&&!T(e))return e.escape};var v={name:"mustache.js",version:"4.2.0",tags:["{{","}}"],clearCache:void 0,escape:void 0,parse:void 0,render:void 0,Scanner:void 0,Context:void 0,Writer:void 0,set templateCache(n){M.templateCache=n},get templateCache(){return M.templateCache}},M=new f;v.clearCache=function(){return M.clearCache()};v.parse=function(e,t){return M.parse(e,t)};v.render=function(e,t,r,s){if(typeof e!="string")throw new TypeError('Invalid template! Template should be a "string" but "'+N(e)+'" was given as the first argument for mustache#render(template, view, partials)');return M.render(e,t,r,s)};v.escape=J;v.Scanner=S;v.Context=k;v.Writer=f;var F=v;function j(n,e,t){return t!==null?F.render(t,n.properties):e===null?Object.keys(n.properties).map(s=>`${s}: ${n.properties[s]}`).join("
"):n.properties[e]}function I(n,e){let[t,r]=e;console.log(t,r),n[t](...r)}function W(n,e){return{addTooltip:function(t,r,s=null){let a={closeButton:!1,closeOnClick:!1},o=new n.Popup(a);e.on("mousemove",t,i=>{let c=i.features[0],u=j(c,r,s);o.setLngLat(i.lngLat).setHTML(u).addTo(e)}),e.on("mouseleave",t,()=>{o.remove()})},addControl:function(t,r,s){e.addControl(new n[t](r),s)},addPopup:function(t,r,s=null){let a={closeButton:!1},o=new n.Popup(a);e.on("click",t,i=>{let c=i.features[0],u=j(c,r,s);o.setLngLat(i.lngLat).setHTML(u).addTo(e)})},addMarker:function({lngLat:t,popup:r,options:s}){let a=new n.Marker(s).setLngLat(t);if(r){let o=new n.Popup(r.options).setHTML(r.text);a.setPopup(o)}a.addTo(e)},setSourceData:function(t,r){e.getSource(t).setData(r)}}}function ne(n){let e="pymaplibregl",t=document.createElement("div");return t.id=e,t.style.height=n.get("height"),t}function re(n,e){let t=new R.Map(n);return n.navigationControl===void 0&&(n.navigationControl=!0),n.navigationControl&&t.addControl(new R.NavigationControl),t.on("mouseover",()=>{t.getCanvas().style.cursor="pointer"}),t.on("mouseout",()=>{t.getCanvas().style.cursor=""}),t.on("click",r=>{e.set("lng_lat",r.lngLat),e.save_changes()}),t.once("load",()=>{t.resize()}),t}function pe({model:n,el:e}){console.log("maplibregl",R.version);let t=ne(n),r=Object.assign({container:t},n.get("map_options"));console.log(r);let s=re(r,n),a=W(R,s),o=c=>{c.forEach(u=>{if(Object.keys(a).includes(u[0])){console.log("internal call",u);let[p,g]=u;a[p](...g);return}I(s,u)})},i=n.get("calls");s.on("load",()=>{console.log("init calls",i),o(i),n.set("_rendered",!0),n.save_changes()}),n.on("msg:custom",c=>{console.log("custom msg",c),o(c.calls)}),e.appendChild(t)}export{pe as render}; +`)};f.prototype.renderPartial=function(e,t,r,s){if(r){var a=this.getConfigTags(s),o=U(r)?r(e[1]):r[e[1]];if(o!=null){var i=e[6],c=e[5],u=e[4],p=o;c==0&&u&&(p=this.indentPartial(o,u,i));var g=this.parse(p,a);return this.renderTokens(g,t,r,p,s)}}};f.prototype.unescapedValue=function(e,t){var r=t.lookup(e[1]);if(r!=null)return r};f.prototype.escapedValue=function(e,t,r){var s=this.getConfigEscape(r)||v.escape,a=t.lookup(e[1]);if(a!=null)return typeof a=="number"&&s===v.escape?String(a):s(a)};f.prototype.rawValue=function(e){return e[1]};f.prototype.getConfigTags=function(e){return T(e)?e:e&&typeof e=="object"?e.tags:void 0};f.prototype.getConfigEscape=function(e){if(e&&typeof e=="object"&&!T(e))return e.escape};var v={name:"mustache.js",version:"4.2.0",tags:["{{","}}"],clearCache:void 0,escape:void 0,parse:void 0,render:void 0,Scanner:void 0,Context:void 0,Writer:void 0,set templateCache(n){M.templateCache=n},get templateCache(){return M.templateCache}},M=new f;v.clearCache=function(){return M.clearCache()};v.parse=function(e,t){return M.parse(e,t)};v.render=function(e,t,r,s){if(typeof e!="string")throw new TypeError('Invalid template! Template should be a "string" but "'+N(e)+'" was given as the first argument for mustache#render(template, view, partials)');return M.render(e,t,r,s)};v.escape=J;v.Scanner=S;v.Context=k;v.Writer=f;var F=v;function j(n,e,t){return t!==null?F.render(t,n.properties):e===null?Object.keys(n.properties).map(s=>`${s}: ${n.properties[s]}`).join("
"):n.properties[e]}function I(n,e){let[t,r]=e;console.log(t,r),n[t](...r)}function W(n,e){return{addTooltip:function(t,r=null,s=null){let a={closeButton:!1,closeOnClick:!1},o=new n.Popup(a);e.on("mousemove",t,i=>{let c=i.features[0],u=j(c,r,s);o.setLngLat(i.lngLat).setHTML(u).addTo(e)}),e.on("mouseleave",t,()=>{o.remove()})},addControl:function(t,r,s){e.addControl(new n[t](r),s)},addPopup:function(t,r=null,s=null){let a={closeButton:!1},o=new n.Popup(a);e.on("click",t,i=>{let c=i.features[0],u=j(c,r,s);o.setLngLat(i.lngLat).setHTML(u).addTo(e)})},addMarker:function({lngLat:t,popup:r,options:s}){let a=new n.Marker(s).setLngLat(t);if(r){let o=new n.Popup(r.options).setHTML(r.text);a.setPopup(o)}a.addTo(e)},setSourceData:function(t,r){e.getSource(t).setData(r)}}}function ne(n){let e="pymaplibregl",t=document.createElement("div");return t.id=e,t.style.height=n.get("height"),t}function re(n,e){let t=new R.Map(n);return n.navigationControl===void 0&&(n.navigationControl=!0),n.navigationControl&&t.addControl(new R.NavigationControl),t.on("mouseover",()=>{t.getCanvas().style.cursor="pointer"}),t.on("mouseout",()=>{t.getCanvas().style.cursor=""}),t.on("click",r=>{e.set("lng_lat",r.lngLat),e.save_changes()}),t.once("load",()=>{t.resize()}),t}function pe({model:n,el:e}){console.log("maplibregl",R.version);let t=ne(n),r=Object.assign({container:t},n.get("map_options"));console.log(r);let s=re(r,n),a=W(R,s),o=c=>{c.forEach(u=>{if(Object.keys(a).includes(u[0])){console.log("internal call",u);let[p,g]=u;a[p](...g);return}I(s,u)})},i=n.get("calls");s.on("load",()=>{console.log("init calls",i),o(i),n.set("_rendered",!0),n.save_changes()}),n.on("msg:custom",c=>{console.log("custom msg",c),o(c.calls)}),e.appendChild(t)}export{pe as render}; /*! Bundled license information: mustache/mustache.mjs: diff --git a/srcjs/mapmethods.js b/srcjs/mapmethods.js index 4572f495..573a2b0a 100644 --- a/srcjs/mapmethods.js +++ b/srcjs/mapmethods.js @@ -10,7 +10,7 @@ function applyMapMethod(map, call) { // Custom map methods function getCustomMapMethods(maplibregl, map) { return { - addTooltip: function (layerId, property, template = null) { + addTooltip: function (layerId, property = null, template = null) { const popupOptions = { closeButton: false, closeOnClick: false, @@ -34,7 +34,7 @@ function getCustomMapMethods(maplibregl, map) { map.addControl(new maplibregl[type](options), position); }, - addPopup: function (layerId, property, template = null) { + addPopup: function (layerId, property = null, template = null) { const popupOptions = { closeButton: false, }; From dd691c9072f2b55c5d5ba42b378751d8eb070d5f Mon Sep 17 00:00:00 2001 From: Stefan Kuethe Date: Thu, 25 Jan 2024 10:05:03 +0100 Subject: [PATCH 11/11] Refactor example --- docs/examples/geopandas/app.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/examples/geopandas/app.py b/docs/examples/geopandas/app.py index 6ea348a3..bafebd69 100644 --- a/docs/examples/geopandas/app.py +++ b/docs/examples/geopandas/app.py @@ -29,9 +29,7 @@ def create_map(): m = Map(map_options) m.add_layer(wilderness_layer) - # m.add_tooltip(LAYER_ID, "NAME", template="NAME: {{ NAME }}") - # m.add_tooltip(LAYER_ID) - m.add_popup(LAYER_ID, template="Name: {{ NAME }}") + m.add_tooltip(LAYER_ID, "NAME") return m