OpenStreetMap logo OpenStreetMap

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.

Email icon Bluesky Icon Facebook Icon LinkedIn Icon Mastodon Icon Telegram Icon X Icon

Discussion

Log in to leave a comment