Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature/deckgl layers #51

Merged
merged 37 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
5a5bb8e
Add deckgl dep
May 28, 2024
14472b7
Add method to add a deckgl layer to the map
May 29, 2024
b4ce9da
Add GridLayer example
May 29, 2024
5a508f6
Add deckgl headers to html template
May 29, 2024
1683e86
Add deckgl example
May 29, 2024
c44086f
Add multiple deck layers at once
May 29, 2024
673a663
Add another example
May 29, 2024
6539f81
Export render as default
May 29, 2024
3fa603e
Add support for deckgl layers in Jupyter
May 30, 2024
ff578b2
Add POC for deck tooltips
May 30, 2024
48d2297
Send picking object to Shiny
May 30, 2024
ce93d5e
Support tooltip for a single layer
May 30, 2024
9b0c9e0
Refactor
May 30, 2024
e246816
Support deck tooltips for multiple layers
May 30, 2024
9c6c217
Update example
May 30, 2024
133d33c
Add func to use maplibre tooltips with deck layers
May 30, 2024
2a4fab1
Add tooltip support for jupyter deck layers
May 30, 2024
0b32118
Change tooltip style
May 31, 2024
28d8fcb
Make maplibre tooltip work
May 31, 2024
d27435b
Add method to update deckgl layers
May 31, 2024
50c0d4d
Add example mixing layers
May 31, 2024
2c69bb6
Add func to ipywidget to update deck layers
May 31, 2024
94188eb
Refactor
May 31, 2024
6fb229e
Update doc strings
May 31, 2024
712a90f
Move docker workflows to obsolete
May 31, 2024
fc97c21
Update deck.gl docs
May 31, 2024
ebaf9c8
Rename 'tooltip_template' to tooltip in py
Jun 1, 2024
fde1289
Use MapLibre Popup as deck tooltio
Jun 1, 2024
6ed4286
Refactor
Jun 1, 2024
9d3e1ea
Refactor default tooltip
Jun 1, 2024
81cc292
Rebuild
Jun 1, 2024
ef75851
Update README
Jun 3, 2024
dc952c9
Update deck tooltip as well
Jun 4, 2024
bea28d2
Update tooltip ipywidget
Jun 4, 2024
f17cf35
Add more deckgl examples
Jun 4, 2024
ed1f63f
Increase versions
Jun 4, 2024
b3ecab8
Update docs
Jun 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ pip install git+https://github.com/eodaGmbH/py-maplibregl@dev
pip install "maplibre[all] @ git+https://github.com/eodaGmbH/py-maplibregl@dev"
```

## Getting started
## Quickstart

```bash
docker run --rm -p 8050:8050 ghcr.io/eodagmbh/py-maplibregl/vancouver-blocks:latest
```python
from maplibre import Map, MapOptions

m = Map(MapOptions(center=(-123.1256, 49.24658), zoom=9))
```

## Documentation

