At SOTM EU I gave a demo of how to use the OSMF vector tiles with custom tiles for detailed information on a feature. In the demo I showed walls, focusing on the material of the walls.
This is an expanded explaination, focusing on the example of trees and forests.
Starting point
I start with the guide on switch2osm, building a style file to serve locally on http://127.0.0.1:8000. I then serve the release directory with a HTTP server. With simple stuff like this I tend to use node’s http-server with npx http-server release -p 8000 --cors -c-1
Making tiles with tree layers
I’m using Tilemaker to make the tile layers. This is a easy way to generate tiles but can’t be updated minutely.
I need a config file and a process file. The former tells tilemaker what layers there are, the latter takes OSM objects and adds them to the right layer
config.json
{
"layers": {
"tree_points": { "minzoom": 10, "maxzoom": 14 },
"tree_lines": { "minzoom": 10, "maxzoom": 14 },
"tree_areas": { "minzoom": 6, "maxzoom": 14 }
},
"settings": {
"minzoom": 6,
"maxzoom": 14,
"basezoom": 14,
"include_ids": false,
"compress": "gzip",
"name": "Tree example",
"version": "0.1",
"description": "Sample vector tiles"
}
}
process.lua
node_keys = { "natural=tree" }
way_keys = { "natural=tree_row", "natural=wood", "landuse=forest" }
function node_function(node)
if Find("natural") == "tree" then
Layer("tree_points", false)
end
end
function way_function(node)
local natural = Find("natural")
if natural == "tree_row" then
Layer("tree_lines", false)
elseif natural == "wood" or Find("landuse") == "forest" then
Layer("tree_areas", true)
end
end
I can now run tilemaker from the command line with ~/osm/tilemaker/tilemaker --input planet-latest.osm.pbf --output release/trees.pmtiles. The tiles takes my computer about 10 minutes to generate for the whole planet.
Using pmtiles
I need to add a couple lines to maplibre.html. I add <script src="https://unpkg.com/pmtiles@4.3/dist/pmtiles.js"></script> below the maplibre script source and a bit of javascript below the MapLibre RTL init
let protocol = new pmtiles.Protocol({metadata: true});
maplibregl.addProtocol("pmtiles", protocol.tile);
Changing the style
Next I need to edit the MapLibre GL style to use the new layers. I start by using a JSON formatter to make it look nicer, then add the pmtiles as a source
"trees": {
"type": "vector",
"url": "pmtiles://http://127.0.0.1:8000/trees.pmtiles"
}
Versatiles Colorful already has a forest layer so I want to change that to point at my tree data. I do this by changing the land-forest layer. The layer becomes
{
"source": "trees",
"id": "land-forest",
"type": "fill",
"source-layer": "tree_areas",
"paint": {
"fill-color": "rgb(102,170,68)",
"fill-opacity": {
"stops": [
[
7,
0
],
[
8,
0.1
]
]
}
}
},
The filter property is no longer needed because the entire tree_areas source layer is forests. I start my http server and go to http://127.0.0.1:8000/maplibre.html to verify that the forests load correctly. After checking them I move on to new layers.
This isn’t a guide to Maplibre styling, so I’m keeping the new layers simple. I add them as defined below, inserting them before the land-forest layer.
{
"source": "trees",
"id": "land-treeline",
"type": "line",
"source-layer": "tree_lines",
"paint": {
"line-color": "rgb(102,170,68)",
"line-width": 3
}
},
{
"source": "trees",
"id": "land-treepoint",
"type": "circle",
"source-layer": "tree_points",
"paint": {
"circle-color": "rgb(102,170,68)"
}
},
If I now load http://127.0.0.1:8000/maplibre.html in a browser I see a map with custom tree rendering.
Discussion