tilemaker Documentation

repository·master·Indexed 23 days ago

https://github.com/systemed/tilemaker

A 'stack-free' C++14 tool that creates Mapbox Vector Tiles (MVT) from OpenStreetMap (.osm.pbf) extracts without requiring a database. It supports output formats like .mbtiles and .pmtiles, utilizing JSON configuration files and Lua processing scripts to define thematic layers, zoom levels, and feature filtering.

Tokens
10.5K
Snippets
27
Records
50
Agent score
84%

What's inside tilemaker

  1. Use Noto unicode fonts for OpenStreetMap & MapBox GL

    master

    This repository provides manually merged Google Noto fonts designed to provide glyphs for unicode OpenStreetMap labels in TileServer GL, MapBox GL JS, Android, and iOS.

    Usage Requirements:

    • You are free to use these fonts in your styles on your servers.
    • Mandatory: You must keep the font name exactly as it is in this repository to credit KlokanTech for the merging work.
  2. Understand the vector tile workflow with tilemaker

    master

    Tilemaker is used to slice OpenStreetMap (OSM) data into vector tiles. The typical workflow is:

    1. Source Data: Obtain OSM data in .pbf format (e.g., from Geofabrik or BBBike) and optional coastline/landuse data in shapefile format.
    2. Schema Definition: Define how OSM objects are categorized into layers (e.g., 'roads', 'landuse') using a JSON configuration file and a Lua script.
    3. Generation: Use tilemaker to process the .pbf data into an .mbtiles container (an SQLite database containing .mvt encoded tiles).
    4. Serving: Use a server (like the bundled tilemaker-server, mbtileserver, or tileserver-php) to serve the .mbtiles via HTTP.
    5. Rendering: Use a client-side library (like MapLibre GL) to render the tiles on-screen using a JSON stylesheet.

    Tilemaker includes ready-made JSON/Lua configuration files compatible with the popular OpenMapTiles schema for out-of-the-box usage.

  3. How tilemaker configuration works

    master

    Vector tiles are organized into thematic 'layers' (e.g., river, railway). You define which OSM data belongs to which layer using two files:

    1. JSON Configuration File: Lists each layer and the zoom levels at which it should be applied. Specified via --config.
    2. Lua Processing File: A program that inspects the tags of each node/way and determines which layer it should be placed into. Specified via --process.

    If you do not provide these flags, tilemaker looks for config.json and process.lua in the current directory. If neither are found, the process will error.

  4. Use the Lua key/value store for external data

    master

    Tilemaker provides a global key/value store accessible across all processing threads. This is useful for bringing in external data during the init_function and retrieving it during object processing.

    Workflow

    1. Initialize: In init_function (where is_first is true), use Lua's standard I/O functions to read data.
    2. Store: Use SetData(key, value) to save a string pair. Both keys and values must be strings.
    3. Retrieve: Use GetData(key) within node_function, way_function, etc. If the key is not found, it returns an empty string.

    Note: SetData and GetData are only available for strings.

  5. How multipolygon relations are handled

    master

    Tilemaker supports multipolygon relations natively without requiring custom Lua code. When a multipolygon is processed, tilemaker constructs the geometry automatically and passes the tags to way_function as if it were a simple area.

    Boundary relations also feature automatic handling of inner/outer ways. Users can choose between two approaches:

    1. Properties-on-ways: Treating boundaries as properties on ways (recommended for administrative boundaries).
    2. Complete-geometries: Treating boundaries as complete geometries (recommended for filled areas like forests or nature reserves).
  6. Handle nested relations and hierarchies

    master

    Tilemaker provides two methods for dealing with nested relations (e.g., a route inside a superroute):

    1. Processing nested relations in relation_function

    You can iterate through parent relations within relation_function using NextRelation() and FindInRelation(key), just as you would for ways or nodes. Note that you must Accept() the parent relations in relation_scan_function first.

    2. Bouncing tags down with relation_postscan_function

    Because the main processing functions (way_function, node_function, relation_function) only support one level of parent relations for performance, you can use relation_postscan_function to handle deeper hierarchies (grandparents, etc.).

    This function runs after relation_scan_function when all accepted relations are in memory. You can read ancestor tags and use SetTag(key, value) to apply them to the child relation. These tags will then be available to the standard processing functions.

    Note: In deeply nested hierarchies, tilemaker flattens the structure during the postscan, so the original hierarchy is not preserved.

    -- Use relation_postscan_function to pass tags from a parent to a child
    function relation_postscan_function()
      while true do
        local parent, role = NextRelation()
        if not parent then break end
        
        if FindInRelation("type")=="superroute" then
          local parent_name = FindInRelation("name")
          SetTag("name", parent_name)
        end
      end
    end
  7. Define tile schemas using JSON and Lua

    master

    To control which OSM objects are included in your tiles and which layers they belong to, you must define a schema. In tilemaker, a schema consists of two parts:

    1. JSON Configuration: A file used to set out your layer definitions.
    2. Lua Script: A script used to logicially place specific OSM objects into those layers.

    By using these two components, you can create custom layers such as roads, landuse, or POIs (points of interest).

  8. Quickstart with Docker

    master

    The fastest way to use tilemaker without compiling from source is via the official Docker image. You can generate Protomaps vector tiles from an OpenStreetMap .osm.pbf snapshot by mounting your local directory to /data in the container.

    1. Download an .osm.pbf file (e.g., from Geofabrik).
    2. Run the following command to generate .pmtiles output.
    docker run -it --rm --pull always -v $(pwd):/data \
      ghcr.io/systemed/tilemaker:master \
      /data/monaco-latest.osm.pbf \
      --output /data/monaco-latest.pmtiles
  9. Use the sqlite_modern_cpp wrapper

    master

    The sqlite_modern_cpp library is a lightweight C++ wrapper around the SQLite C API. It uses stream operators (<< and >>) to execute queries, bind parameters, and retrieve results.

    Key Capabilities:

    • Database Creation: Initializing a sqlite::database object with a filename creates the database file if it doesn't exist.
    • Executing Queries: Use the << operator to send SQL commands to the database.
    • Parameter Binding: Use ? placeholders in your SQL. Supported types for binding include:
      • int, long, long long
      • float, double
      • std::string (for UTF-8)
      • std::u16string (for UTF-16)
    • Retrieving Results:
      • Multiple Rows: Use the >> operator with a lambda function. The lambda is executed for each row returned, with parameters matching the selected columns.
      • Single Column/Row: Use the >> operator to extract a single value directly into a variable (e.g., int, string, double).
    • Metadata: Use db.last_insert_rowid() to get the ID of the last inserted row.
    #include <iostream>
    #include "sqlite_modern_cpp.h"
    using namespace sqlite;
    using namespace std;
    
    try {
        database db("dbfile.db");
    
        // Create table
        db << "create table if not exists user (_id integer primary key autoincrement not null, age int, name text, weight real);";
    
        // Insert with binding
        db << "insert into user (age,name,weight) values (?,?,?);" << 20 << u"bob" << 83.25f;
    
        // Select multiple rows via lambda
        db << "select age,name,weight from user where age > ? ;" << 18
           >> [&](int age, string name, double weight) {
               cout << age << ' ' << name << ' ' << weight << endl;
           };
    
        // Select single value
        int count = 0;
        db << "select count(*) from user" >> count;
    }
    catch (exception& e) {
        cout << e.what() << endl;
    }
  10. Install tilemaker from source

    master

    tilemaker is written in C++14. To build and install it manually, ensure you have the following dependencies installed:

    • Boost (minimum 1.66)
    • Lua (5.1 or later) or LuaJIT
    • sqlite3
    • shapelib
    • rapidjson

    Once dependencies are met, build and install using make:

    make
    sudo make install
  11. Standard usage of tilemaker

    master

    To create vector tiles from an OpenStreetMap .pbf extract, use the standard syntax providing --input, --output, --config, and --process arguments.

    Supported output formats:

    • .mbtiles: An SQLite database (widely supported).
    • .pmtiles: Optimized for cloud serving.
    • Directory path: Writes tiles directly to the filesystem.

    Note: --config and --process paths are required for custom processing, but defaults are available if using the OpenMapTiles-compatible scripts provided in the repository.

    tilemaker --input oxfordshire.osm.pbf \
              --output oxfordshire.mbtiles \
              --config resources/config-openmaptiles.json \
              --process resources/process-openmaptiles.lua