* [Basic usage](https://eodagmbh.github.io/py-maplibregl/)
* [API Documentation](https://eodagmbh.github.io/py-maplibregl/api/map/)
* [Examples](https://eodagmbh.github.io/py-maplibregl/examples/every_person_in_manhattan/)
Expand Down
44 changes: 44 additions & 0 deletions docs/deckgl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Deck.GL layers can be added to the map with `Map.add_deck_layers`.

They are defined as a dictionary, where classes got the `@@type` prefix and getter props
the `@@=` prefix. They are inserted into the layer stack of the maplibre context. Therefore,
you can also pass a `beforeId` prop.

Here is an example corresponding to the [Deck.GL GridLayer API Example](https://deck.gl/docs/api-reference/aggregation-layers/grid-layer):

```python
grid_layer = {
"@@type": "GridLayer", # JS: new GridLayer
"id": "my-awsome-grid-layer",
"data": "https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json",
"extruded": True,
"getPosition": "@@=COORDINATES", # JS: d => d.COORDINATES
"getColorWeight": "@@=SPACES", # JS: d => d.SPACES
"getElevationWeight": "@@=SPACES", # JS: d => d.SPACES
"elevationScale": 4,
"cellSize": 200,
"pickable": True,
"beforeId": "first-labels-layer" # optional
}
```

The Code above will generate the following JavaScript code:

```javascript
const gridLayer = new GridLayer({
id: 'GridLayer',
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json',
extruded: true,
getPosition: d => d.COORDINATES,
getColorWeight: d => d.SPACES,
getElevationWeight: d => d.SPACES,
elevationScale: 4,
cellSize: 200,
pickable: true
});
```

See also:

- [Deck.GL Layer](../examples/deckgl_layer)
- [Deck.GL Multiple Layers](../examples/deckgl_multiple_layers)
33 changes: 33 additions & 0 deletions docs/examples/deckgl_layer/app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>My Awesome Map</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css"/>
<script src="https://unpkg.com/[email protected]/dist.min.js"></script>
<script src="https://unpkg.com/@deck.gl/[email protected]/dist.min.js"></script>
<body>
<div id="pymaplibregl" style="height:600px;"></div>
<script>
(()=>{var $=Object.prototype.toString,_=Array.isArray||function(e){return $.call(e)==="[object Array]"};function D(r){return typeof r=="function"}function B(r){return _(r)?"array":typeof r}function E(r){return r.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function U(r,e){return r!=null&&typeof r=="object"&&e in r}function A(r,e){return r!=null&&typeof r!="object"&&r.hasOwnProperty&&r.hasOwnProperty(e)}var J=RegExp.prototype.test;function F(r,e){return J.call(r,e)}var W=/\S/;function q(r){return!F(W,r)}var G={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};function K(r){return String(r).replace(/[&<>"'`=\/]/g,function(t){return G[t]})}var V=/\s*/,z=/\s+/,j=/\s*=/,Q=/\s*\}/,X=/#|\^|\/|>|\{|&|=|!/;function Y(r,e){if(!r)return[];var t=!1,n=[],s=[],a=[],o=!1,i=!1,c="",l=0;function f(){if(o&&!i)for(;a.length;)delete s[a.pop()];else a=[];o=!1,i=!1}var v,m,x;function N(y){if(typeof y=="string"&&(y=y.split(z,2)),!_(y)||y.length!==2)throw new Error("Invalid tags: "+y);v=new RegExp(E(y[0])+"\\s*"),m=new RegExp("\\s*"+E(y[1])),x=new RegExp("\\s*"+E("}"+y[1]))}N(e||g.tags);for(var u=new L(r),w,d,h,S,T,b;!u.eos();){if(w=u.pos,h=u.scanUntil(v),h)for(var P=0,I=h.length;P<I;++P)S=h.charAt(P),q(S)?(a.push(s.length),c+=S):(i=!0,t=!0,c+=" "),s.push(["text",S,w,w+1]),w+=1,S===`
`&&(f(),c="",l=0,t=!1);if(!u.scan(v))break;if(o=!0,d=u.scan(X)||"name",u.scan(V),d==="="?(h=u.scanUntil(j),u.scan(j),u.scanUntil(m)):d==="{"?(h=u.scanUntil(x),u.scan(Q),u.scanUntil(m),d="&"):h=u.scanUntil(m),!u.scan(m))throw new Error("Unclosed tag at "+u.pos);if(d==">"?T=[d,h,w,u.pos,c,l,t]:T=[d,h,w,u.pos],l++,s.push(T),d==="#"||d==="^")n.push(T);else if(d==="/"){if(b=n.pop(),!b)throw new Error('Unopened section "'+h+'" at '+w);if(b[1]!==h)throw new Error('Unclosed section "'+b[1]+'" at '+w)}else d==="name"||d==="{"||d==="&"?i=!0:d==="="&&N(h)}if(f(),b=n.pop(),b)throw new Error('Unclosed section "'+b[1]+'" at '+u.pos);return ee(Z(s))}function Z(r){for(var e=[],t,n,s=0,a=r.length;s<a;++s)t=r[s],t&&(t[0]==="text"&&n&&n[0]==="text"?(n[1]+=t[1],n[3]=t[3]):(e.push(t),n=t));return e}function ee(r){for(var e=[],t=e,n=[],s,a,o=0,i=r.length;o<i;++o)switch(s=r[o],s[0]){case"#":case"^":t.push(s),n.push(s),t=s[4]=[];break;case"/":a=n.pop(),a[5]=s[2],t=n.length>0?n[n.length-1][4]:e;break;default:t.push(s)}return e}function L(r){this.string=r,this.tail=r,this.pos=0}L.prototype.eos=function(){return this.tail===""};L.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};L.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 k(r,e){this.view=r,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,n;if(t.hasOwnProperty(e))n=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<o.length;)i===o.length-1&&(c=U(a,o[i])||A(a,o[i])),a=a[o[i++]];else a=s.view[e],c=U(s.view,e);if(c){n=a;break}s=s.parent}t[e]=n}return D(n)&&(n=n.call(this.view)),n};function p(){this.templateCache={_cache:{},set:function(e,t){this._cache[e]=t},get:function(e){return this._cache[e]},clear:function(){this._cache={}}}}p.prototype.clearCache=function(){typeof this.templateCache<"u"&&this.templateCache.clear()};p.prototype.parse=function(e,t){var n=this.templateCache,s=e+":"+(t||g.tags).join(":"),a=typeof n<"u",o=a?n.get(s):void 0;return o==null&&(o=Y(e,t),a&&n.set(s,o)),o};p.prototype.render=function(e,t,n,s){var a=this.getConfigTags(s),o=this.parse(e,a),i=t instanceof k?t:new k(t,void 0);return this.renderTokens(o,i,n,e,s)};p.prototype.renderTokens=function(e,t,n,s,a){for(var o="",i,c,l,f=0,v=e.length;f<v;++f)l=void 0,i=e[f],c=i[0],c==="#"?l=this.renderSection(i,t,n,s,a):c==="^"?l=this.renderInverted(i,t,n,s,a):c===">"?l=this.renderPartial(i,t,n,a):c==="&"?l=this.unescapedValue(i,t):c==="name"?l=this.escapedValue(i,t,a):c==="text"&&(l=this.rawValue(i)),l!==void 0&&(o+=l);return o};p.prototype.renderSection=function(e,t,n,s,a){var o=this,i="",c=t.lookup(e[1]);function l(m){return o.render(m,t,n,a)}if(c){if(_(c))for(var f=0,v=c.length;f<v;++f)i+=this.renderTokens(e[4],t.push(c[f]),n,s,a);else if(typeof c=="object"||typeof c=="string"||typeof c=="number")i+=this.renderTokens(e[4],t.push(c),n,s,a);else if(D(c)){if(typeof s!="string")throw new Error("Cannot use higher-order sections without the original template");c=c.call(t.view,s.slice(e[3],e[5]),l),c!=null&&(i+=c)}else i+=this.renderTokens(e[4],t,n,s,a);return i}};p.prototype.renderInverted=function(e,t,n,s,a){var o=t.lookup(e[1]);if(!o||_(o)&&o.length===0)return this.renderTokens(e[4],t,n,s,a)};p.prototype.indentPartial=function(e,t,n){for(var s=t.replace(/[^ \t]/g,""),a=e.split(`
`),o=0;o<a.length;o++)a[o].length&&(o>0||!n)&&(a[o]=s+a[o]);return a.join(`
`)};p.prototype.renderPartial=function(e,t,n,s){if(n){var a=this.getConfigTags(s),o=D(n)?n(e[1]):n[e[1]];if(o!=null){var i=e[6],c=e[5],l=e[4],f=o;c==0&&l&&(f=this.indentPartial(o,l,i));var v=this.parse(f,a);return this.renderTokens(v,t,n,f,s)}}};p.prototype.unescapedValue=function(e,t){var n=t.lookup(e[1]);if(n!=null)return n};p.prototype.escapedValue=function(e,t,n){var s=this.getConfigEscape(n)||g.escape,a=t.lookup(e[1]);if(a!=null)return typeof a=="number"&&s===g.escape?String(a):s(a)};p.prototype.rawValue=function(e){return e[1]};p.prototype.getConfigTags=function(e){return _(e)?e:e&&typeof e=="object"?e.tags:void 0};p.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){O.templateCache=r},get templateCache(){return O.templateCache}},O=new p;g.clearCache=function(){return O.clearCache()};g.parse=function(e,t){return O.parse(e,t)};g.render=function(e,t,n,s){if(typeof e!="string")throw new TypeError('Invalid template! Template should be a "string" but "'+B(e)+'" was given as the first argument for mustache#render(template, view, partials)');return O.render(e,t,n,s)};g.escape=K;g.Scanner=L;g.Context=k;g.Writer=p;var M=g;function R(r,e,t){return t!==null?M.render(t,r.properties):e===null?Object.keys(r.properties).map(s=>`${s}: ${r.properties[s]}`).join("</br>"):r.properties[e]}function te(r,e,t){let n={background:"white",color:"black","border-radius":"5px"};return typeof r=="object"?r[t]&&{html:M.render(r[t],e),style:n}:{html:M.render(r,e),style:n}}function H(r){return({layer:e,object:t})=>t&&te(r,t,e.id)}function ne(){if(typeof deck>"u")return;let r=new deck.JSONConfiguration({classes:deck});return new deck.JSONConverter({configuration:r})}var C=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),this._JSONConverter=ne()}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 s=new maplibregl.Marker(n).setLngLat(e);if(t){let a=new maplibregl.Popup(t.options).setHTML(t.text);s.setPopup(a)}s.addTo(this._map)}addLayer(e,t){this._map.addLayer(e,t),typeof Shiny<"u"&&this._map.on("click",e.id,n=>{console.log(n,n.features[0]);let s=e.id.replaceAll("-","_"),a=`${this._id}_layer_${s}`,o={props:n.features[0].properties,layer_id:e.id};console.log(a,o),Shiny.onInputChange(a,o)})}addPopup(e,t=null,n=null){let s={closeButton:!1},a=new maplibregl.Popup(s);this._map.on("click",e,o=>{let i=o.features[0],c=R(i,t,n);a.setLngLat(o.lngLat).setHTML(c).addTo(this._map)})}addTooltip(e,t=null,n=null){let s={closeButton:!1,closeOnClick:!1},a=new maplibregl.Popup(s);this._map.on("mousemove",e,o=>{let i=o.features[0],c=R(i,t,n);a.setLngLat(o.lngLat).setHTML(c).addTo(this._map)}),this._map.on("mouseleave",e,()=>{a.remove()})}setSourceData(e,t){this._map.getSource(e).setData(t)}addDeckOverlay(e,t=null){if(typeof this._JSONConverter>"u"){console.log("deck or JSONConverter is undefined");return}let n=this._convertDeckLayers(e,t);this._deckOverlay=new deck.MapboxOverlay({interleaved:!0,layers:n,getTooltip:t?H(t):null}),this._map.addControl(this._deckOverlay)}_convertDeckLayers(e,t=null){return e.map(n=>this._JSONConverter.convert(Object.assign(n,{onHover:({layer:s,coordinate:a,object:o})=>{if(typeof Shiny<"u"){let i=`${this._id}_layer_${n.id}`;Shiny.onInputChange(i,o)}}})))}setDeckLayers(e){console.log("Updating Deck.GL layers");let t=this._convertDeckLayers(e);this._deckOverlay.setProps({layers:t})}render(e){e.forEach(([t,n])=>{if(["addLayer","addPopup","addTooltip","addMarker","addPopup","addControl","setSourceData","addDeckOverlay","setDeckLayers"].includes(t)){console.log("Custom method",t,n),this[t](...n);return}console.log("Map method",t),this.applyMapMethod(t,n)})}};var re="0.1.0";console.log("pymaplibregl",re);typeof Shiny>"u"&&(window.pymaplibregl=function({mapOptions:r,calls:e}){let t="pymaplibregl",n=document.getElementById(t),s=new C(Object.assign({container:n.id},r));s.getMap().on("load",()=>{s.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 s=window._maplibreWidget=new C(Object.assign({container:t.id},n.mapData.mapOptions)),a=s.getMap();a.on("load",()=>{s.render(n.mapData.calls)}),a.on("click",i=>{console.log(i);let c=`${t.id}`,l={coords:i.lngLat,point:i.point};console.log(c,l),Shiny.onInputChange(c,l)});let o=`pymaplibregl-${t.id}`;console.log(o),Shiny.addCustomMessageHandler(o,({id:i,calls:c})=>{console.log(i,c),s.render(c)})}}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
*)
*/

// ...
(() => {
var data = {"mapOptions": {"center": [-122.4, 37.74], "hash": true, "pitch": 40, "style": "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json", "zoom": 12}, "calls": [["addDeckOverlay", [[{"@@type": "GridLayer", "id": "MyAwesomeGridLayer", "data": "https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json", "extruded": true, "getPosition": "@@=COORDINATES", "getColorWeight": "@@=SPACES", "getElevationWeight": "@@=SPACES", "elevationScale": 4, "cellSize": 200, "pickable": true}], "Number of points: {{ count }}"]]]};
pymaplibregl(data);
})();
</script>
</body>
</html>
56 changes: 56 additions & 0 deletions docs/examples/deckgl_layer/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Shiny Express App

import json

from maplibre import Map, MapOptions, render_maplibregl
from maplibre.basemaps import Carto
from maplibre.ui import use_deckgl

# from shiny import reactive
from shiny.express import input, render, ui

m = Map(
MapOptions(
style=Carto.POSITRON,
center=(-122.4, 37.74),
zoom=12,
hash=True,
pitch=40,
)
)


deck_grid_layer = {
"@@type": "GridLayer",
"id": "GridLayer",
"data": "https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json",
"extruded": True,
"getPosition": "@@=COORDINATES",
"getColorWeight": "@@=SPACES",
"getElevationWeight": "@@=SPACES",
"elevationScale": 4,
"cellSize": 200,
"pickable": True,
}

m.add_deck_layers([deck_grid_layer], tooltip="Number of points: {{ count }}")

# Shiny Express
use_deckgl()


@render_maplibregl
def render_map():
return m


@render.code
def picking_object():
obj = input.render_map_layer_GridLayer()
print(obj)
return json.dumps(obj["points"], indent=2) if obj else "Pick a feature!"


if __name__ == "__main__":
with open("docs/examples/deckgl_layer/app.html", "w") as f:
f.write(m.to_html())
78 changes: 78 additions & 0 deletions docs/examples/deckgl_layer/app_update_layer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Shiny Express App

import json

from maplibre import Map, MapContext, MapOptions, render_maplibregl
from maplibre.basemaps import Carto
from maplibre.ui import use_deckgl
from shiny import reactive
from shiny.express import input, render, ui

m = Map(
MapOptions(
style=Carto.POSITRON,
center=(-122.4, 37.74),
zoom=12,
hash=True,
pitch=40,
)
)

layer_id = "GridLayer"

CELL_SIZES = [100, 200, 300]
DEFAULT_CELL_SIZE = CELL_SIZES[1]


def deck_grid_layer(cell_size: int = DEFAULT_CELL_SIZE):
return {
"@@type": layer_id,
"id": "GridLayer",
"data": "https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json",
"extruded": True,
"getPosition": "@@=COORDINATES",
"getColorWeight": "@@=SPACES",
"getElevationWeight": "@@=SPACES",
"elevationScale": 4,
"cellSize": cell_size,
"pickable": True,
}


tooltip = "Number of points: {{ count }}"
m.add_deck_layers([deck_grid_layer()], tooltip=tooltip)

# Shiny Express
use_deckgl()

ui.input_select(
"cell_size", "Cell size", choices=CELL_SIZES, selected=DEFAULT_CELL_SIZE
)
ui.input_action_button("update_layer", "Update layer")


@render_maplibregl
def render_map():
return m


@render.code
def picking_object():
obj = input.render_map_layer_GridLayer()
print(obj)
# return json.dumps(obj, indent=2) if obj else "Pick a feature!"
# return f"{obj['count']}" if obj else "Pick a feature!"
return json.dumps(obj["points"], indent=2) if obj else "Pick a feature!"


@reactive.Effect
@reactive.event(input.update_layer)
async def update_layer():
print(input.update_layer())
async with MapContext("render_map") as m:
m.set_deck_layers([deck_grid_layer(input.cell_size())], tooltip)


if __name__ == "__main__":
with open("docs/examples/deckgl_layer/app.html", "w") as f:
f.write(m.to_html())
58 changes: 58 additions & 0 deletions docs/examples/deckgl_layer/column_layer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Shiny Express App

import json

from maplibre import Map, MapOptions, render_maplibregl
from maplibre.basemaps import Carto
from maplibre.ui import use_deckgl

# from shiny import reactive
from shiny.express import input, render, ui

m = Map(
MapOptions(
style=Carto.POSITRON,
center=(-122.4, 37.74),
zoom=11,
hash=True,
pitch=40,
)
)


deck_column_layer = {
"@@type": "ColumnLayer",
"id": "ColumnLayer",
"data": "https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/hexagons.json",
"diskResolution": 12,
"extruded": True,
"radius": 250,
"elevationScale": 5000,
"getElevation": "@@=value",
"getFillColor": "@@=[48, 128, value * 255, 255]",
"getPosition": "@@=centroid",
"pickable": True,
}
tooltip = "Centroid: {{ centroid }}\nValue: {{ value }}"
m.add_deck_layers([deck_column_layer], tooltip=tooltip)

# Shiny Express
use_deckgl()


@render_maplibregl
def render_map():
return m


@render.code
def picking_object():
obj = input.render_map_layer_ColumnLayer()
print(obj)
return obj.keys()
# return json.dumps(obj["points"], indent=2) if obj else "Pick a feature!"


if __name__ == "__main__":
with open("docs/examples/deckgl_layer/column_layer.html", "w") as f:
f.write(m.to_html())
14 changes: 14 additions & 0 deletions docs/examples/deckgl_layer/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!-- <a href="app.html" target="_blank">See example in action</a> -->

<iframe src="app.html" height="620px", width="100%" style="border:none;"></iframe>

```python
-8<-- "deckgl_layer/app.py"
```

Run example:

``` bash
# Run Shiny app
shiny run docs/examples/deckgl_layer/app.py
```
Loading
Loading