Democratizing Digital Maps¶
Veit Schiele
• Founder and managing director of cusy GmbH, Berlin
• Author of the Python for Data Science Tutorials
• Author of the Python-Basics-Tutorial
• Contact: cusy.io/veit
Contents¶
- What is Protomaps? History, architecture, data basis and processing, checking the data
- Deployment with S3, Caddy or nginx
- Integration with common front-end libraries such as MapLibre³, Leaflet⁴ and OpenLayers³⁶
- What can you use protomaps for … and what not?
- Protomaps sprint
- Read more
1. What is Protomaps?¶
Protomaps¹ is an open source ecosystem for
- Creation
- Storage
- Delivery of vector tiles
… and what happened next¶
A Protomaps service consists of three main components:
Component | Purpose |
---|---|
Create PMTiles | Einlesen von Geo‑Daten und Generieren der Tiles, z.B. in einer CI/CD‑Pipeline |
Store PMTiles | Persistent storage of *.pmtiles files, for example, in an S3-compatible storage platform that supports HTTP range requests and Cross-Origin Resource Sharing (CORS) |
PMTiles Server | HTTP/2-based web server that supports caching and range requests, for example, Caddy or nginx |
- Base maps from OpenStreetMap⁶ and Natural Earth¹³ data
- Create your own maps with
github.com/protomaps/basemaps
- Excerpts from these maps with
pmtiles extract
¹⁸
pmtiles convert
¹⁹ converts MBTiles archives to PMTiles
- tippecanoe²⁰ can also convert GeoJSON, FlatGeobuf and other file formats
- rio-pmtiles²¹ is a plugin for the Rasterio²² Python library that can be used to convert GeoTIFFs
- GDAL ≥ 3.8.0 offers native support for PMTiles²³
Check the created Protomaps files with pmtiles verify {NAME}.pmtiles
Display header data with
$ pmtiles show NAME.pmtiles --header-json
{
"tile_compression": "gzip",
"tile_type": "mvt",
"minzoom": 0,
"maxzoom": 7,
"bounds": [
-176.684714,
-14.37374,
145.830418,
71.341223
],
"center": [
-82.96875,
37.71024,
7
]
}
$ pmtiles show NAME.pmtiles --metadata
{"description":"NAME.mbtiles","format":"pbf","generator":"tippecanoe v2.8.1","generator_options":"/Users/veit/workspace/protomaps/tippecanoe/tippecanoe -zg '--projection=EPSG:4326' -o NAME.mbtiles -l zcta NAME.json","maxzoom":"7","minzoom":"0","name":"NAME.mbtiles",… "vector_layers":[{"description":"","fields":{"AFFGEOID10":"String","ALAND10":"Number","AWATER10":"Number","GEOID10":"String","NAME10":"String"},"id":"name","maxzoom":7,"minzoom":0}],"version":"2"}
1.E Checking the data¶
If you want to view your PMTiles data, you can use the PMTiles Viewer²⁵:
2. Deployment¶
S3¶
$ pmtiles upload INPUT.pmtiles REMOTE.pmtiles --bucket=s3://BUCKET_NAME
Caddy¶
map.cusy.io {
### CORS Config ###
header @map_origins Access-Control-Allow-Origin "https://map.cusy.io"
header @map_origins Access-Control-Allow-Methods "GET, HEAD"
header @map_origins Access-Control-Allow-Headers "Range, If-Match"
header @map_origins Access-Control-Expose-Headers "ETag"
### Tiles ###
handle_path /tiles/* {
root * {{ tiles_dir }}
file_server
}
}
if ($request_method = "OPTIONS") {
add_header "Access-Control-Max-Age" 3600;
add_header "Content-Type" "text/plain charset=UTF-8";
add_header "Access-Control-Allow-Origin" "*" always;
add_header "Access-Control-Allow-Headers" "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range" always;
add_header "Content-Length" 0;
return 204;
}
Integration¶
MapLibre GL JS¶
You can also render a map with your own style using the NPM package @protomaps/basemaps³⁵.
This gives you comprehensive control with minimal code.
import { Protocol } from "pmtiles";
let protocol = new Protocol();
maplibregl.addProtocol("pmtiles",protocol.tile);
Integration¶
MapLibre GL JS¶
getStyle(): StyleSpecification {
const colorScheme = this.getActiveColorScheme();
const flavors = this.normalizeFlavors(this.options.flavors);
const sprites =
colorScheme == "dark" && flavors.invertDark ? "light" : colorScheme;
return {
version: 8,
glyphs: "https://map.cusy.io/fonts/{fontstack}/{range}.pbf",
sprite: "https://map.cusy.io/sprites/v4/${sprites}",
sources: {
[PMTILES_SOURCE]: {
type: "vector",
url: "pmtiles://${this.tilesURL}",
attribution:
"Map data: <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>",
},
},
layers: layers(PMTILES_SOURCE, flavors[colorScheme], DEFAULT_LAYERS_OPTS),
};
}
$ npm install protomaps-leaflet
<script src="protomaps-leaflet.js"></script>
<script>
const map = L.map("map")
var layer = protomapsL.leafletLayer({url:"FILE.pmtiles", flavor: "light", lang: "en"})
layer.addTo(map)
</script>
$ npm install ol-pmtiles
import "./style.css";
import { Map, View } from "ol";
import VectorTile from "ol/layer/VectorTile";
import { PMTilesVectorSource } from "ol-pmtiles";
import { Style, Stroke, Fill } from "ol/style";
import { useGeographic } from "ol/proj";
Integration¶
OpenLayers¶
const vectorLayer = new VectorTile({
declutter: true,
source: new PMTilesVectorSource({
url: "https://r2-public.protomaps.com/protomaps-sample-datasets/nz-buildings-v3.pmtiles",
attributions: ["© Land Information New Zealand"],
}),
style: new Style({
stroke: new Stroke({
color: "gray",
width: 1,
}),
fill: new Fill({
color: "rgba(20,20,20,0.9)",
}),
}),
});
4. What can you use Protomaps for?¶
- Cities and municipalities
- Offline maps
- Privacy-critical applications
- Specialised tools
4. What can you use Protomaps for… and what can’t you use them for?¶
PMTiles is intended for the web-based display of large, static data sets, which means
- they are based on a web platform and not on a local desktop application.
- the information totals more than a few megabytes, which is more than can be sensibly loaded at one time.
- the data set changes at most daily or never.
4. What can you use Protomaps for… and what can’t you use them for?¶
If your application does not have these three features, there are simpler alternatives to PMTiles:
- GeoJSON
- PostGIS
- GeoParquet
- Lonboard
5. Protomaps sprint¶
6. Read more¶
¹ Protomaps: https://protomaps.com
² OpenStreetMap: https://www.openstreetmap.org
³ MapLibre: https://maplibre.org
⁴ Leaflet: https://leafletjs.com
⁵ Mapbox: https://www.mapbox.com
⁶ OpenStreetMap (OSM): https://www.openstreetmap.org
⁷ NGI0 Core Fund: https://nlnet.nl/core
⁸ NLnet Foundation: https://nlnet.nl/project/Protomaps/
⁹ HTTP Range Requests: https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
¹⁰ Cross-Origin Resource Sharing (CORS): https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
¹¹ Base maps: https://maps.protomaps.com/builds/
¹² OpenStreetMap: https://openstreetmap.org/
6. Read more¶
¹³ Natural Earth: https://naturalearthdata.com/
¹⁴ basemaps repository: https://github.com/protomaps/basemaps
¹⁵ Planetiler: https://github.com/onthegomap/planetiler
¹⁶ Basemap Layers: https://docs.protomaps.com/basemaps/layers
¹⁷ Tilezen: https://tilezen.readthedocs.io/en/latest/layers/
¹⁸ pmtiles extract
: https://docs.protomaps.com/pmtiles/cli#extract
¹⁹ pmtiles convert
: https://docs.protomaps.com/pmtiles/cli#convert
²⁰ tippecanoe: https://github.com/felt/tippecanoe
²¹ rio-pmtiles: https://pypi.org/project/rio-pmtiles/
²² Rasterio: https://rasterio.readthedocs.io/en/stable/
²³ GDAL docs: https://gdal.org/en/stable/drivers/vector/pmtiles.html
²⁴ tilemaker: https://github.com/systemed/tilemaker
6. Read more¶
²⁵ PMTiles Viewer: https://pmtiles.io/
²⁶ RClone: https://rclone.org/
²⁷ Caddy: https://caddyserver.com/
²⁸ MapLibre GL JS: https://maplibre.org/maplibre-gl-js/docs/
²⁹ PMTiles: https://www.npmjs.com/package/pmtiles
³⁰ MapLibre GL addProtocol
-Funktion: https://maplibre.org/maplibre-gl-js/docs/API/functions/addProtocol/
³¹ MapLibre fontstack: https://maplibre.org/maplibre-style-spec/glyphs/
³² MapLibre spritesheet assets: https://maplibre.org/maplibre-style-spec/sprite/
³³ Basemaps assets repository: http://github.com/protomaps/basemaps-assets
³⁴ MapLibre style: https://maplibre.org/maplibre-style-spec/
³⁵ @protomaps/basemaps: https://www.npmjs.com/package/@protomaps/basemaps
³⁶ OpenLayers: https://openlayers.org
6. Read more¶
³⁷ Nominatim: https://nominatim.org/
³⁸ Photon: https://photon.komoot.io/
³⁹ Pelias: https://pelias.io/
⁴⁰ Valhalla: https://valhalla.github.io/valhalla/
⁴¹ OpenTripPlanner: https://www.opentripplanner.org/