Skip to content

Commit

Permalink
feat: add Leaflet.VectorGrid plugin (#1576)
Browse files Browse the repository at this point in the history
* feat: add Leaflet.VectorGrid plugin

* typos

* notebook styling

Co-authored-by: Frank <[email protected]>
  • Loading branch information
iwpnd and Conengmo authored May 5, 2022
1 parent 009fe83 commit 691511a
Show file tree
Hide file tree
Showing 4 changed files with 461 additions and 0 deletions.
220 changes: 220 additions & 0 deletions examples/plugin-vector-tiles.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 81,
"id": "09534296",
"metadata": {},
"outputs": [],
"source": [
"from folium.plugins import VectorGridProtobuf\n",
"import folium"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "c044c77f",
"metadata": {},
"outputs": [],
"source": [
"styles = {\n",
" \"water\": {\n",
" \"fill\": True,\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#06cccc\",\n",
" \"color\": \"#06cccc\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"admin\": {\n",
" \"weight\": 1,\n",
" \"fillColor\": \"pink\",\n",
" \"color\": \"pink\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"waterway\": {\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#2375e0\",\n",
" \"color\": \"#2375e0\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"landcover\": {\n",
" \"fill\": True,\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#53e033\",\n",
" \"color\": \"#53e033\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"landuse\": {\n",
" \"fill\": True,\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#e5b404\",\n",
" \"color\": \"#e5b404\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"park\": {\n",
" \"fill\": True,\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#84ea5b\",\n",
" \"color\": \"#84ea5b\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"boundary\": {\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#c545d3\",\n",
" \"color\": \"#c545d3\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"aeroway\": {\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#51aeb5\",\n",
" \"color\": \"#51aeb5\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"building\": {\n",
" \"fill\": True,\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#2b2b2b\",\n",
" \"color\": \"#2b2b2b\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"water_name\": {\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#022c5b\",\n",
" \"color\": \"#022c5b\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"transportation_name\": {\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#bc6b38\",\n",
" \"color\": \"#bc6b38\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"place\": {\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#f20e93\",\n",
" \"color\": \"#f20e93\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"housenumber\": {\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#ef4c8b\",\n",
" \"color\": \"#ef4c8b\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"poi\": {\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#3bb50a\",\n",
" \"color\": \"#3bb50a\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
" \"road\": {\n",
" \"weight\": 1,\n",
" \"fillColor\": \"#f2b648\",\n",
" \"color\": \"#f2b648\",\n",
" \"fillOpacity\": 0.2,\n",
" \"opacity\": 0.4\n",
" },\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 92,
"id": "725693fc",
"metadata": {},
"outputs": [],
"source": [
"vectorTileLayerStyles = {}\n",
"vectorTileLayerStyles[\"aerodrome_label\"] = []\n",
"vectorTileLayerStyles[\"aeroway\"] = []\n",
"vectorTileLayerStyles[\"area_name\"] = []\n",
"vectorTileLayerStyles[\"boundary\"] = styles[\"admin\"]\n",
"vectorTileLayerStyles[\"building\"] = []\n",
"vectorTileLayerStyles[\"building_ln\"] = []\n",
"vectorTileLayerStyles[\"construct\"] = []\n",
"vectorTileLayerStyles[\"contour_line\"] = []\n",
"vectorTileLayerStyles[\"landcover\"] = styles[\"landcover\"]\n",
"vectorTileLayerStyles[\"landuse\"] = styles[\"landuse\"]\n",
"vectorTileLayerStyles[\"mountain_peak\"] = []\n",
"vectorTileLayerStyles[\"park\"] = styles[\"park\"]\n",
"vectorTileLayerStyles[\"place\"] = []\n",
"vectorTileLayerStyles[\"poi\"] = []\n",
"vectorTileLayerStyles[\"spot_elevation\"] = []\n",
"vectorTileLayerStyles[\"transportation\"] = styles[\"road\"]\n",
"vectorTileLayerStyles[\"transportation_name\"] = []\n",
"vectorTileLayerStyles[\"water\"] = styles[\"water\"]\n",
"vectorTileLayerStyles[\"waterway\"] = styles[\"water\"]\n",
"vectorTileLayerStyles[\"water_name\"] = []"
]
},
{
"cell_type": "code",
"execution_count": 97,
"id": "49c9bf7a",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div style=\"width:100%;\"><div style=\"position:relative;width:100%;height:0;padding-bottom:60%;\"><span style=\"color:#565656\">Make this Notebook Trusted to load map: File -> Trust Notebook</span><iframe src=\"about:blank\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;\" data-html=%3C%21DOCTYPE%20html%3E%0A%3Chead%3E%20%20%20%20%0A%20%20%20%20%3Cmeta%20http-equiv%3D%22content-type%22%20content%3D%22text/html%3B%20charset%3DUTF-8%22%20/%3E%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%3Cscript%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20L_NO_TOUCH%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20L_DISABLE_3D%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%3C/script%3E%0A%20%20%20%20%0A%20%20%20%20%3Cstyle%3Ehtml%2C%20body%20%7Bwidth%3A%20100%25%3Bheight%3A%20100%25%3Bmargin%3A%200%3Bpadding%3A%200%3B%7D%3C/style%3E%0A%20%20%20%20%3Cstyle%3E%23map%20%7Bposition%3Aabsolute%3Btop%3A0%3Bbottom%3A0%3Bright%3A0%3Bleft%3A0%3B%7D%3C/style%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//cdn.jsdelivr.net/npm/leaflet%401.6.0/dist/leaflet.js%22%3E%3C/script%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//code.jquery.com/jquery-1.12.4.min.js%22%3E%3C/script%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js%22%3E%3C/script%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.js%22%3E%3C/script%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//cdn.jsdelivr.net/npm/leaflet%401.6.0/dist/leaflet.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//cdn.jsdelivr.net/gh/python-visualization/folium/folium/templates/leaflet.awesome.rotate.min.css%22/%3E%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cmeta%20name%3D%22viewport%22%20content%3D%22width%3Ddevice-width%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20initial-scale%3D1.0%2C%20maximum-scale%3D1.0%2C%20user-scalable%3Dno%22%20/%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cstyle%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23map_4b05f680d71e4c9bb2f663b45a87e500%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20relative%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20width%3A%20100.0%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20height%3A%20100.0%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20left%3A%200.0%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20top%3A%200.0%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C/style%3E%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//unpkg.com/leaflet.vectorgrid%40latest/dist/Leaflet.VectorGrid.bundled.js%22%3E%3C/script%3E%0A%3C/head%3E%0A%3Cbody%3E%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22folium-map%22%20id%3D%22map_4b05f680d71e4c9bb2f663b45a87e500%22%20%3E%3C/div%3E%0A%20%20%20%20%20%20%20%20%0A%3C/body%3E%0A%3Cscript%3E%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20map_4b05f680d71e4c9bb2f663b45a87e500%20%3D%20L.map%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22map_4b05f680d71e4c9bb2f663b45a87e500%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20center%3A%20%5B46.8%2C%208.2%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20crs%3A%20L.CRS.EPSG3857%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20zoom%3A%2014%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20zoomControl%3A%20true%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20preferCanvas%3A%20false%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%29%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20var%20vector_grid_protobuf_653d81da7d714651bbab3d81229a2551%20%3D%20L.vectorGrid.protobuf%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%27https%3A//vectortiles3.geo.admin.ch/tiles/ch.swisstopo.leichte-basiskarte.vt/v1.0.0/%7Bz%7D/%7Bx%7D/%7By%7D.pbf%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%22vectorTileLayerStyles%22%3A%20%7B%22aerodrome_label%22%3A%20%5B%5D%2C%20%22aeroway%22%3A%20%5B%5D%2C%20%22area_name%22%3A%20%5B%5D%2C%20%22boundary%22%3A%20%7B%22color%22%3A%20%22pink%22%2C%20%22fillColor%22%3A%20%22pink%22%2C%20%22fillOpacity%22%3A%200.2%2C%20%22opacity%22%3A%200.4%2C%20%22weight%22%3A%201%7D%2C%20%22building%22%3A%20%5B%5D%2C%20%22building_ln%22%3A%20%5B%5D%2C%20%22construct%22%3A%20%5B%5D%2C%20%22contour_line%22%3A%20%5B%5D%2C%20%22landcover%22%3A%20%7B%22color%22%3A%20%22%2353e033%22%2C%20%22fill%22%3A%20true%2C%20%22fillColor%22%3A%20%22%2353e033%22%2C%20%22fillOpacity%22%3A%200.2%2C%20%22opacity%22%3A%200.4%2C%20%22weight%22%3A%201%7D%2C%20%22landuse%22%3A%20%7B%22color%22%3A%20%22%23e5b404%22%2C%20%22fill%22%3A%20true%2C%20%22fillColor%22%3A%20%22%23e5b404%22%2C%20%22fillOpacity%22%3A%200.2%2C%20%22opacity%22%3A%200.4%2C%20%22weight%22%3A%201%7D%2C%20%22mountain_peak%22%3A%20%5B%5D%2C%20%22park%22%3A%20%7B%22color%22%3A%20%22%2384ea5b%22%2C%20%22fill%22%3A%20true%2C%20%22fillColor%22%3A%20%22%2384ea5b%22%2C%20%22fillOpacity%22%3A%200.2%2C%20%22opacity%22%3A%200.4%2C%20%22weight%22%3A%201%7D%2C%20%22place%22%3A%20%5B%5D%2C%20%22poi%22%3A%20%5B%5D%2C%20%22spot_elevation%22%3A%20%5B%5D%2C%20%22transportation%22%3A%20%7B%22color%22%3A%20%22%23f2b648%22%2C%20%22fillColor%22%3A%20%22%23f2b648%22%2C%20%22fillOpacity%22%3A%200.2%2C%20%22opacity%22%3A%200.4%2C%20%22weight%22%3A%201%7D%2C%20%22transportation_name%22%3A%20%5B%5D%2C%20%22water%22%3A%20%7B%22color%22%3A%20%22%2306cccc%22%2C%20%22fill%22%3A%20true%2C%20%22fillColor%22%3A%20%22%2306cccc%22%2C%20%22fillOpacity%22%3A%200.2%2C%20%22opacity%22%3A%200.4%2C%20%22weight%22%3A%201%7D%2C%20%22water_name%22%3A%20%5B%5D%2C%20%22waterway%22%3A%20%7B%22color%22%3A%20%22%2306cccc%22%2C%20%22fill%22%3A%20true%2C%20%22fillColor%22%3A%20%22%2306cccc%22%2C%20%22fillOpacity%22%3A%200.2%2C%20%22opacity%22%3A%200.4%2C%20%22weight%22%3A%201%7D%7D%7D%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.addTo%28map_4b05f680d71e4c9bb2f663b45a87e500%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%3C/script%3E onload=\"this.contentDocument.open();this.contentDocument.write( decodeURIComponent(this.getAttribute('data-html')));this.contentDocument.close();\" allowfullscreen webkitallowfullscreen mozallowfullscreen></iframe></div></div>"
],
"text/plain": [
"<folium.folium.Map at 0x123d388e0>"
]
},
"execution_count": 97,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"url = \"https://vectortiles3.geo.admin.ch/tiles/ch.swisstopo.leichte-basiskarte.vt/v1.0.0/{z}/{x}/{y}.pbf\"\n",
"m = folium.Map(tiles=None, location=[46.8, 8.2], zoom_start=14)\n",
"\n",
"options = {\n",
" \"vectorTileLayerStyles\": vectorTileLayerStyles\n",
"}\n",
"\n",
"vc = VectorGridProtobuf(url, \"folium_layer_name\", options)\n",
"m.add_child(vc)\n",
"m"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
2 changes: 2 additions & 0 deletions folium/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from folium.plugins.time_slider_choropleth import TimeSliderChoropleth
from folium.plugins.timestamped_geo_json import TimestampedGeoJson
from folium.plugins.timestamped_wmstilelayer import TimestampedWmsTileLayers
from folium.plugins.vectorgrid_protobuf import VectorGridProtobuf

__all__ = [
'AntPath',
Expand Down Expand Up @@ -63,4 +64,5 @@
'TimeSliderChoropleth',
'TimestampedGeoJson',
'TimestampedWmsTileLayers',
'VectorGridProtobuf'
]
129 changes: 129 additions & 0 deletions folium/plugins/vectorgrid_protobuf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@

from folium.elements import JSCSSMixin
from folium.map import Layer
from jinja2 import Template


class VectorGridProtobuf(JSCSSMixin, Layer):
"""
Add vector tile layers based on https:/Leaflet/Leaflet.VectorGrid.
Parameters
----------
url: url to tile provider
e.g. https://free-{s}.tilehosting.com/data/v3/{z}/{x}/{y}.pbf?token={token}
layer_name: string, default "VectorGridLayer"
name of the layer
options: dict or str, VectorGrid.protobuf options
For convenience you can pass VectorGrid.protobuf options as python dictionary or string.
Strings allow plain JavaScript to be passed, therefore allow for conditional styling (see examples).
Additionally the url might contain any string literals like {token}, or {key}
that can be passed as attribute to the options dict and will be substituted.
Every layer inside the tile layer has to be styled separately.
Examples
--------
Options as dict:
>>> m = folium.Map()
>>> url = "https://free-{s}.tilehosting.com/data/v3/{z}/{x}/{y}.pbf?token={token}"
>>> options = {
... "subdomain": "tilehosting",
... "token": "af6P2G9dztAt1F75x7KYt0Hx2DJR052G",
... "vectorTileLayerStyles": {
... "layer_name_one": {
... "fill": True,
... "weight": 1,
... "fillColor": 'green',
... "color": 'black',
... "fillOpacity":0.6,
... "opacity":0.6
... },
... "layer_name_two": {
... "fill": True,
... "weight": 1,
... "fillColor": 'red',
... "color": 'black',
... "fillOpacity":0.6,
... "opacity":0.6
... }
... }
... }
>>> VectorGridProtobuf(url,"layer_name",options).add_to(m)
Options as string allows to pass functions
>>> m = folium.Map()
>>> url = "https://free-{s}.tilehosting.com/data/v3/{z}/{x}/{y}.pbf?token={token}"
>>> options = '''{
... "subdomain": "tilehosting",
... "token": "af6P2G9dztAt1F75x7KYt0Hx2DJR052G",
... "vectorTileLayerStyles": {
... all: function(f) {
... if (f.type === 'parks') {
... return {
... "fill": true,
... "weight": 1,
... "fillColor": 'green',
... "color": 'black',
... "fillOpacity":0.6,
... "opacity":0.6
... };
... }
... if (f.type === 'water') {
... return {
... "fill": true,
... "weight": 1,
... "fillColor": 'purple',
... "color": 'black',
... "fillOpacity":0.6,
... "opacity":0.6
... };
... }
... }
... }
}'''
>>> VectorGridProtobuf(url,"layer_name",options).add_to(m)
For more info, see: https://leaflet.github.io/Leaflet.VectorGrid/vectorgrid-api-docs.html#styling-vectorgrids.
"""

_template = Template(
"""
{% macro script(this, kwargs) -%}
var {{ this.get_name() }} = L.vectorGrid.protobuf(
'{{ this.url }}',
{% if this.options is defined %}
{{ this.options if this.options is string else this.options|tojson }})
.addTo({{ this._parent.get_name() }});
{% else %}
{{ this.options }}).addTo({{ this._parent.get_name() }});
{% endif %}
{%- endmacro %}
"""
) # noqa

default_js = [
(
"vectorGrid",
"https://unpkg.com/leaflet.vectorgrid@latest/dist/Leaflet.VectorGrid.bundled.js",
)
]

def __init__(self, url, layer_name, options=None):
self.layer_name = layer_name if layer_name else "VectorGridProtobufLayer"

super(VectorGridProtobuf, self).__init__(name=self.layer_name)

self.url = url
self._name = "VectorGridProtobuf"

if options is not None:
self.options = options
Loading

0 comments on commit 691511a

Please sign in to comment.