Skip to main content

Example: Vector Tiles

Street sign in Brussels: Rue de la Montagne - Bergstraat

Of course osm2pgsql can also generate the data needed for creating vector tiles. In this example we are looking at streets in Brussels. Belgium has three official languages: Dutch, French, and German. Most people speak either Dutch (Flemish) or French, so all streets in the Belgian capital have two names.

For this use case we can create vector tiles that contain the names in both languages. We add, as third option, the generic name, which in Brussels contains both the French and the Dutch name. The resulting map can be switched between languages without the overhead of getting new tiles from the server. All the rendering happens in the browser.

To recreate this map you need the OSM data from Brussels. You can download the data for Belgium and use Osmium to cut out the area:

osmium extract -b 4.29,50.815,4.47,50.90 /arc/geodata/osm/belgium-latest.osm.pbf -o brussels.osm.pbf

Then create a database and import with osm2pgsql:

osm2pgsql -d brussels -O flex -S streets.lua brussels.osm.pbf

Here is the Lua config file:

Download Lua config
local streets = osm2pgsql.define_way_table('streets', {
    { column = 'type',    type = 'text' },
    { column = 'name',    type = 'text' },
    { column = 'name_fr', type = 'text' },
    { column = 'name_nl', type = 'text' },
    { column = 'tags',    type = 'jsonb' },
    { column = 'geom',    type = 'linestring' },

local get_highway_value = osm2pgsql.make_check_values_func({
    'motorway', 'trunk', 'primary', 'secondary', 'tertiary',
    'motorway_link', 'trunk_link', 'primary_link', 'secondary_link', 'tertiary_link',
    'unclassified', 'residential', 'pedestrian'

function osm2pgsql.process_way(object)
    local highway_type = get_highway_value(object.tags.highway)

    if not highway_type then

    if object.tags.area == 'yes' then

        type    = highway_type,
        tags    = object.tags,
        name    =,
        name_fr = object.tags['name:fr'],
        name_nl = object.tags['name:nl'],
        geom    = object:as_linestring(),

There are many ways how to serve the contents of a PostgreSQL/PostGIS database as vector tiles. One of the easiest to set up is pg_tileserv. It only needs the database connection info and will serve all geometry tables it finds there as MVT tiles:

DATABASE_URL=postgresql:///brussels ./pg_tileserv

(For the demo map here we have dumped all tiles into a static file system, so we don’t need to run a database and tileserver.)