OpenStreetMap User's Diaries
Using OSMF and custom vector tiles together
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 127.0.0.1:8000. I the
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.
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 pointI start with the guide on switch2osm, building a style file to serve locally on 127.0.0.1:8000. I the
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.
OpenStreetMap Blogs














































