Running Nominatim and Rendering in a Single Database

This tutorial shows how to set up a custom import style which creates a rendering database next to the geocoding database.

Since version 4.2.0 Nominatim allows to use osm2pgsql’s flex output to describe the table layout for the initial import. The Lua-based style is a lot more flexible than the simple json import configuration offered by Nominatim right now. One of the things you can do with it, is to create additional tables next to the ones needed by Nominatim. This paves the road to a much requested feature: running geocoding and rendering maps from the very same database.

This tutorial uses an experimental feature of Nominatim. You will need the latest release to make it work. Please use with care and be aware that details of the process may change in the future.

Choosing the import styles

In order to have a combined database, you need to have a flex style configuration for all the separate parts. For this tutorial we will use the import-extratags.lua style for the Nominatim database and the compatible.lua style from the flex example of osm2pgsql for the rendering database. The latter just stands in as an example layout. You will, of course, need to choose the style that provides the rendering database for your particular map layout.

Two osm2pgsql flex styles can only be used together when they

  • define different table names for their output tables
  • have different names for all global Lua variables they use.

As a rule, you should try to avoid using global variables in your Lua style files as much as possible. Database schemas are a great way to avoid clashes with table names.

When choosing the style, make sure it is really meant to work with the flex output. Styles like osm-carto still use the pgsql output with a Lua tagtransform script. This cannot be used with the flex style. Have a look at the experimental carto flex branch if you want to experiment with carto rendering.

Preparing the configuration

Nominatim has all the mechanisms for import and updates already built in, so the easiest way to get to a combined database is to configure it to set up the rendering tables in addition to its own tables.

First of all, create a project directory for Nominatim. We use this to collect all the necessary scripts and settings.

mkdir project-flex
cd project-flex

Now copy all necessary Lua style files into the project directory. This will make it simpler for Lua to find the styles later. Assuming you have the Nominatim source code in a directory $NOMINATIM_SRC, then run:

cp $NOMINATIM_SRC/settings/flex-base.lua .
cp $NOMINATIM_SRC/settings/import-extratags.lua .
cp $NOMINATIM_SRC/osm2pgsql/flex-config/compatible.lua .

Next you need a script that combines the two separate style files. Create a file flex-combined.lua in your project directory with the following content:

-- Make sure lua finds out scripts
package.path = '?.lua;' .. package.path

-- Create a table that saves all the handlers we need.

handlers = {node = {}, way = {}, relation = {}}

local function save_handlers()
    if osm2pgsql.process_node ~= nil then
        table.insert(handlers.node, osm2pgsql.process_node)
    end
    if osm2pgsql.process_way ~= nil then
        table.insert(handlers.way, osm2pgsql.process_way)
    end
    if osm2pgsql.process_relation ~= nil then
        table.insert(handlers.relation, osm2pgsql.process_relation)
    end
    osm2pgsql.process_node = nil
    osm2pgsql.process_way = nil
    osm2pgsql.process_relation = nil
end

-- load nominatim style
local nominatim = require('import-extratags')
save_handlers()

--- load mapnik style
local mapnik = require('compatible')
save_handlers()

-- calling both

local function run_all_handlers(object, hlist)
    local saved_tags = object.tags
    for i, handler in pairs(hlist) do
        -- Handlers tend to modify the tag list. Hand in a copy.
        if i == #hlist then
            object.tags = saved_tags
        else
            object.tags = {}
            for tk, tv in pairs(saved_tags) do
                object.tags[tk] = tv
            end
        end
        handler(object)
    end
end

function osm2pgsql.process_node(object)
    run_all_handlers(object, handlers.node)
end

function osm2pgsql.process_way(object)
    run_all_handlers(object, handlers.way)
end

function osm2pgsql.process_relation(object)
    run_all_handlers(object, handlers.relation)
end

The script loads each style file in turn and saves their processing functions in a local variable. Then it overwrites the processing functions with its own version which simply calls the functions it has previously saved. That’s it!

To tell Nominatim about your custom style file, you need to set the appropriate configuration variable:

echo NOMINATIM_IMPORT_STYLE=flex-combined.lua >> .env

Nominatim has an optimisation where it does not propagate changes in OSM data to dependent objects. For example, when a node is moved around, then the geometry of a way that this node is part of usually changes. Such geometry changes are rarely relevant for geocoding but they are important to update when making maps. Therefore, disable this optimisation:

echo NOMINATIM_UPDATE_FORWARD_DEPENDENCIES=yes >> .env

If you want to make further changes to the Nominatim configuration like changing the name of the database or using a flatnode file, have a look at the configuration settings and adapt your .env file accordingly.

You may also want to download additional data into your project directory at this point.

Importing and updating

You can now run the import as described in the Import guide:

nominatim import --osm-file <data file> 2>&1 | tee setup.log

Once it is finished, your database will contain the usual tables of a Nominatim import as well as tables planet_osm_point, planet_osm_line, planet_osm_polygon and planet_osm_roads for rendering.

You can also keep your data up to date by simply following Nominatim’s update guide.

Updates with tile expiry

If you want to use the expiry mechanism of osm2pgsql to update your tiles, then it is not possible anymore to use Nominatim’s builtin update function. Instead you need to do updates manually with osm2pgsql and then run the postprocessing. In this section we show how to do this with osm2pgsql-replication. You find the osm2pgsql-replication script bundled with the Nominatim source code under osm2pgsql/scripts/osm2pgsql-replication. Copy the script into your project directory.

You should already have a combined database imported with Nominatim as shown above. Next you need to initialise the replication process:

./osm2pgsql-replication init -d nominatim

If your database is named differently, adapt the name after the ‘-d’ parameter. Next you need a script that runs all the post-processing steps. Create a script named postprocess-update.sh in your project directory with the following content:

#!/bin/sh

# For Nominatim run the indexing.
nominatim index

# Then run tile expiry.
# This is the example from https://switch2osm.org. Adapt this command to
# whatever you need to do.
render_expired --map=s2o --min-zoom=13 --max-zoom=20 -s /run/renderd/renderd.sock < /var/cache/renderd/dirty_tiles.txt
rm /var/cache/renderd/dirty_tiles.txt

Make the script executable:

chmod 755 postprocess-update.sh

Now you can run a batch of updates with a command like this:

./osm2pgsql-replication update -d nominatim --post-processing ./postprocess-update.sh --osm2pgsql-cmd /usr/local/lib/nominatim/osm2pgsql -- -s -a -O flex -S flex-combined.lua -e 12 -o /var/cache/renderd/dirty_tiles.txt

The --osm2pgsql-cmd parameter tells osm2pgsql-replication where to find the osm2pgsql binary. This should be the binary bundled with Nominatim. If you have installed Nominatim somewhere other than the default /usr/local you need to adopt the path. The parameters after -- are the parameters with which osm2pgsql is called. Choosing the flex output and the style is of course mandatory. You might want to add other parameters here. For example, if you have configured a flatnode file, you must add -F <location of your file> to the parameter list.

← Building your own transliterations