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

Add support for PMTiles #1138

Merged
merged 10 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
94 changes: 94 additions & 0 deletions examples/PMTiles.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Set up for JupyterLite\n",
"try:\n",
" import piplite\n",
" await piplite.install('ipyleaflet')\n",
"except ImportError:\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ipyleaflet import Map, basemaps, PMTilesLayer"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m = Map(center=[52.963529, 4.776306], zoom=7, basemap=basemaps.CartoDB.DarkMatter, scroll_wheel_zoom=True)\n",
"m.layout.height = '600px'\n",
"\n",
"vl = PMTilesLayer(url=\"https://storage.googleapis.com/ahp-research/overture/pmtiles/overture.pmtiles\", \n",
" style = {\n",
" \"layers\": [\n",
" {\n",
" \"id\": \"admins\",\n",
" \"source\": \"example_source\",\n",
" \"source-layer\": \"admins\",\n",
" \"type\": \"fill\",\n",
" \"paint\": {\"fill-color\": \"#BDD3C7\", \"fill-opacity\": 0.1},\n",
" },\n",
" {\n",
" \"id\": \"buildings\",\n",
" \"source\": \"example_source\",\n",
" \"source-layer\": \"buildings\",\n",
" \"type\": \"fill\",\n",
" \"paint\": {\"fill-color\": \"#FFFFB3\", \"fill-opacity\": 0.5},\n",
" },\n",
" {\n",
" \"id\": \"places\",\n",
" \"source\": \"example_source\",\n",
" \"source-layer\": \"places\",\n",
" \"type\": \"fill\",\n",
" \"paint\": {\"fill-color\": \"#BEBADA\", \"fill-opacity\": 0.5},\n",
" },\n",
" {\n",
" \"id\": \"roads\",\n",
" \"source\": \"example_source\",\n",
" \"source-layer\": \"roads\",\n",
" \"type\": \"line\",\n",
" \"paint\": {\"line-color\": \"#FB8072\"},\n",
" },\n",
" ],\n",
" })\n",
"m.add(vl)\n",
"m"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.10.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
29 changes: 29 additions & 0 deletions ipyleaflet/leaflet.py
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,35 @@ def redraw(self):
self.send({'msg': 'redraw'})


class PMTilesLayer(Layer):
"""PMTilesLayer class, with Layer as parent class.

PMTiles layer.


Attributes
----------
url: string, default ""
Url to the PMTiles archive.
attribution: string, default ""
PMTiles archive attribution.
style: dict, default {}
CSS Styles to apply to the vector data.
"""

_view_name = Unicode('LeafletPMTilesLayerView').tag(sync=True)
_model_name = Unicode('LeafletPMTilesLayerModel').tag(sync=True)

url = Unicode().tag(sync=True, o=True)
attribution = Unicode().tag(sync=True, o=True)
style = Dict().tag(sync=True, o=True)

def add_inspector(self):
"""Add an inspector to the layer.
"""
self.send({'msg': 'add_inspector'})


class VectorLayer(Layer):
"""VectorLayer abstract class."""

Expand Down
3 changes: 2 additions & 1 deletion js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
"proj4": "^2.6.0",
"proj4leaflet": "^1.0.1",
"spin.js": "^4.1.0",
"stream-browserify": "^3.0.0"
"stream-browserify": "^3.0.0",
"protomaps-leaflet": "^1.24.0"
},
"devDependencies": {
"@jupyterlab/builder": "^3.6.0",
Expand Down
1 change: 1 addition & 0 deletions js/src/jupyter-leaflet.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './layers/AwesomeIcon.js';
export * from './layers/Popup.js';
export * from './layers/RasterLayer.js';
export * from './layers/TileLayer.js';
export * from './layers/PMTilesLayer.js';
export * from './layers/VectorTileLayer.js';
export * from './layers/LocalTileLayer.js';
export * from './layers/WMSLayer.js';
Expand Down
48 changes: 48 additions & 0 deletions js/src/layers/PMTilesLayer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

const layer = require('./Layer.js');
const protomapsL = require('protomaps-leaflet');

export class LeafletPMTilesLayerModel extends layer.LeafletLayerModel {
defaults() {
return {
...super.defaults(),
_view_name: 'LeafletPMTilesLayerView',
_model_name: 'LeafletPMTilesLayerModel',
url: '',
attribution: '',
style: {},
};
}
}

export class LeafletPMTilesLayerView extends layer.LeafletLayerView {
create_obj() {
var options = {
...this.get_options(),
url: this.model.get('url'),
...protomapsL.json_style(this.model.get('style')),
};
this.obj = protomapsL.leafletLayer(options);
}

model_events() {
super.model_events();
this.listenTo(
this.model,
'change:url',
function () {
this.obj.setUrl(this.model.get('url'));
},
this
);
}

handle_message(content) {
if (content.msg == 'add_inspector') {
this.obj.addInspector(this.map_view.obj);
}
}
}

1 change: 1 addition & 0 deletions js/src/leaflet.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require('leaflet-fullscreen');
require('leaflet-transform');
require('leaflet.awesome-markers');
require('leaflet-search');
require('protomaps-leaflet');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for general purpose vector tile display, maplibre is better than protomaps-leaflet, is it possible to use that instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used maplibre in the initial commit, but it failed. I don't how to fix it, so I changed to back to protomaps-leaflet. See
92586cd#diff-ab455b4e55f1402a4baec5dd7dd302a20e715e7b3897e82cc2e4b6ac557baba8

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For references on how styling vector tiles works with ipyleaflet. https://ipyleaflet.readthedocs.io/en/latest/layers/vector_tile.html

Copy link

@jtmiclat jtmiclat Sep 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i ended up using protomaps-leaflet's json_style function to convert mapbox style specs to protomaps style

jtmiclat@c101c87

I found trying to use maplibre is super hacky with overlays and for the case of ipyleaflet adds a large js dependency on build time for an optional feature.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the json_style only parses a small subset of mapbox style specs. I don't have the time or resources to bring that to a complete state (the spec is very complex - look at https://github.com/openlayers/ol-mapbox-style for a comparison), which is why I would rather remove the feature instead of confusing developers as to why their JSON styles don't work. I may re-evaluate this, but in general the state of protomaps-leaflet is frozen as the majority of use cases have moved to MapLibre. This could change if a company or institution wants to sponsor the continued development of the protomaps-leaflet engine, a good use case may be these notebooks.


// Monkey patch GridLayer for smoother URL updates
L.patchGridLayer = function (layer) {
Expand Down
Loading