Technical Documentation: osm-search/Nominatim
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
Nominatim
Nominatim (from the Latin, 'by name') is a tool to search OpenStreetMap data
by name and address (geocoding) and to generate synthetic addresses of
OSM points (reverse geocoding). An instance with up-to-date data can be found
at https://nominatim.openstreetmap.org. Nominatim is also used as one of the
sources for the Search box on the OpenStreetMap home page.
Documentation
The documentation of the latest development version is in thedocs/ subdirectory. A HTML version can be found at
https://nominatim.org/release-docs/develop/ .
Installation
The latest stable release can be downloaded from https://nominatim.org.
There you can also find installation instructions for the release, as well as an extensive Troubleshooting/FAQ section.
Detailed installation instructions for current master
can be found at nominatim.org as well.
A quick summary of the necessary steps:
Clone this git repository and download the country grid
textgit clone https://github.com/osm-search/Nominatim.git wget -O Nominatim/data/country_osm_grid.sql.gz https://nominatim.org/data/country_grid.sql.gzCreate a Python virtualenv and install the packages:
textpython3 -m venv nominatim-venv ./nominatim-venv/bin/pip install packaging/nominatim-{api,db}Create a project directory, get OSM data and import:
textmkdir nominatim-project cd nominatim-project ../nominatim-venv/bin/nominatim import --osm-file <your planet file> 2>&1 | tee setup.logStart the webserver:
text./nominatim-venv/bin/pip install uvicorn falcon ../nominatim-venv/bin/nominatim serve
License
The Python source code is available under a GPL license version 3 or later.
The Lua configuration files for osm2pgsql are released under the
Apache License, Version 2.0. All other files are under a GPLv2 license.
Contributing
Contributions, bug reports and pull requests are welcome. When reporting a
bug, please use one of the
issue templates
and make sure to provide all the information requested. If you are not
sure if you have really found a bug, please ask for help in the forums
first (see 'Questions' below).
For details on contributing, have a look at the
contribution guide.
Questions and help
If you have questions about search results and the OpenStreetMap data
used in the search, use the OSM Forum.
For questions, community help and discussions around the software and
your own installation of Nominatim, use the
Github discussions forum.
Overview (`packaging/nominatim-api/README.md`)
Nominatim - Frontend Library
Nominatim is a tool to search OpenStreetMap data
by name and address (geocoding) and to generate synthetic addresses of
OSM points (reverse geocoding).
This module implements the library for searching a Nominatim database
imported with the nominatim-db package.
Installation
To install the Nominatim API from pypi, run:
pip install nominatim-apiRunning a Nominatim server
You need Falcon or Starlette to run Nominatim as a service, as well as
an ASGI-capable server like uvicorn. To install them from pypi run:
pip install falcon uvicornYou need to have a Nominatim database imported with the 'nominatim-db'
package. Go to the project directory, then run uvicorn as:
uvicorn --factory nominatim_api.server.falcon.server:run_wsgiDocumentation
The full documentation for the Nominatim library can be found at:
https://nominatim.org/release-docs/latest/library/Getting-Started/
The v1 API of the server is documented at:
https://nominatim.org/release-docs/latest/api/Overview/
License
The source code is available under a GPLv3 license.
Overview (`packaging/nominatim-db/README.md`)
Nominatim - DB Backend
Nominatim is a tool to search OpenStreetMap data
by name and address (geocoding) and to generate synthetic addresses of
OSM points (reverse geocoding).
This module implements the database backend for Nominatim and the
command-line tool for importing and maintaining the database.
Installation
Prerequisites
Nominatim requires osm2pgsql (>=1.8) for reading
OSM data and PostgreSQL (>=9.6) to store the data.
On Ubuntu (>=23.04) and Debian (when using backports), you can install them with:
sudo apt-get install osm2pgsql postgresql-postgisInstallation from pypi
To install Nominatim from pypi, run:
pip install nominatim-dbQuick start
First create a project directory for your new Nominatim database, which
is the space for additional configuration and customization:
mkdir nominatim-projectMake sure you run all nominatim commands from within the project directory:
cd nominatim-projectDownload an appropriate data extract, for example from
Geofabrik and import the file:
nominatim import --osm-file <downlaoded-osm-data.pbf>You will need to install the nominatim-api
package to query the database.
Documentation
A HTML version of the documentation can be found at
https://nominatim.org/release-docs/latest/ .
License
The source code is available under a GPLv3 license.
Index (`docs/index.md`)
Nominatim (from the Latin, 'by name') is a tool to search OSM data by name and
address and to generate synthetic addresses of OSM points (reverse geocoding).
It has also limited capability to search features by their type
(pubs, hotels, churches, etc).
This guide comes in five parts:
- API reference for users of Nominatim
- Administration Guide for those who want
to install their own Nominatim server - Customization Guide for those who want to
adapt their own installation to their special requirements - Library Guide for Python developers who
want to use Nominatim as a library in their project - Developer's Guide for developers of the software
Api Overview (`docs/api/Overview.md`)
This section describes the API V1 of the Nominatim web service. The
service offers the following endpoints:
- /search - search OSM objects by name or type
- /reverse - search OSM object by their location
- /lookup - look up address details for OSM objects by their ID
- /status - query the status of the server
- /deletable - list objects that have been deleted in OSM but are held
back in Nominatim in case the deletion was accidental - /polygons - list of broken polygons detected by Nominatim
- /details - show internal details for an object (for debugging only)
Customize Overview (`docs/customize/Overview.md`)
Nominatim comes with a predefined set of configuration options that should
work for most standard installations. If you have special requirements, there
are many places where the configuration can be adapted. This chapter describes
the following configurable parts:
- Global Settings has a detailed description of all parameters that
can be set in your local.envconfiguration - Import styles explains how to write your own import style
in order to control what kind of OSM data will be imported - API Result Formatting shows how to change the
output of the Nominatim API - Place ranking describes the configuration around classifing
places in terms of their importance and their role in an address - Tokenizers describes the configuration of the module
responsible for analysing and indexing names - Special Phrases are common nouns or phrases that
can be used in search to identify a class of places
There are also guides for adding the following external data:
Develop Overview (`docs/develop/overview.md`)
Basic Architecture
Nominatim provides geocoding based on OpenStreetMap data. It uses a PostgreSQL
database as a backend for storing the data.
There are three basic parts to Nominatim's architecture: the data import,
the address computation and the search frontend.
The data import stage reads the raw OSM data and extracts all information
that is useful for geocoding. This part is done by osm2pgsql, the same tool
that can also be used to import a rendering database. It uses the special
flex output style defined in the directory /lib-lua. The result of
the import can be found in the database table place.
The address computation or indexing stage takes the data from place
and adds additional information needed for geocoding. It ranks the places by
importance, links objects that belong together and computes addresses and
the search index. Most of this work is done in PL/pgSQL via database triggers
and can be found in the files in the sql/functions/ directory.
The search frontend implements the actual API. It takes search
and reverse geocoding queries from the user, looks up the data and
returns the results in the requested format. This part is located in thenominatim-api package. The source code can be found in src/nominatim_api.
Result filters and the rounds of a forward search
A forward search is not a single database query. The frontend derives the
possible interpretations of the query, orders them by penalty and runs them in
rounds until it has collected enough good results. Every round that produces a
result also restricts the ranks that the following rounds may still return, so
the search usually stops long before all interpretations have been tried.
The result filters of the search API (include, exclude, countrycodes
and layer) are applied within these queries. Dropping an early result
therefore lets later, higher-penalty rounds run that would otherwise never
have been reached. As a consequence a filtered query may return more results
than the unfiltered one, and results that the unfiltered query never showed.
That is expected behaviour.
Library Getting Started (`docs/library/Getting-Started.md`)
Getting Started
The Nominatim search frontend is implemented as a Python library and can as
such directly be used in Python scripts and applications. You don't need to
set up a web frontend and access it through HTTP calls. The library gives
direct access to the Nominatim database through similar search functions as
offered by the web API. In addition, it will give you a more complete and
detailed view on the search objects stored in the database.
!!! warning
The Nominatim library is used for accessing a local Nominatim database.
It is not meant to be used against web services of Nominatim like the
one on https://nominatim.openstreetmap.org. If you need a Python library
to access these web services, have a look at
[GeoPy](https://geopy.readthedocs.io). Don't forget to consult the
usage policy of the service you want to use before accessing such
a web service.Installation
To use the Nominatim library, you need access to a local Nominatim database.
Follow the installation and
import instructions to set up your database.
The Nominatim frontend library is contained in the Python package nominatim-api.
You can install the latest released version directly from pip:
pip install nominatim-apiTo install the package from the source tree directly, run:
pip install packaging/nominatim-apiUsually you would want to run this in a virtual environment.
A simple search example
To query the Nominatim database you need to first set up a connection. This
is done by creating an Nominatim API object. This object exposes all the
search functions of Nominatim that are also known from its web API.
This code snippet implements a simple search for the town of 'Brugge':
!!! example
=== "NominatimAPIAsync"
``` python
import asyncio
import nominatim_api as napi
async def search(query):
async with napi.NominatimAPIAsync() as api:
return await api.search(query)
results = asyncio.run(search('Brugge'))
if not results:
print('Cannot find Brugge')
else:
print(f'Found a place at {results[0].centroid.x},{results[0].centroid.y}')
```
=== "NominatimAPI"
``` python
import nominatim_api as napi
with napi.NominatimAPI() as api:
results = api.search('Brugge')
if not results:
print('Cannot find Brugge')
else:
print(f'Found a place at {results[0].centroid.x},{results[0].centroid.y}')
```The Nominatim library is designed around
asyncio. NominatimAPIAsync
provides you with an interface of coroutines.
If you have many requests to make, coroutines can speed up your applications
significantly.
For smaller scripts there is also a synchronous wrapper around the API. By
using NominatimAPI, you get exactly the same interface using classic functions.
The examples in this chapter will always show-case both
implementations. The documentation itself will usually refer only to
'Nominatim API class' when both flavours are meant. If a functionality is
available only for the synchronous or asynchronous version, this will be
explicitly mentioned.
Defining which database to use
The Configuration
section explains how Nominatim is configured using the
dotenv library.
The same configuration mechanism is used with the
Nominatim API library. You should therefore be sure you are familiar with
the section.
There are three different ways, how configuration options can be set for
a 'Nominatim API class'. When you have set up your Nominatim database, you
have normally created a project directory
which stores the various configuration and customization files that Nominatim
needs. You may pass the location of the project directory to your
'Nominatim API class' constructor and it will read the .env file in the
directory and set the configuration accordingly. Here is the simple search
example, using the configuration from a pre-defined project directory in/srv/nominatim-project:
!!! example
=== "NominatimAPIAsync"
``` python
import asyncio
import nominatim_api as napi
async def search(query):
async with napi.NominatimAPIAsync('/srv/nominatim-project') as api:
return await api.search(query)
results = asyncio.run(search('Brugge'))
if not results:
print('Cannot find Brugge')
else:
print(f'Found a place at {results[0].centroid.x},{results[0].centroid.y}')
```
=== "NominatimAPI"
``` python
import nominatim_api as napi
with napi.NominatimAPI('/srv/nominatim-project') as api:
results = api.search('Brugge')
if not results:
print('Cannot find Brugge')
else:
print(f'Found a place at {results[0].centroid.x},{results[0].centroid.y}')
```You may also configure Nominatim by setting environment variables.
Normally Nominatim will check the operating system environment. Lets
say you want to look up 'Brugge' in the special database named 'belgium' instead of the
standard 'nominatim' database. You can run the example script above like this:
NOMINATIM_DATABASE_DSN=pgsql:dbname=belgium python3 example.pyThe third option to configure the library is to hand in the configuration
parameters into the 'Nominatim API class'. Changing the database would look
like this:
!!! example
=== "NominatimAPIAsync"
``` python
import asyncio
import nominatim_api as napi
config_params = {
'NOMINATIM_DATABASE_DSN': 'pgsql:dbname=belgium'
}
async def search(query):
async with napi.NominatimAPIAsync(environ=config_params) as api:
return await api.search(query)
results = asyncio.run(search('Brugge'))
```
=== "NominatimAPI"
``` python
import nominatim_api as napi
config_params = {
'NOMINATIM_DATABASE_DSN': 'pgsql:dbname=belgium'
}
with napi.NominatimAPI(environ=config_params) as api:
results = api.search('Brugge')
```When the environ parameter is given, then only configuration variables
from this dictionary will be used. The operating system's environment
variables will be ignored.
Presenting results to humans
All search functions return full result objects from the database. Such a
result object contains lots of details: names, address information, OSM tags etc.
This gives you lots of flexibility what to do with the results.
One of the most common things to get is some kind of human-readable label
that describes the result in a compact form. Usually this would be the name
of the object and some parts of the address to explain where in the world
it is. To create such a label, you need two things:
- the address details of the place
- all names for the label adapted to the language you wish to use for display
Again searching for 'Brugge', this time with a nicely formatted result:
!!! example
=== "NominatimAPIAsync"
``` python
import asyncio
import nominatim_api as napi
async def search(query):
async with napi.NominatimAPIAsync() as api:
return await api.search(query, address_details=True)
results = asyncio.run(search('Brugge'))
locale = napi.Locales(['fr', 'en'])
for i, result in enumerate(results):
address_parts = result.address_rows.localize(locale)
print(f"{i + 1}. {', '.join(address_parts)}")
```
=== "NominatimAPI"
``` python
import nominatim_api as napi
with napi.NominatimAPI() as api:
results = api.search('Brugge', address_details=True)
locale = napi.Locales(['fr', 'en'])
for i, result in enumerate(results):
address_parts = result.address_rows.localize(locale)
print(f"{i + 1}. {', '.join(address_parts)}")
```To request information about the address of a result, add the optional
parameter 'address_details' to your search:
>>> results = api.search('Brugge', address_details=True)An additional field address_rows will set in results that are returned.
It contains a list of all places that make up the address of the place. For
simplicity, this includes name and house number of the place itself. With
the names in this list it is possible to create a human-readable description
of the result. To do that, you first need to decide in which language the
results should be presented. As with the names in the result itself, the
places in address_rows contain all possible name translation for each row.
The library has a helper class Locales which helps extracting a name of a
place in the preferred language. It takes a single parameter with a list
of language codes in the order of preference. So
locale = napi.Locales(['fr', 'en'])creates a helper class that returns the name preferably in French. If that is
not possible, it tries English and eventually falls back to the default name
or ref.
The Locales object can be applied to a name dictionary to return the best-matching
name out of it:
>>> print(locale.display_name(results[0].names))
'Brugges'The address_row field has a helper function to compute the display name for each Address Line
component based on its local_name field. This is then utilized by the overall result object,
which has a helper function to apply the function to all its ‘address_row’ members and saves
the result in the locale_name field.
However, in order to set this local_name field in a preferred language, you must use the Locales
object which contains the function localize_results, which explicitly sets each local_name field.
>>> Locales().localize_results(results)
>>> address_parts = results[0].address_rows
>>> print(', '.join(address_parts))
Bruges, Flandre-Occidentale, Flandre, BelgiqueThis is a fairly simple way to create a human-readable description. The
place information in address_rows contains further information about each
place. For example, which OSM admin_level was used, what category the place
belongs to or what rank Nominatim has assigned. Use this to adapt the output
to local address formats.
For more information on address rows, see
detailed address description.
Admin Advanced Installations (`docs/admin/Advanced-Installations.md`)
Advanced installations
This page contains instructions for setting up multiple countries in
your Nominatim database. It is assumed that you have already successfully
installed the Nominatim software itself, if not return to the
installation page.
Importing with a database user without superuser rights
Nominatim usually creates its own PostgreSQL database at the beginning of the
import process. This makes usage easier for the user but means that the
database user doing the import needs the appropriate rights.
If you prefer to run the import with a database user with limited rights,
you can do so by changing the import process as follows:
Run the command for database preparation with a database user with
superuser rights. For example, to use a db user 'dbadmin' for a
database 'nominatim', execute:textNOMINATIM_DATABASE_DSN="pgsql:dbname=nominatim;user=dbadmin" nominatim import --prepare-databaseGrant the import user the right to create tables. For example, foe user 'import-user':
textpsql -d nominatim -c 'GRANT CREATE ON SCHEMA public TO "import-user"'Now run the reminder of the import with the import user:
textNOMINATIM_DATABASE_DSN="pgsql:dbname=nominatim;user=import-user" nominatim import --continue import-from-file --osm-file file.pbf
Importing multiple regions (without updates)
To import multiple regions in your database you can simply give multiple
OSM files to the import command:
nominatim import --osm-file file1.pbf --osm-file file2.pbfIf you already have imported a file and want to add another one, you can
use the add-data function to import the additional data as follows:
nominatim add-data --file <FILE>
nominatim refresh --postcodes
nominatim index -j <NUMBER OF THREADS>Please note that adding additional data is always significantly slower than
the original import.
Importing multiple regions (with updates)
If you want to import multiple regions and be able to keep them up-to-date
with updates, then you can use the scripts provided in the utils directory.
These scripts will set up an update directory in your project directory,
which has the following structure:
update
├── europe
│ ├── andorra
│ │ └── sequence.state
│ └── monaco
│ └── sequence.state
└── tmp
└── europe
├── andorra-latest.osm.pbf
└── monaco-latest.osm.pbfThe sequence.state files contain the sequence ID for each region. They will
be used by pyosmium to get updates. The tmp folder is used for import dump and
can be deleted once the import is complete.
Setting up multiple regions
Create a project directory as described for the
simple import. If necessary,
you can also add an .env configuration with customized options. In particular,
you need to make sure that NOMINATIM_REPLICATION_UPDATE_INTERVAL andNOMINATIM_REPLICATION_RECHECK_INTERVAL are set according to the update
interval of the extract server you use.
Copy the scripts utils/import_multiple_regions.sh and utils/update_database.sh
into the project directory.
Now customize both files as per your requirements
List of countries. e.g.
textCOUNTRIES="europe/monaco europe/andorra"URL to the service providing the extracts and updates. eg:
textBASEURL="https://download.geofabrik.de" DOWNCOUNTRYPOSTFIX="-latest.osm.pbf"Followup in the update script can be set according to your installation.
E.g. for Photon,textFOLLOWUP="curl http://localhost:2322/nominatim-update"will handle the indexing.
To start the initial import, change into the project directory and run
bash import_multiple_regions.shUpdating the database
Change into the project directory and run the following command:
bash update_database.shThis will get diffs from the replication server, import diffs and index
the database. The default replication server in the
script (Geofabrik) provides daily updates.
Using an external PostgreSQL database
You can install Nominatim using a database that runs on a different server.
Simply point the configuration variable NOMINATIM_DATABASE_DSN to the
server and follow the standard import documentation.
The import will be faster, if the import is run directly from the database
machine. You can easily switch to a different machine for the query frontend
after the import.
Moving the database to another machine
For some configurations it may be useful to run the import on one machine, then
move the database to another machine and run the Nominatim service from there.
For example, you might want to use a large machine to be able to run the import
quickly but only want a smaller machine for production because there is not so
much load. Or you might want to do the import once and then replicate the
database to many machines.
The important thing to keep in mind when transferring the Nominatim installation
is that you need to transfer the database and the project directory. Both
parts are essential for your installation.
The Nominatim database can be transferred using the pg_dump/pg_restore tool.
Make sure to use the same version of PostgreSQL and PostGIS on source and
target machine.
!!! note
Before creating a dump of your Nominatim database, consider running
nominatim freeze first. Your database looses the ability to receive further
data updates but the resulting database is only about a third of the size
of a full database.
Next install nominatim-api on the target machine by following the standard
installation instructions. Again, make sure to use the same version as the
source machine.
Create a project directory on your destination machine and set up the .env
file to match the configuration on the source machine. That's all.
Admin Deployment Python (`docs/admin/Deployment-Python.md`)
Deploying the Nominatim Python frontend
Nominatim can be run as a Python-based
ASGI web application. You have the
choice between Falcon
and Starlette as the ASGI framework.
This section gives a quick overview on how to configure Nginx to serve
Nominatim. Please refer to the documentation of
Nginx for background information on how
to configure it.
Installing the required packages
!!! warning
ASGI support in gunicorn requires at least version 25.0. If you need
to work with an older version of gunicorn, please refer to
older Nominatim deployment documentation
to learn how to run gunicorn with uvicorn.
The Nominatim frontend is best run from its own virtual environment. If
you have already created one for the database backend during the
installation, you can use that. Otherwise
create one now with:
sudo apt-get install virtualenv
virtualenv /srv/nominatim-venvThe Nominatim frontend is contained in the 'nominatim-api' package. To
install directly from the source tree run:
cd Nominatim
/srv/nominatim-venv/bin/pip install packaging/nominatim-apiThe recommended way to deploy a Python ASGI application is to run
the gunicorn HTTP server. We use
Falcon here as the web framework.
Add the necessary packages to your virtual environment:
/srv/nominatim-venv/bin/pip install falcon gunicornSetting up Nominatim as a systemd job
!!! Note
These instructions assume your Nominatim project directory is
located in /srv/nominatim-project. If you have put it somewhere else,
you need to adjust the commands and configuration accordingly.
Next you need to set up the service that runs the Nominatim frontend. This is
easiest done with a systemd job.
First you need to tell systemd to create a socket file to be used by
gunicorn. Create the following file /etc/systemd/system/nominatim.socket:
[Unit]
Description=Gunicorn socket for Nominatim
[Socket]
ListenStream=/run/nominatim.sock
SocketUser=www-data
[Install]
WantedBy=multi-user.targetNow you can add the systemd service for Nominatim itself.
Create the following file /etc/systemd/system/nominatim.service:
[Unit]
Description=Nominatim running as a gunicorn application
After=network.target
Requires=nominatim.socket
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/srv/nominatim-project
ExecStart=/srv/nominatim-venv/bin/gunicorn -b unix:/run/nominatim.sock -w 4 --worker-class asgi --protocol uwsgi --worker-connections 1000 "nominatim_api.server.falcon.server:run_wsgi()"
ExecReload=/bin/kill -s HUP $MAINPID
PrivateTmp=true
TimeoutStopSec=5
KillMode=mixed
[Install]
WantedBy=multi-user.targetThis sets up gunicorn with 4 workers (-w 4 in ExecStart). Each worker runs
its own Python process usingNOMINATIM_API_POOL_SIZE
connections to the database to serve requests in parallel. The parameter--worker-connections restricts how many requests gunicorn will queue for
each worker. This can help distribute work better when the server is under
high load.
Make the new services known to systemd and start it:
sudo systemctl daemon-reload
sudo systemctl enable nominatim.socket
sudo systemctl start nominatim.socket
sudo systemctl enable nominatim.service
sudo systemctl start nominatim.serviceThis sets the service up so that Nominatim is automatically started
on reboot.
Configuring nginx
To make the service available to the world, you need to proxy it through
nginx. We use the binary uwsgi protocol to speed up communication
between nginx and gunicorn. Add the following definition to the default
configuration:
upstream nominatim_service {
server unix:/run/nominatim.sock fail_timeout=0;
}
server {
listen 80;
listen [::]:80;
root /var/www/html;
index /search;
location / {
uwsgi_pass nominatim_service;
include uwsgi_params;
}
}Reload nginx with
sudo systemctl reload nginxand you should be able to see the status of your server underhttp://localhost/status.
Admin Faq (`docs/admin/Faq.md`)
Troubleshooting Nominatim Installations
Installation Issues
Can a stopped/killed import process be resumed?
"I accidentally killed the import process after it has been running for many hours. Can it be resumed?"
It is possible if the import already got to the indexing stage.
Check the last line of output that was logged before the process
was killed. If it looks like this:
Done 844 in 13 @ 64.923080 per second - Rank 26 ETA (seconds): 7.886255then you can resume with the following command:
nominatim import --continue indexingIf the reported rank is 26 or higher, you can also safely add --index-noanalyse.
PostgreSQL crashed "invalid page in block"
Usually serious problem, can be a hardware issue, not all data written to disc
for example. Check PostgreSQL log file and search PostgreSQL issues/mailing
list for hints.
If it happened during index creation you can try rerunning the step with
nominatim import --continue indexingOtherwise it's best to start the full setup from the beginning.
I see the error: "ERROR: mmap (remap) failed"
This may be a simple out-of-memory error. Try reducing the memory used
for --osm2pgsql-cache. Also make sure that overcommitting memory is
allowed: cat /proc/sys/vm/overcommit_memory should print 0 or 1.
If you are using a flatnode file, then it may also be that the underlying
filesystem does not fully support 'mmap'. A notable candidate is virtualbox's
vboxfs.
nominatim UPDATE failed: ERROR: buffer 179261 is not owned by resource owner Portal
Several users reported this
during the initial import of the database. It's
something PostgreSQL internal Nominatim doesn't control. And PostgreSQL forums
suggest it's threading related but definitely some kind of crash of a process.
Users reported either rebooting the server, different hardware or just trying
the import again worked.
The website shows: "Could not get word tokens"
The server cannot access your database. Add &debug=1 to your URL
to get the full error message.
Website reports "DB Error: insufficient permissions"
The user the webserver, e.g. Apache, runs under needs to have access to the
Nominatim database. You can find the user like
this,
for default Ubuntu operating system for example it's www-data.
Repeat the
createuserstep of the installation instructions.Give the user permission to existing tables
GRANT usage ON SCHEMA public TO "www-data";
GRANT SELECT ON ALL TABLES IN SCHEMA public TO "www-data";Setup fails with "DB Error: extension not found"
Make sure you have the PostgreSQL extensions "hstore" and "postgis" installed.
See the installation instructions for a full list of required packages.
UnicodeEncodeError: 'ascii' codec can't encode character
Make sure that the operating system's locale is UTF-8. With some prebuilt
images (e.g. LXC containers from Proxmox, see
discussion) or
images that optimize for size it might be missing.
On Ubuntu you can check the locale is installed:
grep UTF-8 /etc/default/localeAnd install it using
dpkg-reconfigure localesI forgot to delete the flatnodes file before starting an import.
That's fine. For each import the flatnodes file get overwritten.
See https://help.openstreetmap.org/questions/52419/nominatim-flatnode-storage
for more information.
Running your own instance
Can I import negative OSM ids into Nominatim?
No, negative IDs are no longer supported by osm2pgsql. You can use
large 64-bit IDs that are guaranteed not to clash with OSM IDs. However,
you will not able to use a flatnode file with them.
Admin Import (`docs/admin/Import.md`)
Importing the Database
The following instructions explain how to create a Nominatim database
from an OSM planet file. It is assumed that you have already successfully
installed the Nominatim software itself and the nominatim tool can be found
in your PATH. If this is not the case, return to the
installation page.
Creating the project directory
Before you start the import, you should create a project directory for your
new database installation. This directory receives all data that is related
to a single Nominatim setup: configuration, extra data, etc. Create a project
directory apart from the Nominatim software and change into the directory:
mkdir ~/nominatim-project
cd ~/nominatim-projectIn the following, we refer to the project directory as $PROJECT_DIR. To be
able to copy&paste instructions, you can export the appropriate variable:
export PROJECT_DIR=~/nominatim-projectThe Nominatim tool assumes per default that the current working directory is
the project directory but you may explicitly state a different directory using
the --project-dir parameter. The following instructions assume that you run
all commands from the project directory.
!!! tip "Migration Tip"
Nominatim used to be run directly from the build directory until version 3.6.
Essentially, the build directory functioned as the project directory
for the database installation. This setup still works and can be useful for
development purposes. It is not recommended anymore for production setups.
Create a project directory that is separate from the Nominatim software.Configuration setup in `.env`
The Nominatim server can be customized via an .env configuration file in the
project directory. This is a file in dotenv
format which looks the same as variable settings in a standard shell environment.
You can also set the same configuration via environment variables. All
settings have a NOMINATIM_ prefix to avoid conflicts with other environment
variables.
There are lots of configuration settings you can tweak. A full reference
can be found in the chapter Configuration Settings.
Most should have a sensible default.
Flatnode files
If you plan to import a large dataset (e.g. Europe, North America, planet),
you should also enable flatnode storage of node locations. With this
setting enabled, node coordinates are stored in a simple file instead
of the database. This will save you import time and disk storage.
Add to your .env:
NOMINATIM_FLATNODE_FILE="/path/to/flatnode.file"Replace the second part with a suitable path on your system and make sure
the directory exists. There should be at least 75GB of free space.
Downloading additional data
Wikipedia/Wikidata rankings
Wikipedia can be used as an optional auxiliary data source to help indicate
the importance of OSM features. Nominatim will work without this information
but it will improve the quality of the results if this is installed.
This data is available as a binary download. Put it into your project directory:
cd $PROJECT_DIR
wget https://nominatim.org/data/wikimedia-importance.csv.gz
wget -O secondary_importance.sql.gz https://nominatim.org/data/wikimedia-secondary-importance.sql.gzThe files are about 400MB and add around 4GB to the Nominatim database. For
more information about importance,
see Importance Customization.
!!! tip
If you forgot to download the wikipedia rankings, then you can
also add importances after the import. Download the SQL files, then
run nominatim refresh --wiki-data --secondary-importance --importance.
Updating importances for a planet will take a couple of hours.
External postcodes
Nominatim can use postcodes from an external source to improve searching with
postcodes. We provide precomputed postcodes sets for the US (using TIGER data)
and the UK (using the CodePoint OpenData set.
This data can be optionally downloaded into the project directory:
cd $PROJECT_DIR
wget https://nominatim.org/data/gb_postcodes.csv.gz
wget https://nominatim.org/data/us_postcodes.csv.gzYou can also add your own custom postcode sources, see
Customization of postcodes.
Choosing the data to import
In its default setup Nominatim is configured to import the full OSM data
set for the entire planet. Such a setup requires a powerful machine with
at least 64GB of RAM and around 900GB of SSD hard disks. Depending on your
use case there are various ways to reduce the amount of data imported. This
section discusses these methods. They can also be combined.
Using an extract
If you only need geocoding for a smaller region, then precomputed OSM extracts
are a good way to reduce the database size and import time.
Geofabrik offers extracts for most countries.
They even have daily updates which can be used with the update process described
in the next section. There are also
other providers for extracts.
Please be aware that some extracts are not cut exactly along the country
boundaries. As a result some parts of the boundary may be missing which means
that Nominatim cannot compute the areas for some administrative areas.
Dropping Data Required for Dynamic Updates
About half of the data in Nominatim's database is not really used for serving
the API. It is only there to allow the data to be updated from the latest
changes from OSM. For many uses these dynamic updates are not really required.
If you don't plan to apply updates, you can run the import with the--no-updates parameter. This will drop the dynamic part of the database as
soon as it is not required anymore.
You can also drop the dynamic part later using the following command:
nominatim freezeNote that you still need to provide for sufficient disk space for the initial
import. So this option is particularly interesting if you plan to transfer the
database or reuse the space later.
!!! warning
The data structure for updates are also required when adding additional data
after the import, for example TIGER housenumber data.
If you plan to use those, you must not use the --no-updates parameter.
Do a normal import, add the external data and once you are done with
everything run nominatim freeze.
Reverse-only Imports
If you only want to use the Nominatim database for reverse lookups or
if you plan to use the installation only for exports to a
photon database, then you can set up a database
without search indexes. Add --reverse-only to your setup command above.
This saves about 5% of disk space, import time won't be significant faster.
Filtering Imported Data
Nominatim normally sets up a full search database containing administrative
boundaries, places, streets, addresses and POI data. There are also other
import styles available which only read selected data:
- admin
Only import administrative boundaries and places. - street
Like the admin style but also adds streets. - address
Import all data necessary to compute addresses down to house number level. - full
Default style that also includes points of interest. - extratags
Like the full style but also adds most of the OSM tags into the extratags
column.
The style can be changed with the configuration NOMINATIM_IMPORT_STYLE.
To give you an idea of the impact of using the different styles, the table
below gives rough estimates of the final database size after import of a
2020 planet and after using the --drop option. It also shows the time
needed for the import on a machine with 64GB RAM, 4 CPUS and NVME disks.
Note that the given sizes are just an estimate meant for comparison of
style requirements. Your planet import is likely to be larger as the
OSM data grows with time.
| style | Import time | DB size | after drop |
|---|---|---|---|
| admin | 4h | 215 GB | 20 GB |
| street | 22h | 440 GB | 185 GB |
| address | 36h | 545 GB | 260 GB |
| full | 54h | 640 GB | 330 GB |
| extratags | 54h | 650 GB | 340 GB |
You can also customize the styles further.
A description of the style format
can be found in the customization guide.
Initial import of the data
!!! danger "Important"
First try the import with a small extract, for example from
Geofabrik.
Download the data to import. Then issue the following command
from the project directory to start the import:
nominatim import --osm-file <data file> 2>&1 | tee setup.logThe project directory is the one that you have set up at the beginning.
See creating the project directory.
Notes on full planet imports
Even on a perfectly configured machine
the import of a full planet takes around 2 days. Once you see messages
with Rank .. ETA appear, the indexing process has started. This part takes
the most time. There are 30 ranks to process. Rank 26 and 30 are the most complex.
They take each about a third of the total import time. If you have not reached
rank 26 after two days of import, it is worth revisiting your system
configuration as it may not be optimal for the import.
Notes on memory usage
In the first step of the import Nominatim uses osm2pgsql
to load the OSM data into the PostgreSQL database. This step is very demanding
in terms of RAM usage. osm2pgsql and PostgreSQL are running in parallel at
this point. PostgreSQL blocks at least the part of RAM that has been configured
with the shared_buffers parameter during
PostgreSQL tuning
and needs some memory on top of that. osm2pgsql needs at least 2GB of RAM for
its internal data structures, potentially more when it has to process very large
relations. In addition it needs to maintain a cache for node locations. The size
of this cache can be configured with the parameter --osm2pgsql-cache.
When importing with a flatnode file, it is best to disable the node cache
completely and leave the memory for the flatnode file. Nominatim will do this
by default, so you do not need to configure anything in this case.
For imports without a flatnode file, set --osm2pgsql-cache approximately to
the size of the OSM pbf file you are importing. The size needs to be given in
MB. Make sure you leave enough RAM for PostgreSQL and osm2pgsql as mentioned
above. If the system starts swapping or you are getting out-of-memory errors,
reduce the cache size or even consider using a flatnode file.
Testing the installation
Run this script to verify that all required tables and indices got created
successfully.
nominatim admin --check-databaseIf you have installed the nominatim-api package, then you can try out
your installation by executing a simple query on the command line:
nominatim search --query Berlinor, when you have a reverse-only installation:
nominatim reverse --lat 51 --lon 45If you want to run Nominatim as a service, make sure you have installed
the right packages as per Installation.
Testing the Python frontend
To run the test server against the Python frontend, you must choose a
web framework to use, either starlette or falcon. Make sure the appropriate
packages are installed. Then run
nominatim serveor, if you prefer to use Starlette instead of Falcon as webserver,
nominatim serve --engine starletteGo to http://localhost:8088/status and you should see the message OK.
You can also run a search query, e.g. http://localhost:8088/search?q=Berlin
or, for reverse-only installations a reverse query,
e.g. http://localhost:8088/reverse?lat=27.1750090510034&lon=78.04209025.
Do not use this test server in production.
To run Nominatim via webservers like Apache or nginx, please continue reading
Deploy the Python frontend.
Enabling search by category phrases
To be able to search for places by their type using
special phrases
you also need to import these key phrases like this:
nominatim special-phrases --import-from-wikiNote that this command downloads the phrases from the wiki link above. You
need internet access for the step.
You can also import special phrases from a csv file, for more
information please see the Customization part.
Admin Installation (`docs/admin/Installation.md`)
Basic Installation
This page contains generic installation instructions for Nominatim and its
prerequisites. There are also step-by-step instructions available for
the following operating systems:
These OS-specific instructions can also be found in executable form
in the vagrant/ directory.
Users have created instructions for other frameworks. We haven't tested those
and can't offer support.
Prerequisites
Software
For running Nominatim:
- PostgreSQL (13+)
- PostGIS (3.0+ will work, 3.2+ strongly recommended)
- osm2pgsql (1.8+)
- Python 3 (3.9+, 3.10+ in case of Windows)
Furthermore the following Python libraries are required:
These will be installed automatically when using pip installation.
For running continuous updates:
For running the Python frontend:
- SQLAlchemy (1.4.31+, below 2.1, with greenlet support)
- asyncpg (0.8+, only when using SQLAlchemy < 2.0)
- one of the following web frameworks:
- uvicorn
For dependencies for running tests and building documentation, see
the Development section.
Hardware
A minimum of 2GB of RAM is required or installation will fail. For a full
planet import 128GB of RAM or more are strongly recommended. Do not report
out of memory problems if you have less than 64GB RAM.
For a full planet install you will need at least 1TB of hard disk space.
Take into account that the OSM database is growing fast.
Fast disks are essential. Using NVME disks is recommended.
Even on a well configured machine the import of a full planet takes
around 2.5 days. When using traditional SSDs, 4-5 days are more realistic.
Tuning the PostgreSQL database
You might want to tune your PostgreSQL installation so that the later steps
make best use of your hardware. You should tune the following parameters in
your postgresql.conf file.
shared_buffers = 2GB
maintenance_work_mem = (10GB)
autovacuum_work_mem = 2GB
work_mem = (50MB)
synchronous_commit = off
max_wal_size = 1GB
checkpoint_timeout = 60min
checkpoint_completion_target = 0.9
random_page_cost = 1.0
wal_level = minimal
max_wal_senders = 0The numbers in brackets behind some parameters seem to work fine for
128GB RAM machine. Adjust to your setup. A higher number for max_wal_size
means that PostgreSQL needs to run checkpoints less often but it does require
the additional space on your disk.
Autovacuum must not be switched off because it ensures that the
tables are frequently analysed. If your machine has very little memory,
you might consider setting:
autovacuum_max_workers = 1and even reduce autovacuum_work_mem further. This will reduce the amount
of memory that autovacuum takes away from the import process.
Installing the latest release
Nominatim is easiest installed directly from Pypi. Make sure you have installed
osm2pgsql, PostgreSQL/PostGIS and libICU together with its header files.
Then you can install Nominatim with:
pip install nominatim-db nominatim-apiDownloading and building Nominatim
Downloading the latest release
You can download the latest release from nominatim.org.
The release contains all necessary files. Just unpack it.
Downloading the latest development version
If you want to install latest development version from github:
git clone https://github.com/osm-search/Nominatim.gitThe development version does not include the country grid. Download it separately:
wget -O Nominatim/data/country_osm_grid.sql.gz https://nominatim.org/data/country_grid.sql.gzBuilding Nominatim
Nominatim is easiest to run from its own virtual environment. To create one, run:
sudo apt-get install virtualenv
virtualenv /srv/nominatim-venvTo install Nominatim directly from the source tree into the virtual environment, run:
/srv/nominatim-venv/bin/pip install packaging/nominatim-{db,api}Now continue with importing the database.
Admin Maintenance (`docs/admin/Maintenance.md`)
This chapter describes the various operations the Nominatim database administrator
may use to clean and maintain the database. None of these operations is mandatory
but they may help improve the performance and accuracy of results.
Updating postcodes
Command: nominatim refresh --postcodes
Postcode centroids (aka 'calculated postcodes') are generated by looking at all
postcodes of a country, grouping them and calculating the geometric centroid.
There is currently no logic to deal with extreme outliers (typos or other
mistakes in OSM data). There is also no check if a postcodes adheres to a
country's format, e.g. if Swiss postcodes are 4 digits.
When running regular updates, postcodes results can be improved by running
this command on a regular basis. Note that only the postcode table and the
postcode search terms are updated. The postcode that is assigned to each place
is only updated when the place is updated.
The command takes around 70min to run on the planet and needs ca. 40GB of
temporary disk space.
Updating word counts
Command: nominatim refresh --word-counts
Nominatim keeps frequency statistics about all search terms it indexes. These
statistics are currently used to optimise queries to the database. Thus better
statistics mean better performance. Word counts are created once after import
and are usually sufficient even when running regular updates. You might want
to rerun the statistics computation when adding larger amounts of new data,
for example, when adding an additional country via nominatim add-data.
Forcing recomputation of places and areas
Command: nominatim refresh --data-object [NWR] --data-area [NWR]
When running replication updates, Nominatim tries to recompute the search
and address information for all places that are affected by a change. But it
needs to restrict the total number of changes to make sure it can keep up
with the minutely updates. Therefore it will refrain from propagating changes
that affect a lot of objects.
The administrator may force an update of places in the database.nominatim refresh --data-object invalidates a single OSM object.nominatim refresh --data-area invalidates an OSM object and all dependent
objects. That are usually the places that inside its area or around the
center of the object. Both commands expect the OSM object as an argument
of the form OSM type + OSM id. The type must be N (node), W (way) orR (relation).
After invalidating the object, indexing must be run again. If continuous
update are running in the background, the objects will be recomputed together
with the next round of updates. Otherwise you need to run nominatim index
to finish the recomputation.
Removing large deleted objects
Command: nominatim admin --clean-deleted
Nominatim refuses to delete very large areas because often these deletions are
accidental and are reverted within hours. Instead the deletions are logged in
the import_polygon_delete table and left to the administrator to clean up.
To run this command you will need to pass a PostgreSQL time interval. For example to
delete any objects that have been deleted more than a month ago you would run:nominatim admin --clean-deleted '1 month'
Admin Migration (`docs/admin/Migration.md`)
Database Migrations
Nominatim offers automatic migrations for versions 4.3+. Please follow
the following steps:
- Stop any updates that are potentially running
- Update the backend:
pip install -U nominatim-db - Go to your project directory and run
nominatim admin --migrate - Update the frontend:
pip install -U nominatim-api - (optionally) Restart updates
Below you find additional migrations and hints about other structural and
breaking changes. Please read them before running the migration.
!!! note
If you are migrating from a version <4.3, you need to install 4.3
and migrate to 4.3 first. Then you can migrate to the current
version. It is strongly recommended to do a reimport instead.
5.3.0 -> 5.4.0
Places are classified by categories now
Places have a new categories column on the place and placex tables. It
holds the main tags of a place as hierarchical labels of the formosm.<class>.<type>, see
Database layout. The migration
adds the column and fills it in placex from the class and type of the
existing rows.
The backfill only covers what the category search needs: the places that make
up an address above street level and the POI categories that have a special
phrase. A migrated database also keeps its old layout of one row per main tag.
Reimport if you want the new include and exclude filters of the
search API to find a place under all of
its main tags.
The place_classtype tables are not used anymore
Category search now runs against the categories column of placex, so the
per-category place_classtype_* tables are not created or updated anymore.
The migration leaves the existing tables in place because the old frontend
code still queries them until you have restarted it with the new version.
Once all your frontends run the new version, you can drop the tables to free
the disk space. On a planet database that is a couple of hundred tables and
several GB. To generate the necessary SQL, run:
SELECT format('DROP TABLE IF EXISTS %I;', tablename)
FROM pg_tables
WHERE schemaname = 'public' AND tablename LIKE 'place\_classtype\_%';The --min option of nominatim special-phrases only restricted which of
those tables were created. It has no effect anymore and is deprecated. It
will be removed in a future version.
5.2.0 -> 5.3.0
Expensive table migrations
This version introduces new tables for postcodes, interpolations and
associatedStreet relations. Migration is possible but will take a bit
longer. It is recommended to take Nominatim offline while migrating to 5.3.
5.1.0 -> 5.2.0
Lua import style: required extratags removed
Tags that are required by Nominatim as extratags are now always included
independent of what is defined in the style. The line
flex.add_for_extratags('required')is no longer required in custom styles and will throw an error. Simply
remove the line from your style.
4.5.0 -> 5.0.0
PHP frontend removed
The PHP frontend has been completely removed. Please switch to the Python
frontend.
Without the PHP code, the nominatim refresh --website command is no longer
needed. It currently omits a warning and does otherwise nothing. It will be
removed in later versions of Nominatim. So make sure you remove it from your
scripts.
CMake building removed
Nominatim can now only be installed via pip. Please follow the installation
instructions for the current version to change to pip.
osm2pgsql no longer vendored in
Nominatim no longer ships its own version of osm2pgsql. Please install a
stock version of osm2pgsql from your distribution. See the
installation instruction for osm2pgsql
for details. A minimum version of 1.8 is required. The current stable versions
of Ubuntu and Debian already ship with an appropriate versions. For older
installations, you may have to compile a newer osm2pgsql yourself.
Legacy tokenizer removed
The legacy tokenizer is no longer enabled. This tokenizer has been superseded
by the ICU tokenizer a long time ago. In the unlikely case that your database
still uses the legacy tokenizer, you must reimport your database.
osm2pgsql style overhauled
There are some fundamental changes to how customized osm2pgsql styles should
be written. The changes are mostly backwards compatible, i.e. custom styles
should still work with the new implementation. The only exception is a
customization of the process_tags() function. This function is no longer
considered public and neither are the helper functions used in it.
They currently still work but will be removed at some point. If you have
been making changes to process_tags, please review your style and try
to switch to the new convenience functions.
For more information on the changes, see the
pull request
and read the new
customization documentation.
4.4.0 -> 4.5.0
New structure for Python packages
The nominatim Python package has been split into nominatim-db and nominatim-api.
Any imports need to be adapted accordingly.
If you are running the Python frontend, change the server module fromnominatim.server.falcon.server to nominatim_api.server.falcon.server.
If you are using the Nominatim library, all imports need to be changed
from nominatim.api.<module> to nominatim_api.<module>.
If you have written custom tokenizers or sanitizers, the appropriate modules
are now found in nominatim_db.
4.2.0 -> 4.3.0
New indexes for reverse lookup
The reverse lookup algorithm has changed slightly to improve performance.
This change needs a different index in the database. The required index
will be automatically build during migration. Until the new index is available
performance of the /reverse endpoint is significantly reduced. You should
therefore either remove traffic from the machine before attempting a
version update or create the index manually before starting the update
using the following SQL:
CREATE INDEX IF NOT EXISTS idx_placex_geometry_reverse_lookupPlaceNode
ON placex USING gist (ST_Buffer(geometry, reverse_place_diameter(rank_search)))
WHERE rank_address between 4 and 25 AND type != 'postcode'
AND name is not null AND linked_place_id is null AND osm_type = 'N';4.0.0 -> 4.1.0
ICU tokenizer is the new default
Nominatim now installs the ICU tokenizer
by default. This only has an effect on newly installed databases. When
updating older databases, it keeps its installed tokenizer. If you still
run with the legacy tokenizer, make sure to compile Nominatim with the
PostgreSQL module, see Installation.
geocodejson output changed
The type field of the geocodejson output has changed. It now contains
the address class of the object instead of the value of the OSM tag. If
your client has used the type field, switch them to read osm_value
instead.
3.7.0 -> 4.0.0
NOMINATIM_PHRASE_CONFIG removed
Custom blacklist configurations for special phrases now need to be handed
with the --config parameter to nominatim special-phrases. Alternatively
you can put your custom configuration in the project directory in a file
named phrase-settings.json.
Version 3.8 also removes the automatic converter for the php format of
the configuration in older versions. If you are updating from Nominatim < 3.7
and still work with a custom phrase-settings.php, you need to manually
convert it into a json format.
PHP utils removed
The old PHP utils have now been removed completely. You need to switch to
the appropriate functions of the nominatim command line tool. See
Introducing nominatim command line tool
below.
3.6.0 -> 3.7.0
New format and name of configuration file
The configuration for an import is now saved in a .env file in the project
directory. This file follows the dotenv format. For more information, see
the installation chapter.
To migrate to the new system, create a new project directory, add the .env
file and port your custom configuration from settings/local.php. Most
settings are named similar and only have received a NOMINATIM_ prefix.
Use the default settings in settings/env.defaults as a reference.
New location for data files
External data files for Wikipedia importance, postcodes etc. are no longer
expected to reside in the source tree by default. Instead they will be searched
in the project directory. If you have an automated setup script you must
either adapt the download location or explicitly set the location of the
files to the old place in your .env.
Introducing `nominatim` command line tool
The various php utilities have been replaced with a single nominatim
command line tool. Make sure to adapt any scripts. There is no direct 1:1
matching between the old utilities and the commands of nominatim CLI. The
following list gives you a list of nominatim sub-commands that contain
functionality of each script:
- ./utils/setup.php:
import,freeze,refresh - ./utils/update.php:
replication,add-data,index,refresh - ./utils/specialphrases.php:
special-phrases - ./utils/check_import_finished.php:
admin - ./utils/warm.php:
admin - ./utils/export.php:
export
Try nominatim <command> --help for more information about each subcommand.
./utils/query.php no longer exists in its old form. nominatim search
provides a replacement but returns different output.
Switch to normalized house numbers
The housenumber column in the placex table uses now normalized version.
The automatic migration step will convert the column but this may take a
very long time. It is advisable to take the machine offline while doing that.
3.5.0 -> 3.6.0
Change of layout of search_name_* tables
The table need a different index for nearest place lookup. Recreate the
indexes using the following shell script:
for table in `psql -d nominatim -c "SELECT tablename FROM pg_tables WHERE tablename LIKE 'search_name_%'" -tA | grep -v search_name_blank`;
do
psql -d nominatim -c "DROP INDEX idx_${table}_centroid_place; CREATE INDEX idx_${table}_centroid_place ON ${table} USING gist (centroid) WHERE ((address_rank >= 2) AND (address_rank <= 25)); DROP INDEX idx_${table}_centroid_street; CREATE INDEX idx_${table}_centroid_street ON ${table} USING gist (centroid) WHERE ((address_rank >= 26) AND (address_rank <= 27))";
doneRemoval of html output
The debugging UI is no longer directly provided with Nominatim. Instead we
now provide a simple Javascript application. Please refer to
Setting up the Nominatim UI for details on how to
set up the UI.
The icons served together with the API responses have been moved to the
nominatim-ui project as well. If you want to keep the icon field in the
response, you need to set CONST_MapIcon_URL to the URL of the /mapicon
directory of nominatim-ui.
Change order during indexing
When reindexing places during updates, there is now a different order used
which needs a different database index. Create it with the following SQL command:
CREATE INDEX idx_placex_pendingsector_rank_address
ON placex
USING BTREE (rank_address, geometry_sector)
WHERE indexed_status > 0;You can then drop the old index with:
DROP INDEX idx_placex_pendingsector;Unused index
This index has been unused ever since the query using it was changed two years ago. Saves about 12GB on a planet installation.
DROP INDEX idx_placex_geometry_reverse_lookupPoint;Switching to dotenv
As part of the work changing the configuration format, the configuration for
the website is now using a separate configuration file. To create the
configuration file, run the following command after updating:
./utils/setup.php --setup-websiteUpdate SQL code
To update the SQL code to the leatest version run:
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions3.4.0 -> 3.5.0
New Wikipedia/Wikidata importance tables
The wikipedia_* tables have a new format that also includes references to
Wikidata. You need to update the computation functions and the tables as
follows:
- download the new Wikipedia tables as described in the import section
- reimport the tables:
./utils/setup.php --import-wikipedia-articles - update the functions:
./utils/setup.php --create-functions --enable-diff-updates - create a new lookup index:
CREATE INDEX idx_placex_wikidata
ON placex
USING BTREE ((extratags -> 'wikidata'))
WHERE extratags ? 'wikidata'
AND class = 'place'
AND osm_type = 'N'
AND rank_search < 26;- compute importance:
./utils/update.php --recompute-importance
The last step takes about 10 hours on the full planet.
Remove one function (it will be recreated in the next step):
DROP FUNCTION create_country(hstore,character varying);Finally, update all SQL functions:
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions3.3.0 -> 3.4.0
Reorganisation of location_area_country table
The table location_area_country has been optimized. You need to switch to the
new format when you run updates. While updates are disabled, run the following
SQL commands:
CREATE TABLE location_area_country_new AS
SELECT place_id, country_code, geometry FROM location_area_country;
DROP TABLE location_area_country;
ALTER TABLE location_area_country_new RENAME TO location_area_country;
CREATE INDEX idx_location_area_country_geometry ON location_area_country USING GIST (geometry);
CREATE INDEX idx_location_area_country_place_id ON location_area_country USING BTREE (place_id);Finally, update all SQL functions:
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions3.2.0 -> 3.3.0
New database connection string (DSN) format
Previously database connection setting (CONST_Database_DSN in settings/*.php) had the format
- (simple)
pgsql://@/nominatim - (complex)
pgsql://johndoe:[email protected]:1234/db1
The new format is
- (simple)
pgsql:dbname=nominatim - (complex)
pgsql:dbname=db1;host=machine1.domain.com;port=1234;user=johndoe;password=secret
Natural Earth country boundaries no longer needed as fallback
DROP TABLE country_naturalearthdata;Finally, update all SQL functions:
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functionsConfigurable Address Levels
The new configurable address levels require a new table. Create it with the
following command:
./utils/update.php --update-address-levels3.1.0 -> 3.2.0
New reverse algorithm
The reverse algorithm has changed and requires new indexes. Run the following
SQL statements to create the indexes:
CREATE INDEX idx_placex_geometry_reverse_lookupPoint
ON placex
USING gist (geometry)
WHERE (name IS NOT null or housenumber IS NOT null or rank_address BETWEEN 26 AND 27)
AND class NOT IN ('railway','tunnel','bridge','man_made')
AND rank_address >= 26
AND indexed_status = 0
AND linked_place_id IS null;
CREATE INDEX idx_placex_geometry_reverse_lookupPolygon
ON placex USING gist (geometry)
WHERE St_GeometryType(geometry) in ('ST_Polygon', 'ST_MultiPolygon')
AND rank_address between 4 and 25
AND type != 'postcode'
AND name is not null
AND indexed_status = 0
AND linked_place_id is null;
CREATE INDEX idx_placex_geometry_reverse_placeNode
ON placex USING gist (geometry)
WHERE osm_type = 'N'
AND rank_search between 5 and 25
AND class = 'place'
AND type != 'postcode'
AND name is not null
AND indexed_status = 0
AND linked_place_id is null;You also need to grant the website user access to the country_osm_grid table:
GRANT SELECT ON table country_osm_grid to "www-user";Replace the www-user with the user name of your website server if necessary.
You can now drop the unused indexes:
DROP INDEX idx_placex_reverse_geometry;Finally, update all SQL functions:
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions3.0.0 -> 3.1.0
Postcode Table
A new separate table for artificially computed postcode centroids was introduced.
Migration to the new format is possible but not recommended.
Create postcode table and indexes, running the following SQL statements:
CREATE TABLE location_postcode
(place_id BIGINT, parent_place_id BIGINT, rank_search SMALLINT,
rank_address SMALLINT, indexed_status SMALLINT, indexed_date TIMESTAMP,
country_code varchar(2), postcode TEXT,
geometry GEOMETRY(Geometry, 4326));
CREATE INDEX idx_postcode_geometry ON location_postcode USING GIST (geometry);
CREATE UNIQUE INDEX idx_postcode_id ON location_postcode USING BTREE (place_id);
CREATE INDEX idx_postcode_postcode ON location_postcode USING BTREE (postcode);
GRANT SELECT ON location_postcode TO "www-data";
DROP TYPE IF EXISTS nearfeaturecentr CASCADE;
CREATE TYPE nearfeaturecentr AS (
place_id BIGINT,
keywords int[],
rank_address smallint,
rank_search smallint,
distance float,
isguess boolean,
postcode TEXT,
centroid GEOMETRY
);Add postcode column to location_area tables with SQL statement:
ALTER TABLE location_area ADD COLUMN postcode TEXT;Then reimport the functions:
./utils/setup.php --create-functions --enable-diff-updates --create-partition-functionsCreate appropriate triggers with SQL:
CREATE TRIGGER location_postcode_before_update BEFORE UPDATE ON location_postcode
FOR EACH ROW EXECUTE PROCEDURE postcode_update();Finally populate the postcode table (will take a while):
./utils/setup.php --calculate-postcodes --index --index-noanalyseThis will create a working database. You may also delete the old artificial
postcodes now. Note that this may be expensive and is not absolutely necessary.
The following SQL statement will remove them:
DELETE FROM place_addressline a USING placex p
WHERE a.address_place_id = p.place_id and p.osm_type = 'P';
ALTER TABLE placex DISABLE TRIGGER USER;
DELETE FROM placex WHERE osm_type = 'P';
ALTER TABLE placex ENABLE TRIGGER USER;Admin Setup Nominatim UI (`docs/admin/Setup-Nominatim-UI.md`)
Setting up the Nominatim UI
Nominatim is a search API, it does not provide a website interface on its
own. nominatim-ui offers a
small website for testing your setup and inspecting the database content.
This section provides a quick start how to use nominatim-ui with your
installation. For more details, please also have a look at the
README of nominatim-ui.
Installing nominatim-ui
We provide regular releases of nominatim-ui that contain the packaged website.
They do not need any special installation. Just download, configure
and run it. Grab the latest release from
nominatim-ui's Github release page
and unpack it. You can use nominatim-ui-x.x.x.tar.gz or nominatim-ui-x.x.x.zip.
Next you need to adapt the UI to your installation. Custom settings need to be
put into dist/theme/config.theme.js. At a minimum you need to
set Nominatim_API_Endpoint to point to your Nominatim installation:
cd nominatim-ui
echo "Nominatim_Config.Nominatim_API_Endpoint='https://myserver.org/nominatim/';" > dist/theme/config.theme.jsFor the full set of available settings, have a look at dist/config.defaults.js.
Then you can just test it locally by spinning up a webserver in the dist
directory. For example, with Python:
cd nominatim-ui/dist
python3 -m http.server 8765The website is now available at http://localhost:8765.
Forwarding searches to nominatim-ui
Nominatim used to provide the search interface directly by itself whenformat=html was requested. For the /search endpoint this even used
to be the default.
The following section describes how to set up Apache or nginx, so that your
users are forwarded to nominatim-ui when they go to a URL that formerly presented
the UI.
Setting up forwarding in Nginx
First of all make nominatim-ui available under /ui on your webserver:
server {
# Here is the Nominatim setup as described in the Installation section
location /ui/ {
alias <full path to the nominatim-ui directory>/dist/;
index index.html;
}
}Now we need to find out if a URL should be forwarded to the UI. Add the
following map commands outside the server section:
# Inspect the format parameter in the query arguments. We are interested
# if it is set to html or something else or if it is missing completely.
map $args $format {
default default;
~(^|&)format=html(&|$) html;
~(^|&)format= other;
}
# Determine from the URI and the format parameter above if forwarding is needed.
map $uri/$format $forward_to_ui {
default 0; # no forwarding by default
~/search.*/default 1; # Use this line only, if search should go to UI by default.
~/reverse.*/html 1; # Forward API calls that UI supports, when
~/status.*/html 1; # format=html is explicitly requested.
~/search.*/html 1;
~/details.*/html 1;
}The $forward_to_ui parameter can now be used to conditionally forward the
calls:
location / {
if ($forward_to_ui) {
rewrite ^(/[^/.]*) https://$http_host/ui$1.html redirect;
}
# proxy_pass commands
}Reload nginx and the UI should be available.
Setting up forwarding in Apache
First of all make nominatim-ui available in the ui/ subdirectory where
Nominatim is installed. For example, given you have set up an alias undernominatim like this:
Alias /nominatim /home/vagrant/build/websiteyou need to insert the following rules for nominatim-ui before that alias:
<Directory "/home/vagrant/nominatim-ui/dist">
DirectoryIndex search.html
Require all granted
</Directory>
Alias /nominatim/ui /home/vagrant/nominatim-ui/distReplace /home/vagrant/nominatim-ui with the directory where you have cloned
nominatim-ui.
!!! important
The alias for nominatim-ui must come before the alias for the Nominatim
website directory.
To set up forwarding, the Apache rewrite module is needed. Enable it with:
sudo a2enmod rewriteThen add rewrite rules to the Directory directive of the Nominatim website
directory like this:
<Directory "/home/vagrant/build/website">
Options FollowSymLinks MultiViews
AddType text/html .php
Require all granted
RewriteEngine On
# This must correspond to the URL where nominatim can be found.
RewriteBase "/nominatim/"
# If no endpoint is given, then use search.
RewriteRule ^(/|$) "search"
# If format-html is explicitly requested, forward to the UI.
RewriteCond %{QUERY_STRING} "format=html"
RewriteRule ^([^/.]+) ui/$1.html [R,END]
# Optionally: if no format parameter is there then forward /search.
RewriteCond %{QUERY_STRING} "!format="
RewriteCond %{REQUEST_URI} "/search"
RewriteRule ^([^/.]+) ui/$1.html [R,END]
</Directory>Restart Apache and the UI should be available.
Admin Update (`docs/admin/Update.md`)
Updating the Database
There are many different ways to update your Nominatim database.
The following section describes how to keep it up-to-date using
an online replication service for OpenStreetMap data
For a list of other methods to add or update data see the output ofnominatim add-data --help.
!!! important
If you have configured a flatnode file for the import, then you
need to keep this flatnode file around for updates.
Installing the newest version of Pyosmium
The replication process uses
Pyosmium
to download update data from the server.
It is recommended to install Pyosmium via pip.
Run (as the same user who will later run the updates):
pip3 install --user osmiumSetting up the update process
Next the update process needs to be initialised. By default Nominatim is configured
to update using the global minutely diffs.
If you want a different update source you will need to add some settings
to .env. For example, to use the daily country extracts
diffs for Ireland from Geofabrik add the following:
# base URL of the replication service
NOMINATIM_REPLICATION_URL="https://download.geofabrik.de/europe/ireland-and-northern-ireland-updates"
# How often upstream publishes diffs (in seconds)
NOMINATIM_REPLICATION_UPDATE_INTERVAL=86400
# How long to sleep if no update found yet (in seconds)
NOMINATIM_REPLICATION_RECHECK_INTERVAL=900To set up the update process now run the following command:
nominatim replication --initIt outputs the date where updates will start. Recheck that this date is
what you expect.
The replication --init command needs to be rerun whenever the replication
service is changed.
Updating Nominatim
Nominatim supports different modes how to retrieve the update data from the
server. Which one you want to use depends on your exact setup and how often you
want to retrieve updates.
These instructions are for using a single source of updates. If you have
imported multiple country extracts and want to keep them
up-to-date, Advanced installations section
contains instructions to set up and update multiple country extracts.
One-time mode
When the --once parameter is given, then Nominatim will download exactly one
batch of updates and then exit. This one-time mode still respects theNOMINATIM_REPLICATION_UPDATE_INTERVAL that you have set. If according to
the update interval no new data has been published yet, it will go to sleep
until the next expected update and only then attempt to download the next batch.
The one-time mode is particularly useful if you want to run updates continuously
but need to schedule other work in between updates. For example, you might
want to regularly recompute postcodes -- a process that
must not be run while updates are in progress. An update script refreshing
postcodes regularly might look like this:
#!/bin/bash
# Switch to your project directory.
cd /srv/nominatim
while true; do
nominatim replication --once
if [ -f "/srv/nominatim/schedule-maintenance" ]; then
rm /srv/nominatim/schedule-maintenance
nominatim refresh --postcodes
fi
doneA cron job then creates the file /srv/nominatim/schedule-maintenance once per night.
One-time mode with systemd
You can run the one-time mode with a systemd timer & service.
Create a timer description like /etc/systemd/system/nominatim-updates.timer:
[Unit]
Description=Timer to start updates of Nominatim
[Timer]
OnActiveSec=2
OnUnitActiveSec=1min
Unit=nominatim-updates.service
[Install]
WantedBy=multi-user.targetOnUnitActiveSec defines how often the individual update command is run.
Then add a service definition for the timer in /etc/systemd/system/nominatim-updates.service:
[Unit]
Description=Single updates of Nominatim
[Service]
WorkingDirectory=/srv/nominatim-project
ExecStart=/srv/nominatim-venv/bin/nominatim replication --once
StandardOutput=journal
StandardError=inherit
User=nominatim
Group=nominatim
Type=simple
[Install]
WantedBy=multi-user.targetReplace the WorkingDirectory with your project directory. ExecStart points
to the nominatim binary that was installed in your virtualenv earlier.
Finally, you might need to adapt user and group names as required.
Now activate the service and start the updates:
sudo systemctl daemon-reload
sudo systemctl enable nominatim-updates.timer
sudo systemctl start nominatim-updates.timerYou can stop future data updates while allowing any current, in-progress
update steps to finish, by running sudo systemctl stop nominatim-updates.timer and waiting until nominatim-updates.service isn't
running (sudo systemctl is-active nominatim-updates.service).
To check the output from the update process, use journalctl: journalctl -u nominatim-updates.service
Catch-up mode
With the --catch-up parameter, Nominatim will immediately try to download
all changes from the server until the database is up-to-date. The catch-up mode
still respects the parameter NOMINATIM_REPLICATION_MAX_DIFF. It downloads and
applies the changes in appropriate batches until all is done.
The catch-up mode is foremost useful to bring the database up to date after the
initial import. Give that the service usually is not in production at this
point, you can temporarily be a bit more generous with the batch size and
number of threads you use for the updates by running catch-up like this:
cd /srv/nominatim-project
NOMINATIM_REPLICATION_MAX_DIFF=5000 nominatim replication --catch-up --threads 15The catch-up mode is also useful when you want to apply updates at a lower
frequency than what the source publishes. You can set up a cron job to run
replication catch-up at whatever interval you desire.
!!! hint
When running scheduled updates with catch-up, it is a good idea to choose
a replication source with an update frequency that is an order of magnitude
lower. For example, if you want to update once a day, use an hourly updated
source. This ensures that you don't miss an entire day of updates when
the source is unexpectedly late to publish its update.
If you want to use the source with the same update frequency (e.g. a daily
updated source with daily updates), use the
once mode together with a frequently run systemd script as described above.
It ensures to re-request the newest update until they have been published.Continuous updates
!!! danger
This mode is no longer recommended to use and will removed in future
releases. systemd is much better
suited for running regular updates. Please refer to the setup
instructions for running one-time mode with systemd above.
This is the easiest mode. Simply run the replication command without any
parameters:
nominatim replicationThe update application keeps running forever and retrieves and applies
new updates from the server as they are published.
Api Details (`docs/api/Details.md`)
Place details
Show all details about a single place saved in the database.
This API endpoint is meant for visual inspection of the data in the database,
mainly together with Nominatim-UI.
The parameters of the endpoint and the output may change occasionally between
versions of Nominatim. Do not rely on the output in scripts or applications.
!!! warning
The details endpoint at https://nominatim.openstreetmap.org
may not used in scripts or bots at all.
See Nominatim Usage Policy.
The details API supports the following three request formats:
https://nominatim.openstreetmap.org/details?osmtype=[N|W|R]&osmid=<value>&class=<value>osmtype and osmid are required parameters. The type is one of node (N), way (W)
or relation (R). The id must be a number. The class parameter is optional and
only kept for backwards compatibility. An OSM object has a single entry in
Nominatim, even when it is tagged with more than one main tag. A place tagged
with tourism=hotel and amenity=restaurant is therefore returned as one
result, classified as a restaurant. When class is given, then it must be
equal to the class of that entry or the lookup returns no result.
Databases that were migrated from a version before 5.4 may still hold one entry
per main tag. There the class parameter selects between them and, when it is
left out, one of the entries is chosen at random and displayed.
https://nominatim.openstreetmap.org/details?place_id=<value>Place IDs are assigned sequentially during Nominatim data import. The ID
for a place is different between Nominatim installation (servers) and
changes when data gets reimported. Therefore it cannot be used as
a permanent id and shouldn't be used in bug reports.
https://nominatim.openstreetmap.org/details?postcode=<country_code>:<postcode_id>Artificial postcodes do not always have an OSM object reference. For these,
Nominatim provides a stable postcode reference that combines the country code
and postcode. Spaces in the postcode id are replaced with underscores. For
example, us:94110 refers to postcode 94110 in the United States andgb:EH4_7EA refers to postcode EH4 7EA in Great Britain.
!!! danger "Deprecation warning"
The API can also be used with the URL
https://nominatim.openstreetmap.org/details.php. This is now deprecated
and will be removed in future versions.
Parameters
This section lists additional optional parameters.
Output format
| Parameter | Value | Default |
|---|---|---|
| json_callback | function name | unset |
When set, then JSON output will be wrapped in a callback function with
the given name. See JSONP for more
information.
Output details
| Parameter | Value | Default |
|---|---|---|
| addressdetails | 0 or 1 | 0 |
When set to 1, include a breakdown of the address into elements.
| Parameter | Value | Default |
|---|---|---|
| keywords | 0 or 1 | 0 |
When set to 1, include a list of name keywords and address keywords
in the result.
| Parameter | Value | Default |
|---|---|---|
| linkedplaces | 0 or 1 | 1 |
Include details of places that are linked with this one. Places get linked
together when they are different forms of the same physical object. Nominatim
links two kinds of objects together: place nodes get linked with the
corresponding administrative boundaries. Waterway relations get linked together with their
members.
| Parameter | Value | Default |
|---|---|---|
| hierarchy | 0 or 1 | 0 |
Include details of POIs and address that depend on the place. Only POIs
that use this place to determine their address will be returned.
| Parameter | Value | Default |
|---|---|---|
| group_hierarchy | 0 or 1 | 0 |
When set to 1, the output of the address hierarchy will be
grouped by type.
| Parameter | Value | Default |
|---|---|---|
| polygon_geojson | 0 or 1 | 0 |
Include geometry of result.
| Parameter | Value | Default |
|---|---|---|
| entrances | 0 or 1 | 0 |
When set to 1, include the tagged entrances in the result.
Language of results
| Parameter | Value | Default |
|---|---|---|
| accept-language | browser language string | content of "Accept-Language" HTTP header |
Preferred language order for showing search results. This may either be
a simple comma-separated list of language codes or have the same format
as the "Accept-Language" HTTP header.
Other
| Parameter | Value | Default |
|---|---|---|
| debug | 0 or 1 | 0 |
Output special developer debug information instead of the normal result.
See Debug output for details.
Examples
JSON
https://nominatim.openstreetmap.org/details?osmtype=W&osmid=38210407&format=json
{
"place_id": 85993608,
"parent_place_id": 72765313,
"osm_type": "W",
"osm_id": 38210407,
"category": "place",
"type": "square",
"admin_level": "15",
"localname": "Pariser Platz",
"names": {
"name": "Pariser Platz",
"name:be": "Парыжская плошча",
"name:de": "Pariser Platz",
"name:es": "Plaza de París",
"name:he": "פאריזר פלאץ",
"name:ko": "파리저 광장",
"name:la": "Forum Parisinum",
"name:ru": "Парижская площадь",
"name:uk": "Паризька площа",
"name:zh": "巴黎廣場"
},
"addresstags": {
"postcode": "10117"
},
"housenumber": null,
"calculated_postcode": "10117",
"country_code": "de",
"indexed_date": "2018-08-18T17:02:45+00:00",
"importance": 0.339401620591472,
"calculated_importance": 0.339401620591472,
"extratags": {
"wikidata": "Q156716",
"wikipedia": "de:Pariser Platz"
},
"calculated_wikipedia": "de:Pariser_Platz",
"rank_address": 30,
"rank_search": 30,
"isarea": true,
"centroid": {
"type": "Point",
"coordinates": [
13.3786822618517,
52.5163654
]
},
"geometry": {
"type": "Point",
"coordinates": [
13.3786822618517,
52.5163654
]
}
}Api Faq (`docs/api/Faq.md`)
Frequently Asked Questions
API Results
1. The address of my search results contains far-away places that don't belong there.
Nominatim computes the address from two sources in the OpenStreetMap data:
from administrative boundaries and from place nodes. Boundaries are the more
useful source. They precisely describe an area. So it is very clear for
Nominatim if a point belongs to an area or not. Place nodes are more complicated.
These are only points without any precise extent. So Nominatim has to take a
guess and assume that an address belongs to the closest place node it can find.
In an ideal world, Nominatim would not need the place nodes but there are
many places on earth where there are no precise boundaries available for
all parts that make up an address. This is in particular true for the more
local address parts, like villages and suburbs. Therefore it is not possible
to completely dismiss place nodes. And sometimes they sneak in where they
don't belong.
As a OpenStreetMap mapper, you can improve the situation in two ways: if you
see a place node for which already an administrative area exists, then you
should link the two by adding the node with a 'label' role to the boundary
relation. If there is no administrative area, you can add the approximate
extent of the place and tag it place=
2. When doing reverse search, the address details have parts that don't contain the point I was looking up.
There is a common misconception how the reverse API call works in Nominatim.
Reverse does not give you the address of the point you asked for. Reverse
returns the closest object to the point you asked for and then returns the
address of that object. Now, if you are close to a border, then the closest
object may be across that border. When Nominatim then returns the address,
it contains the county/state/country across the border.
3. I get different counties/states/countries when I change the zoom parameter in the reverse query. How is that possible?
This is basically the same problem as in the previous answer.
The zoom level influences at which search rank Nominatim starts looking
for the closest object. So the closest house number maybe on one side of the
border while the closest street is on the other. As the address details contain
the address of the closest object found, you might sometimes get one result,
sometimes the other for the closest point.
4. Can you return the continent?
Nominatim assigns each map feature one country. Those outside any administrative
boundaries are assigned a special no-country. Continents or other super-national
administrations (e.g. European Union, NATO, Custom unions) are not supported,
see also Administrative Boundary.
5. Can you return the timezone?
See this separate OpenStreetMap-based project Timezone Boundary Builder.
6. I want to download a list of streets/restaurants of a city/region
The Overpass API is more
suited for these kinds of queries.
That said if you installed your own Nominatim instance you can use thenominatim export PHP script as basis to return such lists.
7. My result has a wrong postcode. Where does it come from?
Most places in OSM don't have a postcode, so Nominatim tries to interpolate
one. It first look at all the places that make up the address of the place.
If one of them has a postcode defined, this is the one to be used. When
none of the address parts has a postcode either, Nominatim interpolates one
from the surrounding objects. If the postcode is for your result is one, then
most of the time there is an OSM object with the wrong postcode nearby.
To find the bad postcode, go to
https://nominatim.openstreetmap.org
and search for your place. When you have found it, click on the 'details' link
under the result to go to the details page. There is a field 'Computed Postcode'
which should display the bad postcode. Click on the 'how?' link. A small
explanation text appears. It contains a link to a query for Overpass Turbo.
Click on that and you get a map with all places in the area that have the bad
postcode. If none is displayed, zoom the map out a bit and then click on 'Run'.
Now go to OpenStreetMap and fix the error you
have just found. It will take at least a day for Nominatim to catch up with
your data fix. Sometimes longer, depending on how much editing activity is in
the area.
Api Lookup (`docs/api/Lookup.md`)
Address lookup
The lookup API allows to query the address and other details of one or
multiple OSM objects like node, way or relation.
Endpoint
The lookup API has the following format:
https://nominatim.openstreetmap.org/lookup?osm_ids=[N|W|R]<value>,…,…,&<params>osm_ids is mandatory and must contain a comma-separated list of OSM ids each
prefixed with its type, one of node(N), way(W) or relation(R). Up to 50 ids
can be queried at the same time.
!!! danger "Deprecation warning"
The API can also be used with the URL
https://nominatim.openstreetmap.org/lookup.php. This is now deprecated
and will be removed in future versions.
Parameters
This section lists additional optional parameters.
Output format
| Parameter | Value | Default |
|---|---|---|
| format | one of: xml, json, jsonv2, geojson, geocodejson |
jsonv2 |
See Place Output Formats for details on each format.
| Parameter | Value | Default |
|---|---|---|
| json_callback | function name | unset |
When given, then JSON output will be wrapped in a callback function with
the given name. See JSONP for more
information.
Only has an effect for JSON output formats.
Output details
| Parameter | Value | Default |
|---|---|---|
| addressdetails | 0 or 1 | 1 |
When set to 1, include a breakdown of the address into elements.
The exact content of the address breakdown depends on the output format.
!!! tip
If you are interested in a stable classification of address categories
(suburb, city, state, etc), have a look at the geocodejson format.
All other formats return classifications according to OSM tagging.
There is a much larger set of categories and they are not always consistent,
which makes them very hard to work with.
| Parameter | Value | Default |
|---|---|---|
| extratags | 0 or 1 | 0 |
When set to 1, the response include any additional information in the result
that is available in the database, e.g. wikipedia link, opening hours.
| Parameter | Value | Default |
|---|---|---|
| namedetails | 0 or 1 | 0 |
When set to 1, include a full list of names for the result. These may include
language variants, older names, references and brand.
| Parameter | Value | Default |
|---|---|---|
| entrances | 0 or 1 | 0 |
When set to 1, include the tagged entrances in the result.
Language of results
| Parameter | Value | Default |
|---|---|---|
| accept-language | browser language string | content of "Accept-Language" HTTP header |
Preferred language order for showing search results. This may either be
a simple comma-separated list of language codes or have the same format
as the "Accept-Language" HTTP header.
!!! tip
First-time users of Nominatim tend to be confused that they get different
results when using Nominatim in the browser versus in a command-line tool
like wget or curl. The command-line tools
usually don't send any Accept-Language header, prompting Nominatim
to show results in the local language. Browsers on the contrary always
send the currently chosen browser language.
Polygon output
| Parameter | Value | Default |
|---|---|---|
| polygon_geojson | 0 or 1 | 0 |
| polygon_kml | 0 or 1 | 0 |
| polygon_svg | 0 or 1 | 0 |
| polygon_text | 0 or 1 | 0 |
Add the full geometry of the place to the result output. Output formats
in GeoJSON, KML, SVG or WKT are supported. Only one of these
options can be used at a time.
| Parameter | Value | Default |
|---|---|---|
| polygon_threshold | floating-point number | 0.0 |
When one of the polygon_* outputs is chosen, return a simplified version
of the output geometry. The parameter describes the
tolerance in degrees with which the geometry may differ from the original
geometry. Topology is preserved in the geometry.
Other
| Parameter | Value | Default |
|---|---|---|
| valid email address | unset |
If you are making large numbers of request please include an appropriate email
address to identify your requests. See Nominatim's
Usage Policy for more details.
| Parameter | Value | Default |
|---|---|---|
| debug | 0 or 1 | 0 |
Output special developer debug information instead of the normal result.
See Debug output for details.
Examples
XML
https://nominatim.openstreetmap.org/lookup?osm_ids=R146656,W104393803,N240109189
/* Detailed source-code truncated for AI context efficiency. */JSON with extratags
https://nominatim.openstreetmap.org/lookup?osm_ids=W50637691&format=json&extratags=1
/* Detailed source-code truncated for AI context efficiency. */Api Output (`docs/api/Output.md`)
Place Output
The /reverse, /search and /lookup
API calls produce very similar output which is explained in this section.
There is one section for each format. The format correspond to what was
selected via the format parameter.
JSON
The JSON format returns an array of places (for search and lookup) or
a single place (for reverse) of the following format:
{
"place_id": 100149,
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
"osm_type": "node",
"osm_id": "107775",
"boundingbox": ["51.3473219", "51.6673219", "-0.2876474", "0.0323526"],
"lat": "51.5073219",
"lon": "-0.1276474",
"display_name": "London, Greater London, England, SW1A 2DU, United Kingdom",
"class": "place",
"type": "city",
"importance": 0.9654895765402,
"icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png",
"address": {
"city": "London",
"state_district": "Greater London",
"state": "England",
"ISO3166-2-lvl4": "GB-ENG",
"postcode": "SW1A 2DU",
"country": "United Kingdom",
"country_code": "gb"
},
"extratags": {
"capital": "yes",
"website": "http://www.london.gov.uk",
"wikidata": "Q84",
"wikipedia": "en:London",
"population": "8416535"
}
}The possible fields are:
place_id- reference to the Nominatim internal database ID (see notes)osm_type,osm_id- reference to the OSM object (see notes)boundingbox- area of corner coordinates (see notes)lat,lon- latitude and longitude of the centroid of the objectdisplay_name- full comma-separated addressclass,type- key and value of the main OSM tagimportance- computed importance rankicon- link to class icon (if available)address- dictionary of address details (only withaddressdetails=1,
see notes)extratags- dictionary with additional useful tags like website or maxspeed
(only withextratags=1)namedetails- dictionary with full list of available names including ref etc.geojson,svg,geotext,geokml- full geometry
(only with the appropriatepolygon_*parameter)entrances- array of objects representing tagged entrances for the object, or
null if none are found (only withentrances=1)
JSONv2
This is the same as the JSON format with two changes:
classrenamed tocategory- additional field
place_rankwith the search rank of the object
GeoJSON
This format follows the RFC7946. Every feature includes
a bounding box (bbox).
The properties object has the following fields:
place_id- reference to the Nominatim internal database ID (see notes)osm_type,osm_id- reference to the OSM object (see notes)category,type- key and value of the main OSM tagdisplay_name- full comma-separated addressplace_rank- class search rankimportance- computed importance rankicon- link to class icon (if available)address- dictionary of address details (only withaddressdetails=1,
see notes)extratags- dictionary with additional useful tags likewebsiteormaxspeed
(only withextratags=1)namedetails- dictionary with full list of available names including ref etc.entrances- array of objects representing tagged entrances for the object, or
null if none are found (only withentrances=1)
Use polygon_geojson to output the full geometry of the object instead
of the centroid.
GeocodeJSON
The GeocodeJSON format follows the
GeocodeJSON spec 0.1.0.
The following feature attributes are implemented:
osm_type,osm_id- reference to the OSM object (unofficial extension, see notes)type- the 'address level' of the object ('house', 'street',district,city,
county,state,country,locality)osm_key- key of the main tag of the OSM object (e.g. boundary, highway, amenity)osm_value- value of the main tag of the OSM object (e.g. residential, restaurant)label- full comma-separated addressname- localised name of the placehousenumber,street,locality,district,postcode,city,county,state,country-
provided when it can be determined from the address (only withaddressdetails=1)admin- list of localised names of administrative boundaries (only withaddressdetails=1)extra- dictionary with additional useful tags likewebsiteormaxspeed
(only withextratags=1)entrances- array of objects representing tagged entrances for the object, or
null if none are found (only withentrances=1)
Use polygon_geojson to output the full geometry of the object instead
of the centroid.
XML
The XML response returns one or more place objects in slightly different
formats depending on the API call.
Reverse
<reversegeocode timestamp="Sat, 11 Aug 18 11:53:21 +0000"
attribution="Data © OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright"
querystring="lat=48.400381&lon=11.745876&zoom=5&format=xml">
<result place_id="179509537" osm_type="relation" osm_id="2145268" ref="BY" place_rank="15" address_rank="15"
lat="48.9467562" lon="11.4038717"
boundingbox="47.2701114,50.5647142,8.9763497,13.8396373">
Bavaria, Germany
</result>
<addressparts>
<state>Bavaria</state>
<ISO3166-2-lvl4>DE-BY</ISO3166-2-lvl4>
<country>Germany</country>
<country_code>de</country_code>
</addressparts>
<extratags>
<tag key="place" value="state"/>
<tag key="wikidata" value="Q980"/>
<tag key="wikipedia" value="de:Bayern"/>
<tag key="population" value="12520000"/>
<tag key="name:prefix" value="Freistaat"/>
</extratags>
</reversegeocode>The attributes of the outer reversegeocode element return generic information
about the query, including the time when the response was sent (in UTC),
attribution to OSM and the original querystring.
The place information can be found in the result element. The attributes of that element contain:
place_id- reference to the Nominatim internal database ID (see notes)osm_type,osm_id- reference to the OSM object (see notes)ref- content ofreftag if it existslat,lon- latitude and longitude of the centroid of the objectboundingbox- comma-separated list of corner coordinates (see notes)
The full address of the result can be found in the content of theresult element as a comma-separated list.
Additional information requested with addressdetails=1, extratags=1,namedetails=1, and entrances=1 can be found in extra elements.
Search and Lookup
<searchresults timestamp="Sat, 11 Aug 18 11:55:35 +0000"
attribution="Data © OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright"
querystring="london" polygon="false" exclude_place_ids="N107775"
more_url="https://nominatim.openstreetmap.org/search?q=london&addressdetails=1&extratags=1&exclude_place_ids=N107775&format=xml&accept-language=en-US%2Cen%3Bq%3D0.7%2Cde%3Bq%3D0.3">
<place place_id="100149" osm_type="node" osm_id="107775" place_rank="15" address_rank="15"
boundingbox="51.3473219,51.6673219,-0.2876474,0.0323526" lat="51.5073219" lon="-0.1276474"
display_name="London, Greater London, England, SW1A 2DU, United Kingdom"
class="place" type="city" importance="0.9654895765402"
icon="https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png">
<extratags>
<tag key="capital" value="yes"/>
<tag key="website" value="http://www.london.gov.uk"/>
<tag key="wikidata" value="Q84"/>
<tag key="wikipedia" value="en:London"/>
<tag key="population" value="8416535"/>
</extratags>
<city>London</city>
<state_district>Greater London</state_district>
<state>England</state>
<ISO3166-2-lvl4>GB-ENG</ISO3166-2-lvl4>
<postcode>SW1A 2DU</postcode>
<country>United Kingdom</country>
<country_code>gb</country_code>
</place>
</searchresults>The attributes of the outer searchresults or lookupresults element return
generic information about the query:
timestamp- UTC time when the response was sentattribution- OSM licensing informationquerystring- original querypolygon- true when extra geometry information was requestedexclude_place_ids- IDs of places that should be ignored in a follow-up request (OSM IDs where possible, otherwiseplace_ids)more_url- search call that will yield additional results for the query
just sent
The place information can be found in the place elements, of which there may
be more than one. The attributes of that element contain:
place_id- reference to the Nominatim internal database ID (see notes)osm_type,osm_id- reference to the OSM object (see notes)ref- content ofreftag if it existslat,lon- latitude and longitude of the centroid of the objectboundingbox- comma-separated list of corner coordinates (see notes)place_rank- class search rankaddress_rank- place address rankdisplay_name- full comma-separated addressclass,type- key and value of the main OSM tagimportance- computed importance rankicon- link to class icon (if available)
When addressdetails=1 is requested, the localised address parts appear
as subelements with the type of the address part.
Additional information requested with extratags=1, namedetails=1, andentrances=1 can be found in extra elements as sub-element of extratags,namedetails, and entrances respectively.
Debug output
| Parameter | Value | Default |
|---|---|---|
| debug | 0 or 1 | 0 |
Adding debug=1 to a query to the /search, /reverse,
/lookup or /details endpoint switches the output to
an HTML page with assorted developer debug information: how Nominatim parsed,
understood and executed its database searches.
Debug output is disabled by default. It is only available when the server
administrator has enabledNOMINATIM_SERVE_DEBUG_OUTPUT.
Notes on field values
place_id is not a persistent id
The place_id is an internal identifier that is assigned data is imported
into a Nominatim database. The same OSM object will have a different value
on another server. It may even change its ID on the same server when it is
removed and reimported while updating the database with fresh OSM data.
It is thus not useful to treat it as permanent for later use.
The combination osm_type+osm_id is slightly better but remember in
OpenStreetMap mappers can delete, split, recreate places (and those
get a new osm_id), there is no link between those old and new ids.
Places can also change their meaning without changing their osm_id,
e.g. when a restaurant is retagged as supermarket. For a more in-depth
discussion see Permanent ID.
If you need an ID that is consistent over multiple installations of Nominatim,
then you should use the combination of osm_type+osm_id+class.
OSM reference
Nominatim may sometimes return special objects that do not correspond directly
to an object in OpenStreetMap. These are:
- Postcodes. Nominatim returns an postcode point created from all mapped
postcodes of the same name. The class and type of these object isplace=postcdode.
Noosm_typeandosm_idare included in the result. - Housenumber interpolations. Nominatim returns a single interpolated
housenumber from the interpolation way. The class and type areplace=house
andosm_typeandosm_idcorrespond to the interpolation way in OSM. - TIGER housenumber. Nominatim returns a single interpolated housenumber
from the TIGER data. The class and type areplace=house
andosm_typeandosm_idcorrespond to the street mentioned in the result.
Please note that the osm_type and osm_id returned may be changed in the
future. You should not expect to only find node, way and relation for
the type.
boundingbox
Comma separated list of min latitude, max latitude, min longitude, max longitude.
The whole planet would be -90,90,-180,180.
Can be used to pan and center the map on the result, for example with leafletjs
mapping librarymap.fitBounds([[bbox[0],bbox[2]],[bbox[1],bbox[3]]], {padding: [20, 20], maxzoom: 16});
Bounds crossing the antimeridian have a min latitude -180 and max latitude 180,
essentially covering the entire planet
(see issue 184).
addressdetails
Address details in the xml and json formats return a list of names together
with a designation label. Per default the following labels may appear:
- continent
- country, country_code
- region, state, state_district, county, ISO3166-2-lvl
- municipality, city, town, village
- city_district, district, borough, suburb, subdivision
- hamlet, croft, isolated_dwelling
- neighbourhood, allotments, quarter
- city_block, residential, farm, farmyard, industrial, commercial, retail
- road
- house_number, house_name
- emergency, historic, military, natural, landuse, place, railway,
man_made, aerialway, boundary, amenity, aeroway, club, craft, leisure,
office, mountain_pass, shop, tourism, bridge, tunnel, waterway - postcode
They roughly correspond to the classification of the OpenStreetMap data
according to either the place tag or the main key of the object.
entrances
Entrance details in the xml and json formats return the latitude and longitude
of the entrance, the osm node ID, the type of
entrance, and any extra tags
associated with the entrance node.
- osm_id
- type
- lat
- lon
- extratags
They roughly correspond to the classification of the OpenStreetMap data
according to either the place tag or the main key of the object.
Example
JSON
https://nominatim.openstreetmap.org/details?osmtype=W&osmid=32619803&entrances=1&format=json
{
"place_id": 124325848,
"parent_place_id": 123936289,
"osm_type": "W",
"osm_id": 32619803,
"category": "shop",
"type": "supermarket",
"admin_level": 15,
"localname": "PENNY",
...
"entrances": [
{
"osm_id": 1733488238,
"type": "yes",
"lat": "51.0466704",
"lon": "12.8077106",
"extratags": {
"foot": "yes"
}
},
{
"osm_id": 1733488256,
"type": "main",
"lat": "51.0467197",
"lon": "12.8078448",
"extratags": {
"foot": "yes"
}
},
{
"osm_id": 1733498087,
"type": "exit",
"lat": "51.0467081",
"lon": "12.8078131",
"extratags": {
"foot": "yes"
}
},
{
"osm_id": 7914950851,
"type": "service",
"lat": "51.0468487",
"lon": "12.8075876",
"extratags": {
"access": "delivery"
}
}
]
}Api Reverse (`docs/api/Reverse.md`)
Reverse Geocoding
Reverse geocoding generates an address from a coordinate given as
latitude and longitude.
How it works
The reverse geocoding API does not exactly compute the address for the
coordinate it receives. It works by finding the closest suitable OSM object
and returning its address information. This may occasionally lead to
unexpected results.
First of all, Nominatim only includes OSM objects in
its index that are suitable for searching. Small, unnamed paths for example
are missing from the database and can therefore not be used for reverse
geocoding either.
The other issue to be aware of is that the closest OSM object may not always
have a similar enough address to the coordinate you were requesting. For
example, in dense city areas it may belong to a completely different street.
Endpoint
The main format of the reverse API is
https://nominatim.openstreetmap.org/reverse?lat=<value>&lon=<value>&<params>where lat and lon are latitude and longitude of a coordinate in WGS84
projection. The API returns exactly one result or an error when the coordinate
is in an area with no OSM data coverage.
!!! tip
The reverse API allows a lookup of object by coordinate. If you want
to look up an object by ID, use the Address Lookup API instead.
!!! danger "Deprecation warning"
The API can also be used with the URL
https://nominatim.openstreetmap.org/reverse.php. This is now deprecated
and will be removed in future versions.
Parameters
This section lists additional parameters to further influence the output.
Output format
| Parameter | Value | Default |
|---|---|---|
| format | one of: xml, json, jsonv2, geojson, geocodejson |
xml |
See Place Output Formats for details on each format.
| Parameter | Value | Default |
|---|---|---|
| json_callback | function name | unset |
When given, then JSON output will be wrapped in a callback function with
the given name. See JSONP for more
information.
Only has an effect for JSON output formats.
Output details
| Parameter | Value | Default |
|---|---|---|
| addressdetails | 0 or 1 | 1 |
When set to 1, include a breakdown of the address into elements.
The exact content of the address breakdown depends on the output format.
!!! tip
If you are interested in a stable classification of address categories
(suburb, city, state, etc), have a look at the geocodejson format.
All other formats return classifications according to OSM tagging.
There is a much larger set of categories and they are not always consistent,
which makes them very hard to work with.
| Parameter | Value | Default |
|---|---|---|
| extratags | 0 or 1 | 0 |
When set to 1, the response include any additional information in the result
that is available in the database, e.g. wikipedia link, opening hours.
| Parameter | Value | Default |
|---|---|---|
| namedetails | 0 or 1 | 0 |
When set to 1, include a full list of names for the result. These may include
language variants, older names, references and brand.
| Parameter | Value | Default |
|---|---|---|
| entrances | 0 or 1 | 0 |
When set to 1, include the tagged entrances in the result.
Language of results
| Parameter | Value | Default |
|---|---|---|
| accept-language | browser language string | content of "Accept-Language" HTTP header |
Preferred language order for showing search results. This may either be
a simple comma-separated list of language codes or have the same format
as the "Accept-Language" HTTP header.
!!! tip
First-time users of Nominatim tend to be confused that they get different
results when using Nominatim in the browser versus in a command-line tool
like wget or curl. The command-line tools
usually don't send any Accept-Language header, prompting Nominatim
to show results in the local language. Browsers on the contrary always
send the currently chosen browser language.
Result restriction
| Parameter | Value | Default |
|---|---|---|
| zoom | 0-18 | 18 |
Level of detail required for the address. This is a number that
corresponds roughly to the zoom level used in XYZ tile sources in frameworks
like Leaflet.js, Openlayers etc.
In terms of address details the zoom levels are as follows:
| zoom | address detail |
|---|---|
| 3 | country |
| 5 | state |
| 8 | county |
| 10 | city |
| 12 | town / borough |
| 13 | village / suburb |
| 14 | neighbourhood |
| 15 | any settlement |
| 16 | major streets |
| 17 | major and minor streets |
| 18 | building |
| Parameter | Value | Default |
|---|---|---|
| layer | comma-separated list of: address, poi, railway, natural, manmade |
address,poi |
The layer filter allows to select places by themes.
The address layer contains all places that make up an address:
address points with house numbers, streets, inhabited places (suburbs, villages,
cities, states etc.) and administrative boundaries.
The poi layer selects all point of interest. This includes classic points
of interest like restaurants, shops, hotels but also less obvious features
like recycling bins, guideposts or benches.
The railway layer includes railway infrastructure like tracks.
Note that in Nominatim's standard configuration, only very few railway
features are imported into the database.
The natural layer collects features like rivers, lakes and mountains while
the manmade layer functions as a catch-all for features not covered by the
other layers.
Polygon output
| Parameter | Value | Default |
|---|---|---|
| polygon_geojson | 0 or 1 | 0 |
| polygon_kml | 0 or 1 | 0 |
| polygon_svg | 0 or 1 | 0 |
| polygon_text | 0 or 1 | 0 |
Add the full geometry of the place to the result output. Output formats
in GeoJSON, KML, SVG or WKT are supported. Only one of these
options can be used at a time.
| Parameter | Value | Default |
|---|---|---|
| polygon_threshold | floating-point number | 0.0 |
When one of the polygon_* outputs is chosen, return a simplified version
of the output geometry. The parameter describes the
tolerance in degrees with which the geometry may differ from the original
geometry. Topology is preserved in the geometry.
Other
| Parameter | Value | Default |
|---|---|---|
| valid email address | unset |
If you are making large numbers of request please include an appropriate email
address to identify your requests. See Nominatim's
Usage Policy for more details.
| Parameter | Value | Default |
|---|---|---|
| debug | 0 or 1 | 0 |
Output special developer debug information instead of the normal result.
See Debug output for details.
Examples
<reversegeocode timestamp="Fri, 06 Nov 09 16:33:54 +0000" querystring="...">
<result place_id="1620612" osm_type="node" osm_id="452010817">
135, Pilkington Avenue, Wylde Green, City of Birmingham, West Midlands (county), B72, United Kingdom
</result>
<addressparts>
<house_number>135</house_number>
<road>Pilkington Avenue</road>
<village>Wylde Green</village>
<town>Sutton Coldfield</town>
<city>City of Birmingham</city>
<county>West Midlands (county)</county>
<postcode>B72</postcode>
<country>United Kingdom</country>
<country_code>gb</country_code>
</addressparts>
</reversegeocode>Example with `format=jsonv2`
{
"place_id":"134140761",
"licence":"Data © OpenStreetMap contributors, ODbL 1.0. https:\/\/www.openstreetmap.org\/copyright",
"osm_type":"way",
"osm_id":"280940520",
"lat":"-34.4391708",
"lon":"-58.7064573",
"place_rank":"26",
"category":"highway",
"type":"motorway",
"importance":"0.1",
"addresstype":"road",
"display_name":"Autopista Pedro Eugenio Aramburu, El Triángulo, Partido de Malvinas Argentinas, Buenos Aires, 1.619, Argentina",
"name":"Autopista Pedro Eugenio Aramburu",
"address":{
"road":"Autopista Pedro Eugenio Aramburu",
"village":"El Triángulo",
"state_district":"Partido de Malvinas Argentinas",
"state":"Buenos Aires",
"postcode":"1.619",
"country":"Argentina",
"country_code":"ar"
},
"boundingbox":["-34.44159","-34.4370994","-58.7086067","-58.7044712"]
}Example with `format=geojson`
{
"type": "FeatureCollection",
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
"features": [
{
"type": "Feature",
"properties": {
"place_id": 77292420,
"osm_type": "node",
"osm_id": 1704756187,
"place_rank": 30,
"category": "place",
"type": "house",
"importance": 7.805828946359988e-5,
"addresstype": "place",
"name": "",
"display_name": "71, Via Guglielmo Marconi, Pioggia, Marconi, Porto-Saragozza, Bologna, Emilia-Romagna, 40122, Italy",
"address": {
"house_number": "71",
"road": "Via Guglielmo Marconi",
"neighbourhood": "Pioggia",
"suburb": "Marconi",
"city": "Bologna",
"county": "Bologna",
"ISO3166-2-lvl6": "IT-BO",
"state": "Emilia-Romagna",
"ISO3166-2-lvl4": "IT-45",
"postcode": "40122",
"country": "Italy",
"country_code": "it"
}
},
"bbox": [
11.3397676,
44.5014307,
11.3399676,
44.5016307
],
"geometry": {
"type": "Point",
"coordinates": [
11.3398676,
44.5015307
]
}
}
]
}Example with `format=geocodejson`
https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663&layer=address
{
"type": "FeatureCollection",
"geocoding": {
"version": "0.1.0",
"attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
"licence": "ODbL",
"query": "60.229917843587,11.16630979382"
},
"features": {
"type": "Feature",
"properties": {
"geocoding": {
"place_id": "42700574",
"osm_type": "node",
"osm_id": "3110596255",
"type": "house",
"accuracy": 0,
"label": "1, Løvenbergvegen, Mogreina, Ullensaker, Akershus, 2054, Norway",
"name": null,
"housenumber": "1",
"street": "Løvenbergvegen",
"postcode": "2054",
"county": "Akershus",
"country": "Norway",
"admin": {
"level7": "Ullensaker",
"level4": "Akershus",
"level2": "Norway"
}
}
},
"geometry": {
"type": "Point",
"coordinates": [
11.1658572,
60.2301296
]
}
}
}Api Search (`docs/api/Search.md`)
Search queries
The search API allows you to look up a location from a textual description
or address. Nominatim supports structured and free-form search queries.
The search query may also contain
special phrases
which are translated into specific OpenStreetMap (OSM) tags (e.g. Pub => amenity=pub).
This can be used to narrow down the kind of objects to be returned.
!!! note
Special phrases are not suitable to query all objects of a certain type in an
area. Nominatim will always just return a collection of the best matches. To
download OSM data by object type, use the Overpass API.
Endpoint
The search API has the following format:
https://nominatim.openstreetmap.org/search?<params>!!! danger "Deprecation warning"
The API can also be used with the URL
https://nominatim.openstreetmap.org/search.php. This is now deprecated
and will be removed in future versions.
The query term can be given in two different forms: free-form or structured.
Free-form query
| Parameter | Value |
|---|---|
| q | Free-form query string to search for |
In this form, the query can be unstructured.
Free-form queries are processed first left-to-right and then right-to-left if that fails. So you may search for
pilkington avenue, birmingham as well as for
birmingham, pilkington avenue.
Commas are optional, but improve performance by reducing the complexity of the search.
The free-form may also contain special phrases to describe the type of
place to be returned or a coordinate to search close to a position.
Structured query
| Parameter | Value |
|---|---|
| amenity | name and/or type of POI |
| street | housenumber and streetname |
| city | city |
| county | county |
| state | state |
| country | country |
| postalcode | postal code |
The structured form of the search query allows to lookup up an address
that is already split into its components. Each parameter represents a field
of the address. All parameters are optional. You should only use the ones
that are relevant for the address you want to geocode.
!!! Attention
Cannot be combined with the q=<query> parameter. Newer versions of
the API will return an error if you do so. Older versions simply return
unexpected results.
Parameters
The following parameters can be used to further restrict the search and
change the output. They are usable for both forms of the search query.
Output format
| Parameter | Value | Default |
|---|---|---|
| format | one of: xml, json, jsonv2, geojson, geocodejson |
jsonv2 |
See Place Output Formats for details on each format.
!!! note
The Nominatim service at
https://nominatim.openstreetmap.org
has a different default behaviour for historical reasons. When the
format parameter is omitted, the request will be forwarded to the Web UI.
| Parameter | Value | Default |
|---|---|---|
| json_callback | function name | unset |
When given, then JSON output will be wrapped in a callback function with
the given name. See JSONP for more
information.
Only has an effect for JSON output formats.
| Parameter | Value | Default |
|---|---|---|
| limit | number | 10 |
Limit the maximum number of returned results. Cannot be more than 40.
Nominatim may decide to return less results than given, if additional
results do not sufficiently match the query.
Output details
| Parameter | Value | Default |
|---|---|---|
| addressdetails | 0 or 1 | 0 |
When set to 1, include a breakdown of the address into elements.
The exact content of the address breakdown depends on the output format.
!!! tip
If you are interested in a stable classification of address categories
(suburb, city, state, etc), have a look at the geocodejson format.
All other formats return classifications according to OSM tagging.
There is a much larger set of categories and they are not always consistent,
which makes them very hard to work with.
| Parameter | Value | Default |
|---|---|---|
| extratags | 0 or 1 | 0 |
When set to 1, the response include any additional information in the result
that is available in the database, e.g. wikipedia link, opening hours.
| Parameter | Value | Default |
|---|---|---|
| namedetails | 0 or 1 | 0 |
When set to 1, include a full list of names for the result. These may include
language variants, older names, references and brand.
| Parameter | Value | Default |
|---|---|---|
| entrances | 0 or 1 | 0 |
When set to 1, include the tagged entrances in the result.
Language of results
| Parameter | Value | Default |
|---|---|---|
| accept-language | browser language string | content of "Accept-Language" HTTP header |
Preferred language order for showing search results. This may either be
a simple comma-separated list of language codes or have the same format
as the "Accept-Language" HTTP header.
!!! tip
First-time users of Nominatim tend to be confused that they get different
results when using Nominatim in the browser versus in a command-line tool
like wget or curl. The command-line tools
usually don't send any Accept-Language header, prompting Nominatim
to show results in the local language. Browsers on the contrary always
send the currently chosen browser language.
Result restriction
There are two ways to influence the results. Filters exclude certain
kinds of results completely. Boost parameters only change the order of the
results and thus give a preference to some results over others.
| Parameter | Value | Default |
|---|---|---|
| countrycodes | comma-separated list of country codes | unset |
Filter that limits the search results to one or more countries.
The country code must be the
ISO 3166-1alpha2 code
of the country, e.g. gb for the United Kingdom, de for Germany.
Each place in Nominatim is assigned to one country code based
on OSM country boundaries. In rare cases a place may not be in any country
at all, for example, when it is in international waters. These places are
also excluded when the filter is set.
!!! Note
This parameter should not be confused with the 'country' parameter of
the structured query. The 'country' parameter contains a search term
and will be handled with some fuzziness. The countrycodes parameter
is a hard filter and as such should be preferred. Having both parameters
in the same query will work. If the parameters contradict each other,
the search will come up empty.
| Parameter | Value | Default |
|---|---|---|
| layer | comma-separated list of: address, poi, railway, natural, manmade |
unset (no restriction) |
The layer filter allows to select places by themes.
The address layer contains all places that make up an address:
address points with house numbers, streets, inhabited places (suburbs, villages,
cities, states tec.) and administrative boundaries.
The poi layer selects all point of interest. This includes classic POIs like
restaurants, shops, hotels but also less obvious features like recycling bins,
guideposts or benches.
The railway layer includes railway infrastructure like tracks.
Note that in Nominatim's standard configuration, only very few railway
features are imported into the database.
The natural layer collects features like rivers, lakes and mountains while
the manmade layer functions as a catch-all for features not covered by the
other layers.
| Parameter | Value | Default |
|---|---|---|
| featureType | one of: country, state, city, settlement |
unset |
The featureType allows to have a more fine-grained selection for places
from the address layer. Results can be restricted to places that make up
the 'state', 'country' or 'city' part of an address. A featureType ofsettlement selects any human inhabited feature from 'state' down to
'neighbourhood'.
When featureType is set, then results are automatically restricted
to the address layer (see above).
!!! tip
Instead of using the featureType filters country, state or city,
you can also use a structured query without the finer-grained parameters
amenity or street.
| Parameter | Value | Default |
|---|---|---|
| include | comma-separated list of categories | unset (no restriction) |
| exclude | comma-separated list of categories | unset (no restriction) |
Filters that select or drop results by category. A category describes what kind
of place an object is. It has the form osm.<key>.<value>, taken from a main
OSM tag of the object, for example osm.amenity.restaurant. An object has one
category for every main tag it carries. A place tagged with tourism=hotel andamenity=restaurant therefore has the categories osm.tourism.hotel andosm.amenity.restaurant.
Categories are hierarchical and a filter matches the category itself as well as
all its descendants. include=osm.amenity selects restaurants, cafes,
pharmacies and everything else carrying an amenity tag. A category must
consist of at least two labels separated by dots, so include=osm is rejected
with an error. Only letters, digits and underscores may appear in a label.
Hyphens of the original OSM tag are replaced with underscores when the category
is created, so shop=car-repair is selected withinclude=osm.shop.car_repair.
Both parameters may be repeated. Note that a comma and a repetition combine
the categories in different ways:
| Query | Returns places that |
|---|---|
include=osm.amenity.cafe |
have osm.amenity.cafe |
include=osm.amenity.cafe,osm.amenity.bar |
have osm.amenity.cafe or osm.amenity.bar |
include=osm.tourism.hotel&include=osm.amenity.restaurant |
have osm.tourism.hotel and osm.amenity.restaurant |
exclude=osm.amenity.fast_food |
do not have osm.amenity.fast_food |
exclude=osm.shop.bakery,osm.amenity.cafe |
do not have osm.shop.bakery and osm.amenity.cafe at the same time |
exclude=osm.shop.bakery&exclude=osm.amenity.cafe |
have neither osm.shop.bakery nor osm.amenity.cafe |
!!! note
Categories are only used for filtering, they are never part of the output.
The category/class and type fields of a result contain the single
main tag that Nominatim uses to classify the place. That is only one of
the categories the place may have.
!!! note
A place without any category cannot satisfy include. Postcodes, address
interpolations, TIGER housenumbers and countries returned from
Nominatim's built-in country-list fallback (used when no matching OSM
boundary was found) therefore disappear from the results as soon as
include is used. exclude has no effect on them. Countries backed by
an OSM boundary keep their usual category and are filtered normally.
| Parameter | Value | Default |
|---|---|---|
| exclude_place_ids | comma-separated list of ids (OSM IDs where possible, otherwise place_ids or stable postcode refs) |
If you do not want certain OSM objects to appear in the search
result, give a comma separated list of the ids you want to skip.
Each entry may be one of:
- a Nominatim internal
place_id(for example125279639) - an OSM object reference in the form
<osm_type><osm_id>where<osm_type>is one ofN(node),W(way) orR(relation), for exampleN107775 - a stable postcode reference in the form
P<country_code>:, for examplePus:94110orPgb:EH4_7EA
Usage of OSM IDs is recommended because they are server independent. Stable postcode refs should be used for artificial postcode results without an OSM object reference. In postcode refs, spaces are replaced with underscores. place_ids are still required for results without either kind of stable reference (for example, countries). When a street is excluded via its OSM ID, then interpolations and TIGER data derived from that street are excluded as well.
This can be used to retrieve additional search results. For example, if a
previous query only returned a few results, then including those here would
cause the search to return other, less accurate, matches (if possible).
| Parameter | Value | Default |
|---|---|---|
| viewbox | <x1>,<y1>,<x2>,<y2> |
unset |
Boost parameter which focuses the search on the given area.
Any two corner points of the box are accepted as long as they make a proper
box. x is longitude, y is latitude.
| Parameter | Value | Default |
|---|---|---|
| bounded | 0 or 1 | 0 |
When set to 1, then it turns the 'viewbox' parameter (see above) into
a filter parameter, excluding any results outside the viewbox.
When bounded=1 is given and the viewbox is small enough, then an amenity-only
search is allowed. Give the special keyword for the amenity in square
brackets, e.g. [pub] and a selection of objects of this type is returned.
There is no guarantee that the result returns all objects in the area.
Polygon output
| Parameter | Value | Default |
|---|---|---|
| polygon_geojson | 0 or 1 | 0 |
| polygon_kml | 0 or 1 | 0 |
| polygon_svg | 0 or 1 | 0 |
| polygon_text | 0 or 1 | 0 |
Add the full geometry of the place to the result output. Output formats
in GeoJSON, KML, SVG or WKT are supported. Only one of these
options can be used at a time.
| Parameter | Value | Default |
|---|---|---|
| polygon_threshold | floating-point number | 0.0 |
When one of the polygon_* outputs is chosen, return a simplified version
of the output geometry. The parameter describes the
tolerance in degrees with which the geometry may differ from the original
geometry. Topology is preserved in the geometry.
Other
| Parameter | Value | Default |
|---|---|---|
| valid email address | unset |
If you are making large numbers of request please include an appropriate email
address to identify your requests. See Nominatim's
Usage Policy for more details.
| Parameter | Value | Default |
|---|---|---|
| dedupe | 0 or 1 | 1 |
Sometimes you have several objects in OSM identifying the same place or
object in reality. The simplest case is a street being split into many
different OSM ways due to different characteristics. Nominatim will
attempt to detect such duplicates and only return one match. Setting
this parameter to 0 disables this deduplication mechanism and
ensures that all results are returned.
| Parameter | Value | Default |
|---|---|---|
| debug | 0 or 1 | 0 |
Output special developer debug information instead of the normal result.
See Debug output for details.
Examples
XML with KML polygon
/* Detailed source-code truncated for AI context efficiency. */JSON with SVG polygon
[
{
"address": {
"ISO3166-2-lvl4": "DE-BE",
"borough": "Mitte",
"city": "Berlin",
"country": "Deutschland",
"country_code": "de",
"historic": "Kommandantenhaus",
"house_number": "1",
"neighbourhood": "Friedrichswerder",
"postcode": "10117",
"road": "Unter den Linden",
"suburb": "Mitte"
},
"boundingbox": [
"52.5170798",
"52.5173311",
"13.3975116",
"13.3981577"
],
"class": "historic",
"display_name": "Kommandantenhaus, 1, Unter den Linden, Friedrichswerder, Mitte, Berlin, 10117, Deutschland",
"importance": 0.8135042058306902,
"lat": "52.51720765",
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
"lon": "13.397834399325466",
"osm_id": 15976890,
"osm_type": "way",
"place_id": 108681845,
"svg": "M 13.3975116 -52.5172905 L 13.397549 -52.5170798 13.397715 -52.5170906 13.3977122 -52.5171064 13.3977392 -52.5171086 13.3977417 -52.5170924 13.3979655 -52.5171069 13.3979623 -52.5171233 13.3979893 -52.5171248 13.3979922 -52.5171093 13.3981577 -52.5171203 13.398121 -52.5173311 13.3978115 -52.5173103 Z",
"type": "house"
}
]JSON with address details
[
{
"address": {
"ISO3166-2-lvl4": "DE-BE",
"borough": "Mitte",
"city": "Berlin",
"country": "Deutschland",
"country_code": "de",
"neighbourhood": "Sprengelkiez",
"postcode": "13347",
"road": "Lindower Straße",
"shop": "Ditsch",
"suburb": "Wedding"
},
"addresstype": "shop",
"boundingbox": [
"52.5427201",
"52.5427654",
"13.3668619",
"13.3669442"
],
"category": "shop",
"display_name": "Ditsch, Lindower Straße, Sprengelkiez, Wedding, Mitte, Berlin, 13347, Deutschland",
"importance": 9.99999999995449e-06,
"lat": "52.54274275",
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright",
"lon": "13.36690305710228",
"name": "Ditsch",
"osm_id": 437595031,
"osm_type": "way",
"place_id": 204751033,
"place_rank": 30,
"type": "bakery"
}
]GeoJSON
{
"type": "FeatureCollection",
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
"features": [
{
"type": "Feature",
"properties": {
"place_id": "35811445",
"osm_type": "node",
"osm_id": "2846295644",
"display_name": "17, Strada Pictor Alexandru Romano, Bukarest, Bucharest, Sector 2, Bucharest, 023964, Romania",
"place_rank": "30",
"category": "place",
"type": "house",
"importance": 0.62025
},
"bbox": [
26.1156689,
44.4354754,
26.1157689,
44.4355754
],
"geometry": {
"type": "Point",
"coordinates": [
26.1157189,
44.4355254
]
}
}
]
}GeocodeJSON
{
"type": "FeatureCollection",
"geocoding": {
"version": "0.1.0",
"attribution": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
"licence": "ODbL",
"query": "Αγία Τριάδα, Αδωνιδος, Athens, Greece"
},
"features": [
{
"type": "Feature",
"properties": {
"geocoding": {
"type": "place_of_worship",
"label": "Αγία Τριάδα, Αδωνιδος, Άγιος Νικόλαος, 5º Δημοτικό Διαμέρισμα Αθηνών, Athens, Municipality of Athens, Regional Unit of Central Athens, Region of Attica, Attica, 11472, Greece",
"name": "Αγία Τριάδα",
"admin": null
}
},
"geometry": {
"type": "Point",
"coordinates": [
23.72949633941,
38.0051697
]
}
}
]
}Api Status (`docs/api/Status.md`)
Status
Report on the state of the service and database. Useful for checking if the
service is up and running. The JSON output also reports
when the database was last updated.
Endpoint
The status API has the following format:
https://nominatim.openstreetmap.org/status!!! danger "Deprecation warning"
The API can also be used with the URL
https://nominatim.openstreetmap.org/status.php. This is now deprecated
and will be removed in future versions.
Parameters
The status endpoint takes a single optional parameter:
| Parameter | Value | Default |
|---|---|---|
| format | one of: text, json |
'text' |
Selects the output format. See below.
Output
Text format
When everything is okay, a status code 200 is returned and a simple message: OK
On error it will return HTTP status code 500 and print a detailed error message, e.g.ERROR: Database connection failed.
JSON format
Always returns a HTTP code 200, when the status call could be executed.
On success a JSON dictionary with the following structure is returned:
{
"status": 0,
"message": "OK",
"data_updated": "2020-05-04T14:47:00+00:00",
"software_version": "3.6.0-0",
"database_version": "3.6.0-0"
}The software_version field contains the version of Nominatim used to serve
the API. The database_version field contains the version of the data format
in the database.
On error will return a shorter JSON dictionary with the error message
and status only, e.g.
{
"status": 700,
"message": "Database connection failed"
}Customize Country Settings (`docs/customize/Country-Settings.md`)
Customizing Per-Country Data
Whenever an OSM is imported into Nominatim, the object is first assigned
a country. Nominatim can use this information to adapt various aspects of
the address computation to the local customs of the country. This section
explains how country assignment works and the principal per-country
localizations.
Country assignment
Countries are assigned on the basis of country data from the OpenStreetMap
input data itself. Countries are expected to be tagged according to the
administrative boundary schema:
a OSM relation with boundary=administrative and admin_level=2. Nominatim
uses the country code to distinguish the countries.
If there is no country data available for a point, then Nominatim uses the
fallback data imported from data/country_osm_grid.sql.gz. This was computed
from OSM data as well but is guaranteed to cover all countries.
Some OSM objects may also be located outside any country, for example a buoy
in the middle of the ocean. These object do not get any country assigned and
get a default treatment when it comes to localized handling of data.
Per-country settings
Global country settings
The main place to configure settings per country is the filesettings/country_settings.yaml. This file has one section per country that
is recognised by Nominatim. Each section is tagged with the country code
(in lower case) and contains the different localization information. Only
countries which are listed in this file are taken into account for computations.
For example, the section for Andorra looks like this:
partition: 35
languages: ca
names: !include country-names/ad.yaml
postcode:
pattern: "(ddd)"
output: AD\1The individual settings are described below.
`partition`
Nominatim internally splits the data into multiple tables to improve
performance. The partition number tells Nominatim into which table to put
the country. This is purely internal management and has no effect on the
output data.
The default is to have one partition per country.
`languages`
A comma-separated list of ISO-639 language codes of default languages in the
country. These are the languages used in name tags without a language suffix.
Note that this is not necessarily the same as the list of official languages
in the country. There may be officially recognised languages in a country
which are only ever used in name tags with the appropriate language suffixes.
Conversely, a non-official language may appear a lot in the name tags, for
example when used as an unofficial Lingua Franca.
List the languages in order of frequency of appearance with the most frequently
used language first. It is not recommended to add languages when there are only
very few occurrences.
If only one language is listed, then Nominatim will 'auto-complete' the
language of names without an explicit language-suffix.
`names`
List of names of the country and its translations. These names are used as
a baseline. It is always possible to search countries by the given names, no
matter what other names are in the OSM data. They are also used as a fallback
when a needed translation is not available.
!!! Note
The list of names per country is currently fairly large because Nominatim
supports translations in many languages per default. That is why the
name lists have been separated out into extra files. You can find the
name lists in the file settings/country-names/<country code>.yaml.
The names section in the main country settings file only refers to these
files via the special !include directive.
`postcode`
Describes the format of the postcode that is in use in the country.
When a country has no official postcodes, set this to no. Example:
ae:
postcode: noWhen a country has a postcode, you need to state the postcode pattern and
the default output format. Example:
bm:
postcode:
pattern: "(ll)[ -]?(dd)"
output: \1 \2The pattern is a regular expression that describes the possible formats
accepted as a postcode. The pattern follows the standard syntax for
regular expressions in Python
with two extra shortcuts: d is a shortcut for a single digit([0-9])
and l for a single ASCII letter ([A-Z]).
Use match groups to indicate groups in the postcode that may optionally be
separated with a space or a hyphen.
For example, the postcode for Bermuda above always consists of two letters
and two digits. They may optionally be separated by a space or hyphen. That
means that Nominatim will consider AB56, AB 56 and AB-56 spelling variants
for one and the same postcode.
Never add the country code in front of the postcode pattern. Nominatim will
automatically accept variants with a country code prefix for all postcodes.
The output field is an optional field that describes what the canonical
spelling of the postcode should be. The format is the
regular expression expand syntax referring back to the bracket groups in the pattern.
Most simple postcodes only have one spelling variant. In that case, the
output can be omitted. The postcode will simply be used as is.
In the Bermuda example above, the canonical spelling would be to have a space
between letters and digits.
!!! Warning
When your postcode pattern covers multiple variants of the postcode, then
you must explicitly state the canonical output or Nominatim will not
handle the variations correctly.
`sanitizers`
Defines Sanitizers which should only applied to places
within the given country. Country-specific sanitizers are executed after
the general sanitizers have run.
Other country-specific configuration
There are some other configuration files where you can set localized settings
according to the assigned country. These are:
Please see the linked documentation sections for more information.
Customize Import Styles (`docs/customize/Import-Styles.md`)
Configuring the Import of OSM data
In the very first step of a Nominatim import, OSM data is loaded into the
database. Nominatim uses osm2pgsql for this task.
It comes with a flex style
specifically tailored to filter and convert OSM data into Nominatim's
internal data representation. Nominatim ships with a few preset
configurations for this import, each results in a geocoding database of
different detail. The
Import section explains
these default configurations in detail.
If you want to have more control over which OSM data is added to the database,
you can also create your own custom style. Create a new lua style file, put it
into your project directory and then set NOMINATIM_IMPORT_STYLE to the name
of the file. Custom style files can be used to modify the existing preset
configurations or to implement your own configuration from scratch.
The remainder of the page describes how the flex style works and how to
customize it.
The `flex-base` lua module
The core of Nominatim's flex import configuration is the flex-base module.
It defines the table layout used by Nominatim and provides standard
implementations for the import callbacks that help with customizing
how OSM tags are used by Nominatim.
Every custom style must include this module to make sure that the correct
tables are created. Thus start your custom style as follows:
local flex = require('flex-base')Using preset configurations
If you want to start with one of the existing presets, then you can import
its settings using the load_topic() function:
local flex = require('flex-base')
flex.load_topic('streets')The load_topic function takes an optional second configuration
parameter. The available options are explained in the
themepark section.
Available topics are: admin, street, address, full. These topic
correspond to the import styles
you can choose during import. To start with the 'extratags' style, use thefull topic with the appropriate config parameter:
flex.load_topic('full', {with_extratags = true})!!! note
You can also directly import the preset style files, e.g.
local flex = require('import-street'). It is not possible to
set extra configuration this way.
How processing works
When Nominatim processes an OSM object, it looks for four kinds of tags:
The main tags classify what kind of place the OSM object represents. One
OSM object can have more than one main tag. All of them end up in the same
database entry, which gets one category per main tag. Name tags represent
searchable names of the place. Address tags are used to compute the address
information of the place. Address tags are used for searching and for creating
a display name of the place.
Extra tags are any tags that are not directly related to search but
contain interesting additional information. These are just saved in the database
and may be returned with the result on request.
!!! danger
Some of the extra tags are used by Nominatim to better
classify the place. These tags will always be added, independent of
any settings in the style.
Configuring the style means deciding which key and/or key/value is used
as which kind of tag.
Categories
A category is derived from a main tag and written as osm.<key>.<value>.
An object tagged with tourism=hotel and amenity=restaurant therefore gets
the categories osm.tourism.hotel and osm.amenity.restaurant. Categories are
hierarchical and can be used to filter search results, see the
search API.
A key or value may only be turned into a category label if it consists
solely of ASCII letters (upper- or lower-case), digits, underscore and
hyphen; hyphens are then replaced with underscores, so the resulting label
itself never contains one. A main tag whose value contains any other
character gets the category osm.<key>.yes. When the key itself contains
such a character, then no category is created for the tag at all.
Nominatim also needs a single main tag to classify and rank the place. It is
saved as the class and type of the place and returned in thecategory/type fields of a result. When an object has more than one main
tag, then the alphabetically first key/value pair wins. Tags classified asfallback (see below) never win against another main tag.
Changing the recognized tags
The flex style offers a number of functions to set the classification of
each OSM tag. Most of these functions can also take a preset string instead
of a tag description. These presets describe common configurations that
are also used in the definition of the predefined styles. This section
lists the configuration functions and the accepted presets.
Key match lists
Some of the following functions take key match lists. These lists can
contain three kinds of strings to match against tag keys:
A string that ends in an asterisk * is a prefix match and accordingly matches
against any key that starts with the given string (minus the *).
A suffix match can be defined similarly with a string that starts with a *.
Any other string is matched exactly against tag keys.
Main tags
set/modify_main_tags() allow to define which tags are used as main tags. It
takes a lua table parameter which defines for keys and key/value
combinations, how they are classified.
The following classifications are recognized:
| classification | meaning |
|---|---|
| always | Unconditionally use this tag as a main tag. |
| named | Consider as main tag, when the object has a primary name (see names below) |
| named_with_key | Consider as main tag, when the object has a primary name with a domain prefix. For example, if the main tag is bridge=yes, then it will only be used, if there is a tag bridge:name[:XXX] for the same object. If this property is set, all names that are not domain-specific are ignored. |
| fallback | Use as main tag, but only make it the class and type of the place when no other main tag was found. Fallback always implies named, i.e. fallbacks are only tried for objects with primary names. |
| postcode_area | Tag indicates a postcode area. Copy area into the table of postcodes but only when the object is a relation and has a postcode tagged. |
| delete | Completely ignore the tag in any further processing |
| extra | Move the tag to extratags and then ignore it for further processing |
<function> |
Advanced handling, see below |
Each key in the table parameter defines an OSM tag key. The value may
be directly a classification as described above. Then the tag will
be considered a main tag for any possible value that is not further defined.
To further restrict which values are acceptable, give a table with the
permitted values and their kind of main tag. If the table contains a simple
value without key, then this is used as default for values that are not listed.
set_main_tags() will completely replace the current main tag configuration
with the new configuration. modify_main_tags() will merge the new
configuration with the existing one. Merging is done at value level.
For example, when the current setting is highway = {'always', primary = 'named'},
then set_main_tags{highway = 'delete'} will result in a rulehighway = {'delete', primary = 'named'}.
!!! example
``` lua
local flex = require('import-full')
flex.set_main_tags{
boundary = {administrative = 'named'},
highway = {'always', street_lamp = 'named', no = 'delete'},
landuse = 'fallback'
}
```
In this example an object with a `boundary` tag will only be included
when it has a value of `administrative`. Objects with `highway` tags are
always included with two exceptions: the troll tag `highway=no` is
deleted on the spot. And when the value is `street_lamp` then the object
must also have a name, to be included. Finally, if a `landuse` tag is
present on a named object then it is used independently of the concrete
value, but it only determines the class and type of the place when neither
a boundary nor a highway tag was found.Presets
| Name | Description |
|---|---|
| admin | Basic tag set collecting places and administrative boundaries. This set is needed also to ensure proper address computation and should therefore always be present. You can disable selected place types like place=locality after adding this set, if they are not relevant for your use case. |
| all_boundaries | Extends the set of recognized boundaries and places to all available ones. |
| natural | Tags for natural features like rivers and mountain peaks. |
| street/default | Tags for streets. Major streets are always included, minor ones only when they have a name. |
| street/car | Tags for all streets that can be used by a motor vehicle. |
| street/all | Includes all highway features named and unnamed. |
| poi/delete | Adds most POI features with and without name. Some frequent but very domain-specific values are excluded by deleting them. |
| poi/extra | Like 'poi/delete' but excluded values are moved to extratags. |
Advanced main tag handling
The groups described above are in fact only a preset for a filtering function
that is used to make the final decision how a pre-selected main tag is entered
into Nominatim's internal table. To further customize handling you may also
supply your own filtering function.
The function takes up to three parameters: a Place object of the object
being processed, the key of the main tag and the value of the main tag.
The function may return one of three values:
nilorfalsecauses the entry to be ignored- the Place object causes the place to be added as is
- `Place.copy(names=..., address=..., extratags=...) causes the
place to be enter into the database but with name/address/extratags
set to the given different values.
The Place object has some read-only values that can be used to determine
the handling:
- object is the original OSM object data handed in by osm2pgsql
- admin_level is the content of the admin_level tag, parsed into an integer and normalized to a value between 0 and 15
- has_name is a boolean indicating if the object has a primary name tag
- names is a table with the collected list of name tags
- address is a table with the collected list of address tags
- extratags is a table with the collected list of additional tags to save
!!! example
``` lua
local flex = require('flex-base')
flex.add_topic('street')
local function no_sidewalks(place, k, v)
if place.object.tags.footway == 'sidewalk' then
return false
end
-- default behaviour is to have all footways
return place
end
flex.modify_main_tags(highway = {'footway' = no_sidewalks}
```
This script adds a custom handler for `highway=footway`. It only includes
them in the database, when the object doesn't have a tag `footway=sidewalk`
indicating that it is just part of a larger street which should already
be indexed. Note that it is not necessary to check the key and value
of the main tag because the function is only used for the specific
main tag.Ignored tags
The function ignore_keys() sets the delete classification for keys.
This function takes a key match list so that it is possible to exclude
groups of keys.
Note that full matches always take precedence over suffix matches, which
in turn take precedence over prefix matches.
!!! example
``` lua
local flex = require('flex-base')
flex.add_topic('admin')
flex.ignore_keys{'old_name', 'old_name:*'}
```
This example uses the `admin` preset with the exception that names
that are no longer are in current use, are ignored.Presets
| Name | Description |
|---|---|
| metatags | Tags with meta information about the OSM tag like source, notes and import sources. |
| name | Non-names that actually describe properties or name parts. These names can throw off search and should always be removed. |
| address | Extra addr:* tags that are not useful for Nominatim. |
Tags for `extratags`
The function add_for_extratags() sets the extra classification for keys.
This function takes a
key match list so that it is possible to move groups of keys to extratags.
Note that full matches always take precedence over suffix matches, which
in turn take precedence over prefix matches.
!!! example
``` lua
local flex = require('flex-base')
flex.add_topic('street')
flex.add_for_extratags{'surface', 'access', 'vehicle', 'maxspeed'}
```
This example uses the `street` preset but adds a couple of tags that
are of interest about the condition of the street.Presets
Accepts all presets from ignored tags.
General pre-filtering
(deprecated) set_prefilters() allows to set the delete and extra
classification for main tags.
This function removes all previously set main tags with delete and extra
classification and then adds the newly defined tags.
set_prefilters() takes a table with four optional fields:
- delete_keys is a key match list for tags that should be deleted
- delete_tags contains a table of tag keys pointing to a list of tag
values. Tags with matching key/value pairs are deleted. - extra_keys is a key match list for tags which should be saved into
extratags - extra_tags contains a table of tag keys pointing to a list of tag
values. Tags with matching key/value pairs are moved to extratags.
!!! danger "Deprecation warning"
Use of this function should be replaced with modify_main_tags() to
set the data from delete_tags and extra_tags, with ignore_keys()
for the delete_keys parameter and with add_for_extratags() for the
extra_keys parameter.
Name tags
set/modify_name_tags() allow to define the tags used for naming places. Name tags
can only be selected by their keys. The import script distinguishes
between primary and auxiliary names. A primary name is the given name of
a place. Having a primary name makes a place named. This is important
for main tags that are only included when a name is present. Auxiliary names
are identifiers like references. They may be searched for but should not
be included on their own.
The functions take a table with two optional fields main and extra.
They take key match lists for primary and auxiliary names respectively.
A third field house can contain tags for names that appear in place of
house numbers in addresses. This field can only contain complete key names.
'house tags' are special in that they cause the OSM object to be added to
the database independently of the presence of other main tags.
set_name_tags() overwrites the current configuration, whilemodify_name_tags() replaces the fields that are given. (Be aware that
the fields are replaced as a whole. main = {'foo_name'} will causefoo_name to become the only recognized primary name. Any previously
defined primary names are forgotten.)
!!! example
``` lua
local flex = require('flex-base')
flex.set_main_tags{highway = {traffic_light = 'named'}}
flex.set_name_tags{main = {'name', 'name:*'},
extra = {'ref'}
}
```
This example creates a search index over traffic lights but will
only include those that have a common name and not those which just
have some reference ID from the city.Presets
| Name | Description |
|---|---|
| core | Basic set of recognized names for all places. |
| address | Additional names useful when indexing full addresses. |
| poi | Extended set of recognized names for pois. Use on top of the core set. |
Address tags
set/modify_address_tags() defines the tags that will be used to build
up the address of an object. Address tags can only be chosen by their key.
The functions take a table with arbitrary fields, each defining
a key list or key match list. Some fields have a special meaning:
| Field | Type | Description |
|---|---|---|
| main | key list | Tags that make a full address object out of the OSM object. This is usually the house number or variants thereof. If a main address tag appears, then the object will always be included, if necessary with a fallback of place=house. If the key has a prefix of addr: or is_in: this will be stripped. |
| extra | key match list | Supplementary tags for addresses, tags like addr:street, addr:city etc. If the key has a prefix of addr: or is_in: this will be stripped. |
| interpolation | key list | Tags that identify address interpolation lines. |
| country | key match list | Tags that may contain the country the place is in. The first found value with a two-letter code will be accepted, all other values are discarded. |
| other | key match list | Summary field. If a key matches the key match list, then its value will be added to the address tags with the name of the field as key. If multiple tags match, then an arbitrary one wins. |
set_address_tags() overwrites the current configuration, whilemodify_address_tags() replaces the fields that are given. (Be aware that
the fields are replaced as a whole.)
!!! example
``` lua
local flex = require('import-full')
flex.set_address_tags{
main = {'addr:housenumber'},
extra = {'addr:*'},
postcode = {'postal_code', 'postcode', 'addr:postcode'},
country = {'country_code', 'ISO3166-1'}
}
```
In this example all tags which begin with `addr:` will be saved in
the address tag list. If one of the tags is `addr:housenumber`, the
object will fall back to be entered as a `place=house` in the database
unless there is another interested main tag to be found.
Tags with keys `country_code` and `ISO3166-1` are saved with their
value under `country` in the address tag list. The same thing happens
to postcodes, they will always be saved under the key `postcode` thus
normalizing the multitude of keys that are used in the OSM database.Presets
| Name | Description |
|---|---|
| core | Basic set of tags needed to recognize address relationship for any place. Always include this. |
| houses | Additional set of tags needed to recognize proper addresses |
Handling of unclassified tags
set_unused_handling() defines what to do with tags that remain after all tags
have been classified using the functions above. There are two ways in
which the function can be used:
set_unused_handling(delete_keys = ..., delete_tags = ...) deletes all
keys that match the descriptions in the parameters and moves all remaining
tags into the extratags list.
set_unused_handling(extra_keys = ..., extra_tags = ...) moves all tags
matching the parameters into the extratags list and then deletes the remaining
tags. For the format of the parameters see the description in set_prefilters()
above.
When no special handling is set, then unused tags will be discarded with one
exception: place tags are kept in extratags for administrative boundaries.
When using a custom setting, you should also make sure that the place tag
is added for extratags.
!!! example
``` lua
local flex = require('import-full')
flex.set_address_tags{
main = {'addr:housenumber'},
extra = {'addr:*', 'tiger:county'}
}
flex.set_unused_handling{delete_keys = {'tiger:*'}}
```
In this example all remaining tags except those beginning with `tiger:`
are moved to the extratags list. Note that it is not possible to
already delete the tiger tags with `set_prefilters()` because that
would remove tiger:county before the address tags are processed.Filling additional tables
Most of the OSM objects are saved in the main place table for further
processing. In addition to that, there are some smaller tables that save
specialised information. The content of these tables can be customized as
well.
Entrance table
The table place_entrance saves information about OSM nodes that represent
an entrance. This data is later mingled with buildings and other areas and
can be returned on request. The table
saves the type of entrance as well as a set of custom extra tags.
The function set_entrance_filter() can be used to customize the table's
content.
When called without any parameter, then filling the entrance table will be
disabled. When called with a preset name, the appropriate preset will be
applied.
To create a custom configuration, call the function
with a table with the following fields:
- main_tags is a list of tags that mark an entrance node. The value of the
first tag found in the list will be used as the entrance type. - extra_include is an optional list of tags to be added to the extratags
for this entrance. When left out, all tags except for the ones defined
in 'main_tags' will be included. To disable saving of extra tags, set
this to the empty list. - extra_exclude defines an optional list of tags to drop before including
the remaining tags as extratags. Note that the tags defined in 'main_tags'
will always be excluded, independently of this setting.
To have even more fine-grained control over the output, you can also hand
in a callback for processing entrance information. The callback function
receives a single parameter, the
osm2pgsql object.
This object itself must not be modified. The callback should return eithernil when the object is not an entrance. Or it returns a table with a
mandatory entrance field containing a string with the type of entrance
and an optional extratags field with a simple key-value table of extra
information.
Presets
| Name | Description |
|---|---|
| default | Standard configuration used with full and extratags styles. |
Customizing osm2pgsql callbacks
osm2pgsql expects the flex style to implement three callbacks, one process
function per OSM type. If you want to implement special handling for
certain OSM types, you can override the default implementations provided
by the flex-base module.
Enabling additional relation types
OSM relations can represent very diverse
types of real-world objects. To
be able to process them correctly, Nominatim needs to understand how to
create a geometry for each type. By default, the script knows how to
process relations of type multipolygon, boundary and waterway. All
other relation types are ignored.
To add other types relations, set RELATION_TYPES for
the type to the kind of geometry that should be created. The following
kinds of geometries can be used:
- relation_as_multipolygon creates a (Multi)Polygon from the ways in
the relation. If the ways do not form a valid area, then the object is
silently discarded. - relation_as_multiline creates a (Multi)LineString from the ways in
the relations. Ways are combined as much as possible without any regards
to their order in the relation.
!!! Example
``` lua
local flex = require('import-full')
flex.RELATION_TYPES['site'] = flex.relation_as_multipolygon
```
With this line relations of `type=site` will be included in the index
according to main tags found. This only works when the site relation
resolves to a valid area. Nodes in the site relation are not part of the
geometry.Adding additional logic to processing functions
The default processing functions are also exported by the flex-base module
as process_node, process_way and process_relation. These can be used
to implement your own processing functions with some additional processing
logic.
!!! Example
``` lua
local flex = require('import-full')
function osm2pgsql.process_relation(object)
if object.tags.boundary ~= 'administrative' or object.tags.admin_level ~= '2' then
flex.process_relation(object)
end
end
```
This example discards all country-level boundaries and uses standard
handling for everything else. This can be useful if you want to use
your own custom country boundaries.Customizing the main processing function
!!! danger "Deprecation Warning"
The style used to allow overwriting the internal processing function
process_tags(). While this is currently still possible, it is no longer
encouraged and may stop working in future versions. The internal
Place class should now be considered read-only.
Using osm2pgsql-themepark
The Nominatim osm2pgsql style is designed so that it can also be used as
a theme for osm2pgsql-themepark. This
makes it easy to combine Nominatim with other projects like
openstreetmap-carto
in the same database.
To set up one of the preset styles, simply include a topic with the same name:
local themepark = require('themepark')
themepark:add_topic('nominatim/address')Themepark topics offer two configuration options:
- street_theme allows to choose one of the sub topics for streets:
- default - include all major streets and named minor paths
- car - include all streets physically usable by cars
- all - include all major streets and minor paths
- with_extratags, when set to a truthy value, then tags that are
not specifically used for address or naming are added to the
extratags column
The customization functions described in the
Changing recognized tags section
are available from the theme. To access the theme you need to explicitly initialize it.
!!! Example
``` lua
local themepark = require('themepark')
themepark:add_topic('nominatim/full', {with_extratags = true})
local flex = themepark:init_theme('nominatim')
flex.modify_main_tags{'amenity' = {
'waste_basket' = 'delete'}
}
```
This example uses the full Nominatim configuration but disables
importing waste baskets.You may also write a new configuration from scratch. Simply omit including
a Nominatim topic and only call the required customization functions.
Customizing the osm2pgsql processing functions as explained
above is not possible
when running under themepark. Instead include other topics that make the
necessary modifications or add an additional processor before including
the Nominatim topic.
!!! Example
``` lua
local themepark = require('themepark')
local function discard_country_boundaries(object)
if object.tags.boundary == 'administrative' and object.tags.admin_level == '2' then
return 'stop'
end
end
themepark:add_proc('relation', discard_country_boundaries)
-- Order matters here. The topic needs to be added after the custom callback.
themepark:add_topic('nominatim/full', {with_extratags = true})
```
Discarding country-level boundaries when running under themepark.Changing the style of existing databases
There is usually no issue changing the style of a database that is already
imported and now kept up-to-date with change files. Just be aware that any
change in the style applies to updates only. If you want to change the data
that is already in the database, then a reimport is necessary.
Customize Importance (`docs/customize/Importance.md`)
Importance
Search requests can yield multiple results which match equally well with
the original query. In such case Nominatim needs to order the results
according to a different criterion: importance. This is a measure for how
likely it is that a user will search for a given place. This section explains
the sources Nominatim uses for computing importance of a place and how to
customize them.
How importance is computed
The main value for importance is derived from page ranking values for Wikipedia
pages for a place. For places that do not have their own
Wikipedia page, a formula is used that derives a static importance from the
place's search rank.
In a second step, a secondary importance value is added which is meant to
represent how well-known the general area is where the place is located. It
functions as a tie-breaker between places with very similar primary
importance values.
nominatim.org has preprocessed importance tables for the
primary Wikipedia rankings
and for secondary importance
based on Wikipedia importance of the administrative areas.
The source code for creating these files is available in the Github projects
osm-search/wikipedia-wikidata
and
osm-search/secondary-importance.
Customizing secondary importance
The secondary importance is implemented as a simple
Postgis raster table, where Nominatim
looks up the value for the coordinates of the centroid of a place. You can
provide your own secondary importance raster in form of an SQL file namedsecondary_importance.sql.gz in your project directory.
The SQL file needs to drop and (re)create a table secondary_importance which
must as a minimum contain a column rast of type raster. The raster must
be in EPSG:4326 and contain 16bit unsigned ints
(`raster_constraint_pixel_types(rast) = '{16BUI}'). Any other columns in the
table will be ignored. You must furthermore create an index as follows:
CREATE INDEX ON secondary_importance USING gist(ST_ConvexHull(gist))The following raster2pgsql command will create a table from a tiff file
that conforms to the requirements:
raster2pgsql -I -C -Y -d -t 128x128 input.tiff public.secondary_importanceCustomize Postcodes (`docs/customize/Postcodes.md`)
External postcode data
Nominatim creates a table of known postcode centroids and geometries during import.
This table is used for searches of postcodes and for adding postcodes to places where
the OSM data does not provide one. These postcode centroids are mainly computed
from the OSM data itself. In addition, Nominatim supports reading postcode information
from external files to supplement the postcodes that are missing in OSM.
Supported file formats
Nominatim supports 2 data formats for external import:
JSONL format
The JSONL format may contain the postcode data along with a POLYGON or MULTIPOLYGON geometry
of the postcode area. To enable external postcode support, put one file per country into
your project directory and name it <CC>_postcodes_geometry.<ext>. <CC> must be the
two-letter country code for which to apply the file. File type may be either .jsonl or.jsonl.gz (gzip compressed). The file must be in UTF-8 encoding and contain one JSON object
per line with the following structure:
GeoJSON Feature
A standard RFC7946 GeoJSON Feature object.
geometry: (required) A GeoJSON geometry (Polygon/MultiPolygon).properties: (required) An object containing:postcode: (required) The postcode string.lat,lon: (optional) Coordinates for the centroid. If not provided, the centroid
is computed from the geometry.
CSV format
To enable external postcode support, put one file per country into
your project directory and name it <CC>_postcodes.<ext>. <CC> must be the
two-letter country code for which to apply the file. File type may be either .csv or .csv.gz
(gzip compressed). The CSV file must use commas as a delimiter and have a header line. Nominatim
expects three columns to be present: postcode, lat and lon. All other columns are ignored.lon and lat must describe the x and y coordinates of the postcode centroids in WGS84.
The postcode area is assumed to be in a buffer around the centroid (typically 5km).
Usage
As a rule, the external postcode data should be put into the project directory
before starting the initial import. Still, you can add, remove and update the
external postcode data at any time. Simply run:
nominatim refresh --postcodesto make the changes visible in your database. Be aware, however, that the changes
only have an immediate effect on searches for postcodes. Postcodes that were
added to places are only updated, when they are reindexed. That usually happens
only during replication updates.
Customize Query Preprocessing (`docs/customize/Query-Preprocessing.md`)
Query preprocessing
The query preprocessing step allows to transform the incoming query before
it is processed by the tokenizer. This includes removing unrelated parts,
rephrasing and phrase tagging.
Configuration
Query preprocessing is defined in the 'query_preprocessing.yaml' configuration
file. The file must contain a list of steps. Each step has a mandatory
parameter step which defines the type of preprocessor. Additional step
configuration may then be set with additional parameters.
The steps are executed in the order that they are defined in the configuration
file. Order matters here: each preprocessor works with the output of the previous
step.
Pre-defined preprocessors
The following is a list of preprocessors that are shipped with Nominatim.
To learn how to define custom preprocessors, see the section on
query processing modules.
split-japanese-phrases
::: nominatim_api.query_preprocessing.split_japanese_phrases
options:
members: False
heading_level: 6
docstring_section_style: spacy
regex-replace
::: nominatim_api.query_preprocessing.regex_replace
options:
members: False
heading_level: 6
docstring_section_style: spacy
Customize Ranking (`docs/customize/Ranking.md`)
Place Ranking in Nominatim
Nominatim uses two metrics to rank a place: search rank and address rank.
This chapter explains what place ranking means and how it can be customized.
Search rank
The search rank describes the extent and importance of a place. It is used
when ranking search results. Simply put, if there are two results for a
search query which are otherwise equal, then the result with the lower
search rank will be appear higher in the result list.
Search ranks are not so important these days because many well-known
places use the Wikipedia importance ranking instead.
The following table gives an overview of the kind of features that Nominatim
expects for each rank:
| rank | typical place types | extent |
|---|---|---|
| 1-3 | oceans, continents | - |
| 4 | countries | - |
| 5-9 | states, regions, provinces | - |
| 10-12 | counties | - |
| 13-16 | cities, municipalities, islands | 15 km |
| 17-18 | towns, boroughs | 4 km |
| 19 | villages, suburbs | 2 km |
| 20 | hamlets, farms, neighbourhoods | 1 km |
| 21-25 | isolated dwellings, city blocks | 500 m |
The extent column describes how far a feature is assumed to reach when it
is mapped only as a point. Larger features like countries and states are usually
available with their exact area in the OpenStreetMap data. That is why no extent
is given.
Address rank
The address rank describes where a place shows up in an address hierarchy.
Usually only administrative boundaries and place nodes and areas are
eligible to be part of an address. Places that should not appear in the
address must have an address rank of 0.
The following table gives an overview how ranks are mapped to address parts:
| rank | address part |
|---|---|
| 1-3 | unused |
| 4 | country |
| 5-9 | state |
| 10-12 | county |
| 13-16 | city |
| 17-21 | suburb |
| 22-24 | neighbourhood |
| 25 | squares, farms, localities |
| 26-27 | street |
| 28-30 | POI/house number |
The country rank 4 usually doesn't show up in the address parts of an object.
The country is determined indirectly from the country code.
Ranks 5-24 can be assigned more or less freely. They make up the major part
of the address.
Rank 25 is also an addressing rank but it is special because while it can be
the parent to a POI with an addr:place of the same name, it cannot be a parent
to streets. Use it for place features that are technically on the same level
as a street (e.g. squares, city blocks) or for places that should not normally
appear in an address unless explicitly tagged so (e.g place=locality which
should be uninhabited and as such not addressable).
The street ranks 26 and 27 are handled slightly differently. Only one object
from these ranks shows up in an address.
For POI level objects like shops, buildings or house numbers always use rank 30.
Ranks 28 is reserved for house number interpolations. 29 is for internal use
only.
Rank configuration
Search and address ranks are assigned to a place when it is first imported
into the database. There are a few hard-coded rules for the assignment:
- postcodes follow special rules according to their length
- boundaries that are not areas and railway=rail are dropped completely
- the following are always search rank 30 and address rank 0:
- highway nodes
- landuse that is not an area
Other than that, the ranks can be freely assigned via the JSON file according
to their type and the country they are in. The name of the config file to be
used can be changed with the setting NOMINATIM_ADDRESS_LEVEL_CONFIG.
The address level configuration must consist of an array of configuration
entries, each containing a tag definition and an optional country array:
[ {
"tags" : {
"place" : {
"county" : 12,
"city" : 16,
},
"landuse" : {
"residential" : 22,
"" : 30
}
}
},
{
"countries" : [ "ca", "us" ],
"tags" : {
"boundary" : {
"administrative8" : 18,
"administrative9" : 20
},
"landuse" : {
"residential" : [22, 0]
}
}
}
]The countries field contains a list of countries (as ISO 3166-1 alpha 2 code)
for which the definition applies. When the field is omitted, then the
definition is used as a fallback, when nothing more specific for a given
country exists.
tags contains the ranks for key/value pairs. The ranks can be either a
single number, in which case they are the search and address rank, or an array
of search and address rank (in that order). The value may be left empty.
Then the rank is used when no more specific value is found for the given
key.
Countries and key/value combination may appear in multiple definitions. Just
make sure that each combination of country/key/value appears only once per
file. Otherwise the import will fail with a UNIQUE INDEX constraint violation
on import.
Customize Result Formatting (`docs/customize/Result-Formatting.md`)
Changing the Appearance of Results in the Server API
The Nominatim Server API offers a number of formatting options that
present search results in different output formats.
These results only contain a subset of all the information that Nominatim
has about the result. This page explains how to adapt the result output
or add additional result formatting.
Defining custom result formatting
To change the result output, you need to place a file api/v1/format.py
into your project directory. This file needs to define a single variabledispatch containing a FormatDispatcher. This class
serves to collect the functions for formatting the different result types
and offers helper functions to apply the formatters.
There are two ways to define the dispatch variable. If you want to reuse
the default output formatting and just make some changes or add an additional
format type, then import the dispatch object from the default API:
from nominatim_api.v1.format import dispatch as dispatchIf you prefer to define a completely new result output, then you can
create an empty dispatcher object:
from nominatim_api import FormatDispatcher
dispatch = FormatDispatcher()The formatting function
The dispatcher organises the formatting functions by format and result type.
The format corresponds to the format parameter of the API. It can contain
one of the predefined format names or you can invent your own new format.
API calls return data classes or an array of a data class which represent
the result. You need to make sure there are formatters defined for the
following result types:
- StatusResult (single object, returned by
/status) - DetailedResult (single object, returned by
/details) - SearchResults (list of objects, returned by
/search) - ReverseResults (list of objects, returned by
/reverseand/lookup) - RawDataList (simple object, returned by
/deletableand/polygons)
A formatter function has the following signature:
def format_func(result: ResultType, options: Mapping[str, Any]) -> strThe options dictionary contains additional information about the original
query. See the reference below
about the possible options.
To set the result formatter for a certain result type and format, you need
to write the format function and decorate it with theformat_func
decorator.
For example, let us extend the result for the status call in text format
and add the server URL. Such a formatter would look like this:
from nominatim_api import StatusResult
@dispatch.format_func(StatusResult, 'text')
def _format_status_text(result, _):
header = 'Status for server nominatim.openstreetmap.org'
if result.status:
return f"{header}\n\nERROR: {result.message}"
return f"{header}\n\nOK"If your dispatcher is derived from the default one, then this definition
will overwrite the original formatter function. This way it is possible
to customize the output of selected results.
Adding new formats
You may also define a completely different output format. This is as simple
as adding formatting functions for all result types using the custom
format name:
from nominatim_api import StatusResult
@dispatch.format_func(StatusResult, 'chatty')
def _format_status_text(result, _):
if result.status:
return f"The server is currently not running. {result.message}"
return "Good news! The server is running just fine."That's all. Nominatim will automatically pick up the new format name and
will allow the user to use it. There is no need to implement formatter
functions for all the result types, when you invent a new one. The
available formats will be determined for each API endpoint separately.
To find out which formats are available, you can use the --list-formats
option of the CLI tool:
me@machine:planet-project$ nominatim status --list-formats
2024-08-16 19:54:00: Using project directory: /home/nominatim/planet-project
text
json
chatty
debug
me@machine:planet-project$The debug format listed in the last line will always appear. It is a
special format that enables debug output via the command line (the same
as the debug=1 parameter enables for the server API). To not clash
with this built-in function, you shouldn't name your own format 'debug'.
Note that the debug format of the CLI is always available, while thedebug=1 parameter of the server API needs to be enabled explicitly with
NOMINATIM_SERVE_DEBUG_OUTPUT.
Content type of new formats
All responses will be returned with the content type application/json by
default. If your format produces a different content type, you need
to configure the content type with the set_content_type() function.
For example, the 'chatty' format above returns just simple text. So the
content type should be set up as:
from nominatim_api.server.content_types import CONTENT_TEXT
dispatch.set_content_type('chatty', CONTENT_TEXT)The content_types module used above provides constants for the most
frequent content types. You set the content type to an arbitrary string,
if the content type you need is not available.
Formatting error messages
Any exception thrown during processing of a request is given to
a special error formatting function. It takes the requested content type,
the status code and the error message. It should return the error message
in a form appropriate for the given content type.
You can overwrite the default formatting function with the decoratorerror_format_func:
import json
from xml.sax.saxutils import escape
import nominatim_api.server.content_types as ct
@dispatch.error_format_func
def _format_error(content_type: str, msg: str, status: int) -> str:
if content_type == ct.CONTENT_XML:
return f"""<?xml version="1.0" encoding="UTF-8" ?>
<message>{escape(msg)}</message>
"""
if content_type == ct.CONTENT_JSON:
return json.dumps(msg)
return f"ERROR: {msg}"Debugging custom formatters
The easiest way to try out your custom formatter is by using the Nominatim
CLI commands. Custom formats can be chosen with the --format parameter:
me@machine:planet-project$ nominatim status --format chatty
2024-08-16 19:54:00: Using project directory: /home/nominatim/planet-project
Good news! The server is running just fine.
me@machine:planet-project$They will also emit full error messages when there is a problem with the
code you need to debug.
!!! danger
In some cases, when you make an error with your import statement, the
CLI will not give you an error but instead tell you, that the API
commands are no longer available:
me@machine: nominatim status
usage: nominatim [-h] [--version] {import,freeze,replication,special-phrases,add-data,index,refresh,admin} ...
nominatim: error: argument subcommand: invalid choice: 'status'
This happens because the CLI tool is meant to still work when the
nominatim-api package is not installed. Import errors involving
`nominatim_api` are interpreted as "package not installed".
Use the help command to find out which is the offending import that
could not be found:
me@machine: nominatim -h
... [other help text] ...
Nominatim API package not found (was looking for module: nominatim_api.xxx).Reference
FormatDispatcher
::: nominatim_api.FormatDispatcher
options:
heading_level: 6
group_by_category: False
JsonWriter
::: nominatim_api.utils.json_writer.JsonWriter
options:
heading_level: 6
group_by_category: False
Options for different result types
This section lists the options that may be handed in with the different result
types in the v1 version of the Nominatim API.
StatusResult
None.
DetailedResult
| Option | Description |
|---|---|
| locales | Locales object for the requested language(s) |
| group_hierarchy | Setting of group_hierarchy parameter |
| icon_base_url | (optional) URL pointing to icons as set in NOMINATIM_MAPICON_URL |
SearchResults
| Option | Description |
|---|---|
| query | Original query string |
| more_url | URL for requesting additional results for the same query |
| exclude_place_ids | List of IDs already returned (OSM IDs where possible, otherwise place_ids) |
| viewbox | Setting of viewbox parameter |
| extratags | Setting of extratags parameter |
| namedetails | Setting of namedetails parameter |
| addressdetails | Setting of addressdetails parameter |
ReverseResults
| Option | Description |
|---|---|
| query | Original query string |
| extratags | Setting of extratags parameter |
| namedetails | Setting of namedetails parameter |
| addressdetails | Setting of addressdetails parameter |
RawDataList
None.
Customize SQLite (`docs/customize/SQLite.md`)
A Nominatim database can be converted into an SQLite database and used as
a read-only source for geocoding queries. This sections describes how to
create and use an SQLite database.
!!! danger
This feature is in an experimental state at the moment. Use at your own
risk.
Installing prerequisites
To use a SQLite database, you need to install:
- SQLite (>= 3.30)
- Spatialite (> 5.0.0)
- aiosqlite
On Ubuntu/Debian, you can run:
sudo apt install sqlite3 libsqlite3-mod-spatialite libspatialite7Install the aiosqlite Python package in your virtual environment:
/srv/nominatim-venv/bin/pip install aiosqliteCreating a new SQLite database
Nominatim cannot import directly into SQLite database. Instead you have to
first create a geocoding database in PostgreSQL by running a
regular Nominatim import.
Once this is done, the database can be converted to SQLite with
nominatim convert -o mydb.sqliteThis will create a database where all geocoding functions are available.
Depending on what functions you need, the database can be made smaller:
--without-reverseomits indexes only needed for reverse geocoding--without-searchomit tables and indexes used for forward search--without-detailsleaves out extra information only available in the
details API
Using an SQLite database
Once you have created the database, you can use it by simply pointing the
database DSN to the SQLite file:
NOMINATIM_DATABASE_DSN=sqlite:dbname=mydb.sqlitePlease note that SQLite support is only available for the Python frontend. To
use the test server with an SQLite database, you therefore need to switch
the frontend engine:
nominatim serve --engine falconYou need to install falcon or starlette for this, depending on which engine
you choose.
The CLI query commands and the library interface already use the new Python
frontend and therefore work right out of the box.
Customize Sanitizers (`docs/customize/Sanitizers.md`)
Sanitizers
Sanitizing is the process of cleaning up and otherwise preprocessing names
before adding them to the search index during the import process. This allows
to clean up tagging, normalise different spellings and mark names with extra
attributes for further processing.
!!! hint
Sanitizers only have an effect on how the search index is built. They
do not change the information about each place that is saved in the
database. In particular, they have no influence on how the results are
displayed. The returned results always show the original information as
stored in the OpenStreetMap database.
Configuration
The sanitizing process is defined in the 'sanitizers.yaml' configuration
file. The file must contain a list of steps. Each step has a mandatory
parameter step which defines the type of sanitizer. Additional step
configuration may then be set with additional parameters.
The steps are executed in the order that they are defined in the configuration
file. Order matters here: each sanitizer works with the output of the previous
step.
Pre-defined sanitizers
The following is a list of sanitizers that are shipped with Nominatim.
To learn about how to add your own custom sanitizer, see the section on
custom sanitizer modules.
affix-expansion
::: nominatim_db.tokenizer.sanitizers.affix_expansion
options:
members: False
heading_level: 6
docstring_section_style: spacy
clean-housenumbers
::: nominatim_db.tokenizer.sanitizers.clean_housenumbers
options:
members: False
heading_level: 6
docstring_section_style: spacy
clean-multilingual-names
::: nominatim_db.tokenizer.sanitizers.clean_multilingual_names
options:
members: False
heading_level: 6
docstring_section_style: spacy
clean-postcodes
::: nominatim_db.tokenizer.sanitizers.clean_postcodes
options:
members: False
heading_level: 6
docstring_section_style: spacy
clean-tiger-tags
::: nominatim_db.tokenizer.sanitizers.clean_tiger_tags
options:
members: False
heading_level: 6
docstring_section_style: spacy
delete-names
::: nominatim_db.tokenizer.sanitizers.delete_names
options:
members: False
heading_level: 6
docstring_section_style: spacy
derive-names
::: nominatim_db.tokenizer.sanitizers.derive_names
options:
members: False
heading_level: 6
docstring_section_style: spacy
split-name-list
::: nominatim_db.tokenizer.sanitizers.split_name_list
options:
members: False
heading_level: 6
docstring_section_style: spacy
strip-brace-terms
::: nominatim_db.tokenizer.sanitizers.strip_brace_terms
options:
members: False
heading_level: 6
docstring_section_style: spacy
tag-analyzer-by-language
::: nominatim_db.tokenizer.sanitizers.tag_analyzer_by_language
options:
members: False
heading_level: 6
docstring_section_style: spacy
tag-japanese
::: nominatim_db.tokenizer.sanitizers.tag_japanese
options:
members: False
heading_level: 6
docstring_section_style: spacy
Customize Settings (`docs/customize/Settings.md`)
This section provides a reference of all configuration parameters that can
be used with Nominatim.
Configuring Nominatim
Nominatim uses dotenv to manage
its configuration settings. There are two means to set configuration
variables: through an .env configuration file or through an environment
variable.
The .env configuration file needs to be placed into the
project directory. It
must contain configuration parameters in =<value> format.
Please refer to the dotenv documentation for details.
The configuration options may also be set in the form of shell environment
variables. This is particularly useful, when you want to temporarily change
a configuration option. For example, to force the replication serve to
download the next change, you can temporarily disable the update interval:
NOMINATIM_REPLICATION_UPDATE_INTERVAL=0 nominatim replication --onceIf a configuration option is defined through .env file and environment
variable, then the latter takes precedence.
Configuration Parameter Reference
Import and Database Settings
NOMINATIM_DATABASE_DSN
| Summary | |
|---|---|
| Description: | Database connection string |
| Format: | string: pgsql:=<value1>;=<value2>;... |
| Default: | pgsql:dbname=nominatim |
| After Changes: | run nominatim refresh --website |
Sets the connection parameters for the Nominatim database. At a minimum
the name of the database (dbname) is required. You can set any additional
parameter that is understood by libpq. See the Postgres documentation for a full list.
!!! note
It is usually recommended not to set the password directly in this
configuration parameter. Use a
password file
instead.
NOMINATIM_DATABASE_WEBUSER
| Summary | |
|---|---|
| Description: | Database query user |
| Format: | string |
| Default: | www-data |
| After Changes: | cannot be changed after import |
Defines the name of the database user that will run search queries. Usually
this is the user under which the webserver is executed. The Postgres user
needs to be set up before starting the import.
Nominatim grants minimal rights to this user to all tables that are needed
for running geocoding queries.
NOMINATIM_TOKENIZER
| Summary | |
|---|---|
| Description: | Tokenizer used for normalizing and parsing queries and names |
| Format: | string |
| Default: | icu |
| After Changes: | cannot be changed after import |
Sets the tokenizer type to use for the import. For more information on
available tokenizers and how they are configured, see
Tokenizers.
NOMINATIM_TOKENIZER_CONFIG
| Summary | |
|---|---|
| Description: | Configuration file for the tokenizer |
| Format: | path |
| Default: | empty (default file depends on tokenizer) |
| After Changes: | see documentation for each tokenizer |
Points to the file with additional configuration for the tokenizer.
See the Tokenizer descriptions for details
on the file format.
If a relative path is given, then the file is searched first relative to the
project directory and then in the global settings directory.
NOMINATIM_LIMIT_REINDEXING
| Summary | |
|---|---|
| Description: | Avoid invalidating large areas |
| Format: | bool |
| Default: | yes |
Nominatim computes the address of each place at indexing time. This has the
advantage to make search faster but also means that more objects needs to
be invalidated when the data changes. For example, changing the name of
the state of Florida would require recomputing every single address point
in the state to make the new name searchable in conjunction with addresses.
Setting this option to 'yes' means that Nominatim skips reindexing of contained
objects when the area becomes too large.
NOMINATIM_LANGUAGES
| Summary | |
|---|---|
| Description: | Restrict search languages |
| Format: | string: comma-separated list of language codes |
| Default: | empty |
Normally Nominatim will include all language variants of name:XX
in the search index. Set this to a comma separated list of language
codes, to restrict import to a subset of languages.
Currently only affects the initial import of country names and special phrases.
NOMINATIM_USE_US_TIGER_DATA
| Summary | |
|---|---|
| Description: | Enable searching for Tiger house number data |
| Format: | boolean |
| Default: | no |
| After Changes: | run nominatim refresh --functions |
When this setting is enabled, search and reverse queries also take data
from Tiger house number data into account.
NOMINATIM_OSM2PGSQL_BINARY
| Summary | |
|---|---|
| Description: | Location of the osm2pgsql binary |
| Format: | path |
| Default: | empty (use binary shipped with Nominatim) |
| Comment: | EXPERT ONLY |
Nominatim uses osm2pgsql to load the OSM data
initially into the database. Nominatim comes bundled with a version of
osm2pgsql that is guaranteed to be compatible. Use this setting to use
a different binary instead. You should do this only when you know exactly
what you are doing. If the osm2pgsql version is not compatible, then the
result is undefined.
NOMINATIM_WIKIPEDIA_DATA_PATH
| Summary | |
|---|---|
| Description: | Directory with the wikipedia importance data |
| Format: | path |
| Default: | empty (project directory) |
Set a custom location for the
wikipedia ranking file. When
unset, Nominatim expects the data to be saved in the project directory.
NOMINATIM_ADDRESS_LEVEL_CONFIG
| Summary | |
|---|---|
| Description: | Configuration file for rank assignments |
| Format: | path |
| Default: | address-levels.json |
The address level configuration defines the rank assignments for places. See
Place Ranking for a detailed explanation what rank assignments
are and what the configuration file must look like.
When a relative path is given, then the file is searched first relative to the
project directory and then in the global settings directory.
NOMINATIM_IMPORT_STYLE
| Summary | |
|---|---|
| Description: | Configuration to use for the initial OSM data import |
| Format: | string or path |
| Default: | extratags |
The style configuration describes which OSM objects and tags are taken
into consideration for the search database. Nominatim comes with a set
of pre-configured styles, that may be configured here.
You can also write your own custom style and point the setting to the file
with the style. When a relative path is given, then the style file is searched
first relative to the project directory and then in the global settings
directory.
See Import Styles
for more information on the available internal styles and the format of the
configuration file.
NOMINATIM_FLATNODE_FILE
| Summary | |
|---|---|
| Description: | Location of osm2pgsql flatnode file |
| Format: | path |
| Default: | empty (do not use a flatnote file) |
| After Changes: | Only change when moving the file physically. |
The osm2pgsql flatnode file is file that efficiently stores geographic
location for OSM nodes. For larger imports it can significantly speed up
the import. When this option is unset, then osm2pgsql uses a PsotgreSQL table
to store the locations.
When a relative path is given, then the flatnode file is created/searched
relative to the project directory.
!!! warning
The flatnode file is not only used during the initial import but also
when adding new data with `nominatim add-data` or `nominatim replication`.
Make sure you keep the flatnode file around and this setting unmodified,
if you plan to add more data or run regular updates.NOMINATIM_TABLESPACE_*
| Summary | |
|---|---|
| Description: | Group of settings for distributing the database over tablespaces |
| Format: | string |
| Default: | empty (do not use a table space) |
| After Changes: | no effect after initial import |
Nominatim allows to distribute the search database over up to 10 different
PostgreSQL tablespaces.
If you use this option, make sure that the tablespaces exist before starting
the import.
The available tablespace groups are:
NOMINATIM_TABLESPACE_SEARCH_DATA
: Data used by the geocoding frontend.
NOMINATIM_TABLESPACE_SEARCH_INDEX
: Indexes used by the geocoding frontend.
NOMINATIM_TABLESPACE_OSM_DATA
: Raw OSM data cache used for import and updates.
NOMINATIM_TABLESPACE_OSM_INDEX
: Indexes on the raw OSM data cache.
NOMINATIM_TABLESPACE_PLACE_DATA
: Data table with the pre-filtered but still unprocessed OSM data.
Used only during imports and updates.
NOMINATIM_TABLESPACE_PLACE_INDEX
: Indexes on raw data table. Used only during imports and updates.
NOMINATIM_TABLESPACE_ADDRESS_DATA
: Data tables used for computing search terms and addresses of places
during import and updates.
NOMINATIM_TABLESPACE_ADDRESS_INDEX
: Indexes on the data tables for search term and address computation.
Used only for import and updates.
NOMINATIM_TABLESPACE_AUX_DATA
: Auxiliary data tables for non-OSM data, e.g. for Tiger house number data.
NOMINATIM_TABLESPACE_AUX_INDEX
: Indexes on auxiliary data tables.
Replication Update Settings
NOMINATIM_REPLICATION_URL
| Summary | |
|---|---|
| Description: | Base URL of the replication service |
| Format: | url |
| Default: | https://planet.openstreetmap.org/replication/minute |
| After Changes: | run nominatim replication --init |
Replication services deliver updates to OSM data. Use this setting to choose
which replication service to use. See Updates for more
information on how to set up regular updates.
NOMINATIM_REPLICATION_MAX_DIFF
| Summary | |
|---|---|
| Description: | Maximum amount of data to download per update cycle (in MB) |
| Format: | integer |
| Default: | 50 |
| After Changes: | restart the replication process |
At each update cycle Nominatim downloads diffs until either no more diffs
are available on the server (i.e. the database is up-to-date) or the limit
given in this setting is exceeded. Nominatim guarantees to downloads at least
one diff, if one is available, no matter how small the setting.
The default for this setting is fairly conservative because Nominatim keeps
all data downloaded in one cycle in RAM. Using large values in a production
server may interfere badly with the search frontend because it evicts data
from RAM that is needed for speedy answers to incoming requests. It is usually
a better idea to keep this setting lower and run multiple update cycles
to catch up with updates.
When catching up in non-production mode, for example after the initial import,
the setting can easily be changed temporarily on the command line:
NOMINATIM_REPLICATION_MAX_DIFF=3000 nominatim replicationNOMINATIM_REPLICATION_UPDATE_INTERVAL
| Summary | |
|---|---|
| Description: | Publication interval of the replication service (in seconds) |
| Format: | integer |
| Default: | 75 |
| After Changes: | restart the replication process |
This setting determines when Nominatim will attempt to download again a new
update. The time is computed from the publication date of the last diff
downloaded. Setting this to a slightly higher value than the actual
publication interval avoids unnecessary rechecks.
NOMINATIM_REPLICATION_RECHECK_INTERVAL
| Summary | |
|---|---|
| Description: | Wait time to recheck for a pending update (in seconds) |
| Format: | integer |
| Default: | 60 |
| After Changes: | restart the replication process |
When replication updates are run in continuous mode (using nominatim replication),
this setting determines how long Nominatim waits until it looks for updates
again when updates were not available on the server.
Note that this is different from
NOMINATIM_REPLICATION_UPDATE_INTERVAL.
Nominatim will never attempt to query for new updates for UPDATE_INTERVAL
seconds after the current database date. Only after the update interval has
passed it asks for new data. If then no new data is found, it waits for
RECHECK_INTERVAL seconds before it attempts again.
API Settings
NOMINATIM_CORS_NOACCESSCONTROL
| Summary | |
|---|---|
| Description: | Send permissive CORS access headers |
| Format: | boolean |
| Default: | yes |
| After Changes: | run nominatim refresh --website |
When this setting is enabled, API HTTP responses include the HTTP
CORS headersaccess-control-allow-origin: * and access-control-allow-methods: OPTIONS,GET.
NOMINATIM_MAPICON_URL
| Summary | |
|---|---|
| Description: | URL prefix for static icon images |
| Format: | url |
| Default: | empty |
| After Changes: | run nominatim refresh --website |
When a mapicon URL is configured, then Nominatim includes an additional icon
field in the responses, pointing to an appropriate icon for the place type.
Map icons used to be included in Nominatim itself but now have moved to the
nominatim-ui project. If you
want the URL to be included in API responses, make the /mapicon
directory of the project available under a public URL and point this setting
to the directory.
NOMINATIM_DEFAULT_LANGUAGE
| Summary | |
|---|---|
| Description: | Language of responses when no language is requested |
| Format: | language code |
| Default: | empty (use the local language of the feature) |
| After Changes: | run nominatim refresh --website |
Nominatim localizes the place names in responses when the corresponding
translation is available. Users can request a custom language setting through
the HTTP accept-languages header or through the explicit parameter
accept-languages. If neither is
given, it falls back to this setting. If the setting is also empty, then
the local languages (in OSM: the name tag without any language suffix) is
used.
NOMINATIM_LOOKUP_MAX_COUNT
| Summary | |
|---|---|
| Description: | Maximum number of OSM ids accepted by /lookup |
| Format: | integer |
| Default: | 50 |
| After Changes: | run nominatim refresh --website |
The /lookup point accepts list of ids to look up address details for. This
setting restricts the number of places a user may look up with a single
request.
NOMINATIM_POLYGON_OUTPUT_MAX_TYPES
| Summary | |
|---|---|
| Description: | Number of different geometry formats that may be returned |
| Format: | integer |
| Default: | 1 |
| After Changes: | run nominatim refresh --website |
Nominatim supports returning full geometries of places. The geometries may
be requested in different formats with one of thepolygon_* parameters. Use this
setting to restrict the number of geometry types that may be requested
with a single query.
Setting this parameter to 0 disables polygon output completely.
NOMINATIM_SEARCH_WITHIN_COUNTRIES
| Summary | |
|---|---|
| Description: | Disable search for elements that are not in the country grid |
| Format: | boolean |
| Default: | no |
| After Changes: | run nominatim refresh --website |
Enable to search elements just within countries.
When enabled, if, despite not finding a point within the static grid of countries, it
finds a geometry of a region, do not return the geometry.
Return "Unable to geocode" instead.
NOMINATIM_SERVE_LEGACY_URLS
| Summary | |
|---|---|
| Description: | Enable serving via URLs with a .php suffix |
| Format: | boolean |
| Default: | yes |
When enabled, then endpoints are reachable as /<name> as well as /<name>.php.
This can be useful when you want to be backwards-compatible with previous
versions of Nominatim.
NOMINATIM_SERVE_DEBUG_OUTPUT
| Summary | |
|---|---|
| Description: | Enable the HTML debug output |
| Format: | boolean |
| Default: | no |
When enabled, requests to /search, /reverse, /lookup and /details with
the parameter debug=1 return an HTML page detailing how the query was
parsed, understood and executed.
The debug format of the command-line tool (nominatim search --format debug),
which has a similar output (text instead of HTML), is always available.
NOMINATIM_API_POOL_SIZE
| Summary | |
|---|---|
| Description: | Number of parallel database connections per worker |
| Format: | number |
| Default: | 10 |
Sets the maximum number of database connections available for a single instance
of Nominatim. When configuring the maximum number of connections that your
PostgreSQL database can handle, you need at leastNOMINATIM_API_POOL_SIZE * <number of configured workers> connections.
For configuring the number of workers, refer to the section about
Deploying the Python frontend.
NOMINATIM_QUERY_TIMEOUT
| Summary | |
|---|---|
| Description: | Timeout for SQL queries to the database |
| Format: | number (seconds) |
| Default: | 10 |
When this timeout is set, then all SQL queries that run longer than the
specified numbers of seconds will be cancelled and the user receives a
timeout exceptions. Users of the API see a 503 HTTP error.
The timeout does ont apply when using the
low-level DB access
of the library. A timeout can be manually set, if required.
NOMINATIM_REQUEST_TIMEOUT
| Summary | |
|---|---|
| Description: | Timeout for search queries |
| Format: | number (seconds) |
| Default: | 60 |
When this timeout is set, a search query will finish sending queries
to the database after the timeout has passed and immediately return the
results gathered so far.
Note that under high load you may observe that users receive different results
than usual without seeing an error. This may cause some confusion.
NOMINATIM_OUTPUT_NAMES
| Summary | |
|---|---|
| Description: | Specifies order of name tags |
| Format: | string: comma-separated list of tag names |
| Default: | name:XX,name,brand,official_name:XX,short_name:XX,official_name,short_name,ref |
Specifies the order in which different name tags are used.
The values in this list determine the preferred order of name variants,
including language-specific names (in OSM: the name tag with and without any language suffix).
Comma-separated list, where :XX stands for language suffix
(e.g. name:en) and no :XX stands for general tags (e.g. name).
See also NOMINATIM_DEFAULT_LANGUAGE.
!!! note
If NOMINATIM_OUTPUT_NAMES = name:XX,name,short_name:XX,short_name the search follows
```
'name', 'short_name'
```
if we have no preferred language order for showing search results.
For languages ['en', 'es'] the search follows
```
'name:en', 'name:es',
'name',
'short_name:en', 'short_name:es',
'short_name'
```
For those familiar with the internal implementation, the `_place_*` expansion is added, but to simplify, it is not included in this example.Logging Settings
NOMINATIM_LOG_FILE
| Summary | |
|---|---|
| Description: | Log requests into a file |
| Format: | path |
| Default: | empty (logging disabled) |
Enable logging of requests into a file with this setting by setting the log
file where to log to. A relative file name is assumed to be relative to
the project directory. The format of the log output can be set
with NOMINATIM_LOG_FORMAT.
NOMINATIM_LOG_FORMAT
| Summary | |
|---|---|
| Description: | Log requests into a file |
| Format: | Python String Format string |
| Default: | [{start}] {total_time:.4f} {results_total} {endpoint} "{query_string}" |
Describes the content of a log line for a single request. The format
must be readable by Python's format function. Nominatim provides a number
of metrics than can be logged. The default set of metrics is the following:
/// html | div.simple-table
| name | type | Description |
|---|---|---|
| start | time | Point in time when the request arrived. |
| end | time | Point in time when the request was done. |
| query_start | time | Point in time when processing started. |
| total_time | float | Total time in seconds to handle the request. |
| wait_time | float | Time in seconds the request waited for a database connection to be available. |
| query_time | float | Total time in seconds to process the request once a connection was available. |
| results_total | int | Number of results found. |
| endpoint | string | API endpoint used. |
| query_string | string | Raw query string received. |
| /// |
Variables of type 'time' contain a UTC timestamp string in ISO format.
Nominatim also exposes additional metrics to help with development. These
are subject to change between versions:
/// html | div.simple-table
| name | type | Description |
|---|---|---|
| search_rounds | int | Total number of searches executed for the request. |
| search_min_penalty | float | Minimal possible penalty for the request. |
| search_first_result_round | int | Number of first search to yield any result. |
| search_min_result_penalty | float | Minimal penalty by a result found. |
| search_best_penalty_round | int | Search round that yielded the best penalty result. |
| /// |
NOMINATIM_DEBUG_SQL
| Summary | |
|---|---|
| Description: | Enable printing of raw SQL by SQLAlchemy |
| Format: | boolean |
| Default: | no |
| Comment: | For developers only. |
This settings enables
SQL debugging
by SQLAlchemy. This can be helpful when debugging some bugs with internal
query handling. It should only be used together with the CLI query functions.
Enabling it for server mode may have unintended consequences. Use the debug
parameter instead, which prints information on how the search is executed
including SQL statements.
Customize Special Phrases (`docs/customize/Special-Phrases.md`)
Special phrases
Importing OSM user-maintained special phrases
As described in the Import section, it is possible to
import special phrases from the wiki with the following command:
nominatim special-phrases --import-from-wikiImporting custom special phrases
Special phrases may also be imported from any custom CSV file. The file needs
to have a header line, use comma as delimiter and define the following
columns:
- phrase: the keyword to look for
- class: key of the main tag of the place to find
(see Import styles - type: value of the main tag
- operator: type of special phrase, may be one of:
- in: place is within the place defined by the search term (e.g. "Hotels in Berlin")
- near: place is near the place defined by the search term (e.g. "bus stops near Big Ben")
- named: special phrase is a classifier (e.g. "hotel California")
- -: unspecified, can be any of the above
If the file contains any other columns, then they are silently ignored
To import the CSV file, use the following command:
nominatim special-phrases --import-from-csv <csv file>Note that the two previous import commands will update the phrases from your database.
This means that if you import some phrases from a CSV file, only the phrases
present in the CSV file will be kept in the database. All other phrases will
be removed.
If you want to only add new phrases and not update the other ones you can add
the argument --no-replace to the import command. For example:
nominatim special-phrases --import-from-csv <csv file> --no-replaceThis will add the phrases present in the CSV file into the database without
removing the other ones.
Customize Tiger (`docs/customize/Tiger.md`)
Installing TIGER housenumber data for the US
Nominatim is able to use the official TIGER
address set to complement the OSM house number data in the US. You can add
TIGER data to your own Nominatim instance by following these steps. The
entire US adds about 10GB to your database.
Get preprocessed TIGER data:
cd $PROJECT_DIR
wget https://nominatim.org/data/tiger-nominatim-preprocessed-latest.csv.tar.gzImport the data into your Nominatim database:
nominatim add-data --tiger-data tiger-nominatim-preprocessed-latest.csv.tar.gz
Enable use of the Tiger data in your existing
.envfile by adding:echo NOMINATIM_USE_US_TIGER_DATA=yes >> .env
Apply the new settings:
nominatim refresh --functions --website
Importing only part of the US
If your database covers only part of the US, you can speed up the import by
cutting the TIGER data down to that area first. Use the scripttiger_create_extract.py from the
TIGER-data project. It needs
nothing but Python 3. Select states by abbreviation or FIPS code (--states),
a bounding box (--bbox), or both:
python3 tiger_create_extract.py --states NY,NJ \
tiger-nominatim-preprocessed-latest.csv.tar.gz tiger-ny-nj/The result is a directory of CSV files. Use it in step 2 in place of the
archive:
nominatim add-data --tiger-data tiger-ny-nj/Run python3 tiger_create_extract.py --help for more examples.
See the TIGER-data project for more
information on how the data got preprocessed.
Customize Tokenizers (`docs/customize/Tokenizers.md`)
Tokenizers
The tokenizer module in Nominatim is responsible for analysing the names given
to OSM objects and the terms of an incoming query in order to make sure, they
can be matched appropriately.
Nominatim currently offers only one tokenizer module, the ICU tokenizer. This section
describes the tokenizer and how it can be configured.
!!! important
The selection of tokenizer is tied to a database installation. You need to choose
and configure the tokenizer before starting the initial import. Once the import
is done, you cannot switch to another tokenizer anymore. Reconfiguring the
chosen tokenizer is very limited as well. See the comments in each tokenizer
section.
ICU tokenizer
The ICU tokenizer uses the ICU library to
normalize names and queries. It also offers configurable decomposition and
abbreviation handling.
This tokenizer is currently the default.
To enable the tokenizer add the following line to your project configuration:
NOMINATIM_TOKENIZER=icuHow it works
On import the tokenizer receives the sanaitzed list of names and
processes it in the following two stages:
- The normalization part removes all information from the full names
that are not relevant for search. - The token analysis step takes the normalized full names and creates
all transliterated variants under which the name should be searchable.
See the Token analysis section below for more
information.
During query time, the tokeinzer is responsible for processing incoming
queries. This happens in two stages:
- During query preprocessing the incoming text is split into name
chunks and normalised. This usually means applying the same normalisation
as during the import process but may involve other processing like,
for example, word break detection. - The token analysis step breaks down the query parts into tokens,
looks them up in the database and assigns them possible functions and
probabilities.
Query processing can be further customized while the rest of the analysis
is hard-coded.
Configuration
The ICU tokenizer is configured using a YAML file which can be configured usingNOMINATIM_TOKENIZER_CONFIG. The configuration is read on import and then
saved as part of the internal database status. Later changes to the variable
have no effect.
Here is an example configuration file:
normalization:
- ":: lower ()"
- "ß > 'ss'" # German szet is unambiguously equal to double ss
transliteration:
- !include /etc/nominatim/icu-rules/extended-unicode-to-asccii.yaml
- ":: Ascii ()"
tokenizer-sanitizers:
- step: tag-analyzer-by-language
token-analysis:
- analyzer: generic
variants:
- !include icu-rules/variants-ca.yaml
- words:
- road -> rd
- bridge -> bdge,br,brdg,bri,brg
mutations:
- pattern: 'ä'
replacements: ['ä', 'ae']The configuration file contains four sections:normalization, transliteration, tokenizer-sanitizers and token-analysis.
Normalization and Transliteration
The normalization and transliteration sections each define a set of
ICU rules that are applied to the names.
The normalization rules are applied after sanitation. They should remove
any information that is not relevant for search at all. Usual rules to be
applied here are: lower-casing, removing of special characters, cleanup of
spaces.
The transliteration rules are applied at the end of the tokenization
process to transfer the name into an ASCII representation. Transliteration can
be useful to allow for further fuzzy matching, especially between different
scripts.
Each section must contain a list of
ICU transformation rules.
The rules are applied in the order in which they appear in the file.
You can also include additional rules from external yaml file using the!include tag. The included file must contain a valid YAML list of ICU rules
and may again include other files.
!!! warning
The ICU rule syntax contains special characters that conflict with the
YAML syntax. You should therefore always enclose the ICU rules in
double-quotes.
Tokenizer-specific Sanitizers
The tokenizer-sanitizers rules define Sanitizers which
specificly configure the subsequent token analysis. They are executed
after the global sanitizers and before any country-specific sanitizers.
Token Analysis
Token analyzers take a full name and transform it into one or more normalized
form that are then saved in the search index. In its simplest form, the
analyzer only applies the transliteration rules. More complex analyzers
create additional spelling variants of a name. This is useful to handle
decomposition and abbreviation.
The ICU tokenizer may use different analyzers for different names. To select
the analyzer to be used, the name must be tagged with the analyzer attribute
by a sanitizer (see for example the
tag-analyzer-by-language sanitizer).
The token-analysis section contains the list of configured analyzers. Each
analyzer must have an id parameter that uniquely identifies the analyzer.
The only exception is the default analyzer that is used when no special
analyzer was selected. There are analysers with special ids:
- '@housenumber'. If an analyzer with that name is present, it is used
for normalization of house numbers. - '@potcode'. If an analyzer with that name is present, it is used
for normalization of postcodes.
Different analyzer implementations may exist. To select the implementation,
the analyzer parameter must be set. The different implementations are
described in the following.
Generic token analyzer
The generic analyzer generic is able to create variants from a list of given
abbreviation and decomposition replacements and introduce spelling variations.
Variants
The optional 'variants' section defines lists of replacements which create alternative
spellings of a name. To create the variants, a name is scanned from left to
right and the longest matching replacement is applied until the end of the
string is reached.
The variants section must contain a list of replacement groups. Each group
defines a set of properties that describes where the replacements are
applicable. In addition, the word section defines the list of replacements
to be made. The basic replacement description is of the form:
<source>[,<source>[...]] => <target>[,<target>[...]]The left side contains one or more source terms to be replaced. The right side
lists one or more replacements. Each source is replaced with each replacement
term.
!!! tip
The source and target terms are internally normalized using the
normalization rules given in the configuration. This ensures that the
strings match as expected. In fact, it is better to use unnormalized
words in the configuration because then it is possible to change the
rules for normalization later without having to adapt the variant rules.
Decomposition
In its standard form, only full words match against the source. There
is a special notation to match the prefix and suffix of a word:
- ~strasse => str # matches "strasse" as full word and in suffix position
- hinter~ => hntr # matches "hinter" as full word and in prefix positionThere is no facility to match a string in the middle of the word. The suffix
and prefix notation automatically trigger the decomposition mode: two variants
are created for each replacement, one with the replacement attached to the word
and one separate. So in above example, the tokenization of "hauptstrasse" will
create the variants "hauptstr" and "haupt str". Similarly, the name "rote strasse"
triggers the variants "rote str" and "rotestr". By having decomposition work
both ways, it is sufficient to create the variants at index time. The variant
rules are not applied at query time.
To avoid automatic decomposition, use the '|' notation:
- ~strasse |=> strsimply changes "hauptstrasse" to "hauptstr" and "rote strasse" to "rote str".
Initial and final terms
It is also possible to restrict replacements to the beginning and end of a
name:
- ^south => s # matches only at the beginning of the name
- road$ => rd # matches only at the end of the nameSo the first example would trigger a replacement for "south 45th street" but
not for "the south beach restaurant".
Replacements vs. variants
The replacement syntax source => target works as a pure replacement. It changes
the name instead of creating a variant. To create an additional version, you'd
have to write source => source,target. As this is a frequent case, there is
a shortcut notation for it:
<source>[,<source>[...]] -> <target>[,<target>[...]]The simple arrow causes an additional variant to be added. Note that
decomposition has an effect here on the source as well. So a rule
- "~strasse -> str"means that for a word like hauptstrasse four variants are created:hauptstrasse, haupt strasse, hauptstr and haupt str.
Mutations
The 'mutation' section in the configuration describes an additional set of
replacements to be applied after the variants have been computed.
Each mutation is described by two parameters: pattern and replacements.
The pattern must contain a single regular expression to search for in the
variant name. The regular expressions need to follow the syntax for
Python regular expressions.
Capturing groups are not permitted.replacements must contain a list of strings that the pattern
should be replaced with. Each occurrence of the pattern is replaced with
all given replacements. Be mindful of combinatorial explosion of variants.
Modes
The generic analyser supports a special mode variant-only. When configured
then it consumes the input token and emits only variants (if any exist). Enable
the mode by adding:
mode: variant-onlyto the analyser configuration.
Housenumber token analyzer
The analyzer housenumbers is purpose-made to analyze house numbers. It
creates variants with optional spaces between numbers and letters. Thus,
house numbers of the form '3 a', '3A', '3-A' etc. are all considered equivalent.
The analyzer cannot be customized.
Postcode token analyzer
The analyzer postcodes is pupose-made to analyze postcodes. It supports
a 'lookup' variant of the token, which produces variants with optional
spaces. Use together with the clean-postcodes sanitizer.
The analyzer cannot be customized.
Reconfiguration
Changing the configuration after the import is currently not possible, although
this feature may be added at a later time.
Develop Database Layout (`docs/develop/Database-Layout.md`)
Database Layout
Import tables
OSM data is initially imported using osm2pgsql.
Nominatim uses a custom flex style to create the initial import tables.
The import process creates the following tables:
The planet_osm_* tables are the usual backing tables for OSM data. Note
that Nominatim uses them to look up special relations and to find nodes on
ways. Apart from those the osm2pgsql import produces five tables as output.
The place_postcode table collects postcode information that is not
already present on an object in the place table. That is for one thing
postcode area relations
and for another objects with a postcode tag but no other tagging that
qualifies it for inclusion into the geocoding database.
The table has the following fields:
osm_type- kind of OSM object (N - node, W - way, R - relation)osm_id- original OSM IDpostcode- postcode as extacted from thepostcal_codetagcountry_code- computed country code for this postcode. This field
functions as a cache and is only computed when the table is used for
the computation of the final postcodes.centroid- centroid of the objectgeometry- the full geometry of the area for postcode areas only
The place_interpolation table holds all
address interpolation lines
and has the following fields:
osm_id- original OSM IDtype- type of interpolation as extracted from theaddr:interpolationtagaddress- any otheraddr:*tagsnodes- list of OSM nodes contained in this interpolation,
needed to compute the involved housenumbers latergeometry- the linestring for the interpolation (in WSG84)
The place table holds all other OSM object that are interesting and
has the following fields:
osm_type- kind of OSM object (N - node, W - way, R - relation)osm_id- original OSM IDclass- key of principal tag defining the object typetype- value of principal tag defining the object typename- collection of tags that contain a name or referenceadmin_level- numerical value of the tagged administrative leveladdress- collection of tags defining the address of an objectextratags- collection of additional interesting tags that are not
directly relevant for searchinggeometry- geometry of the object (in WGS84)categories- all principal tags of the object, each one as a hierarchical
label of the formosm.<key>.<value>
An OSM object appears at most once in this table, even when it is tagged with
more than one tag that may constitute a principal tag. Take for example a
motorway bridge. In OSM, this would be a way which is tagged withhighway=motorway and bridge=yes. This way gets a single row in the place
table with categories of {osm.highway.motorway, osm.bridge.yes}. The
unique key for place is therefore (osm_type, osm_id).
The class and type columns still hold a single principal tag, the one that
Nominatim uses to classify and rank the place. When an object has more than one
principal tag, then the alphabetically first key/value pair wins. Tags that are
only used as a fallback (see Import styles)
contribute a category but never become class and type unless they are the
only principal tag of the object.
How raw OSM tags are mapped to the columns in the place table is to a certain
degree configurable. See Customizing Import Styles
for more information.
The place_entrance table collects the nodes that are tagged as an entrance
of a building or another feature. Nominatim does not make them searchable but
returns them together with the enclosing place. The table has the following
fields:
osm_id- original OSM ID of the nodetype- value of theentrancetagextratags- any other tags of the entrance that may be interestinggeometry- position of the node (in WGS84)
The place_associated_street table saves the members of
associatedStreet relations.
They are used to find the street a housenumber belongs to when no addr:street
tag can be matched. The table has the following fields:
relation_id- OSM ID of the relationmember_type,member_id- reference to the OSM object that is a member
of the relationmember_role- role of the member within the relation, usuallyhouse
orstreet
Search tables
The following tables carry all information needed to do the search:
The placex table is the central table that saves all information about the
searchable places in Nominatim.
In simpler terms, the placex table can be seen as the final, processed version of OSM data that is ready for search.
While the place table contains raw imported data, placex stores enriched and indexed data that includes ranking, hierarchy (parent-child relationships), and computed metadata such as importance and postcode.
Most search queries in Nominatim ultimately read from this table, making it the core table for forward and reverse geocoding.
The basic columns are the same as for the
place table and have the same meaning. The placex tables adds the following
additional columns:
place_id- the internal unique ID to identify the placepartition- the id to use with partitioned tables (see below)geometry_sector- a location hash used for geographically close orderingparent_place_id- the next higher place in the address hierarchy, only
relevant for POI-type places (with rank 30)linked_place_id- place ID of the place this object has been merged with.
When this ID is set, then the place is invisible for search.importance- measure how well known the place isrank_search,rank_address- search and address rank (see Customizing rankingwikipedia- the wikipedia page used for computing the importance of the placecountry_code- the country the place is located inhousenumber- normalized housenumber, if the place has onepostcode- computed postcode for the placeindexed_status- processing status of the place (0 - ready, 1 - freshly inserted, 2 - needs updating, 100 - needs deletion)indexed_date- timestamp when the place was processed lastcentroid- a point feature for the placetoken_info- a dummy field used to inject information from the tokenizer
into the indexing process
The categories column is copied from the place table. It is an array ofltree values, so that a search for a category can use the containment
operator <@ and match all descendants of a category with a single comparison.
The combined index idx_placex_centroid_categories over centroid andcategories backs the search for POIs of a given category around a point.
For implementation details, see the SQL definition in lib-sql/tables/placex.sql and the SQLAlchemy schema in src/nominatim_api/sql/sqlalchemy_schema.py.
The placex_entrance table holds the entrances that could be assigned to a
place, that is all entrance nodes that are part of the way of a place. The
columns have the same meaning as in place_entrance with the exception of:
place_id- reference to the place the entrance belongs tolocation- position of the entrance node
The location_property_osmline table is a special table for
address interpolations.
The columns have the same meaning and use as the columns with the same name in
the placex table. Only the following columns are special:
startnumber,endnumberandstep- beginning and end of the number range
for the interpolation and the increment stepstype- a string to indicate the interval between the numbers as imported
from the OSMaddr:interpolationtag; valid values areodd,even,all
or a single digit number; interpolations with other values are silently
dropped
Address interpolations are always ways in OSM, which is why there is no columnosm_type.
The location_postcodes table holds computed postcode assembled from the postcode information
available in OSM. When a postcode has a postcode area relation, or when the postcode geometry is
imported via JSONL files then the table stores
its full geometry. For all other postcodes the centroid is computed using the position of all OSM
objects that reference the same postcode. The osm_id and is_area fields can be used to
distinguish the two. When osm_id is set, it refers to the OSM relation with the postcode area,
and is_area is true for postcodes with a mature geometry (either from a postcode OSM area
relation or imported via JSONL), false for postcodes without a mature geometry (guessed
postcode geometries). The meaning of other columns in the table is again the same as that of the
placex table.
Every place needs an address, a set of surrounding places that describe the
location of the place. The set of address places is made up of OSM places
themselves. The place_addressline table cross-references for each place
all the places that make up its address. Two columns define the address
relation:
place_id- reference to the place being addressedaddress_place_id- reference to the place serving as an address part
The most of the columns cache information from the placex entry of the address
part. The exceptions are:
fromarea- is true if the address part has an area geometry and can
therefore be considered preceiseisaddress- is true if the address part should show up in the address
output. Sometimes there are multiple places competing for for same address
type (e.g. multiple cities) and this field resolves the tie.
The search_name table contains the search index proper. It saves for each
place the terms with which the place can be found. The terms are split into
the name itself and all terms that make up the address. The table mirrors some
of the columns from placex for faster lookup.
Search terms are not saved as strings. Each term is assigned an integer and those
integers are saved in the name and address vectors of the search_name table. The
word table serves as the lookup table from string to such a word ID. The
exact content of the word table depends on the tokenizer used.
Address computation tables
Next to the main search tables, there is a set of secondary helper tables used
to compute the address relations between places. These tables are partitioned.
Each country is assigned a partition number in the country_name table (see
below) and the data is then split between a set of tables, one for each
partition. Note that Nominatim still manually manages partitioned tables
instead of using PostgreSQL's native partitioning.
The search_name_X tables are used to look up streets that appear in theaddr:street tag.
The location_area_large_X tables are used to look up larger areas
(administrative boundaries and place nodes) either through their geographic
closeness or through addr:* entries.
The location_road_X tables are used to find the closest street for a
dependent place.
All three table cache specific information from the placex table for their
selected subset of places:
keywordsandname_vectorcontain lists of term ids (from the word table)
that the full name of the place should match againstisguessis true for places that are not described by an area
All other columns reflect their counterpart in the placex table.
The location_area_country table is not partitioned. It caches the
geometries of the country boundaries found in the data and is used to determine
the country a place is located in.
Static data tables
Nominatim also creates a number of static tables at import:
nominatim_propertiessaves settings that must not be changed after
importaddress_levelssave the rank information from the
ranking configurationcountry_namecontains a fallback of names for all countries, their
default languages and saves the assignment of countries to partitions.country_osm_gridprovides a fallback for country geometries
Auxiliary data tables
Finally there are some table for auxiliary data:
location_property_tiger- saves housenumber from the Tiger import. Its
layout is similar to that oflocation_propoerty_osmline.import_polygon_error- logs objects whose geometry was too broken to be
processed during an updateimport_polygon_delete- logs deletions of very large areas, which
Nominatim refuses to apply automatically, see
Maintenance
Develop Development Environment (`docs/develop/Development-Environment.md`)
Setting up Nominatim for Development
This chapter gives an overview how to set up Nominatim for development
and how to run tests.
!!! Important
This guide assumes you develop under the latest version of Debian/Ubuntu.
You can of course also use your favourite distribution. You just might have
to adapt the commands below slightly, in particular the commands for
installing additional software.
Installing Nominatim
The first step is to install Nominatim itself. Please follow the installation
instructions in the Admin section. You don't need
to set up a webserver for development, the webserver that can be started
via nominatim serve is sufficient.
If you want to run Nominatim in a VM via Vagrant, use the default ubuntu24 setup.
Vagrant's libvirt provider runs out-of-the-box under Ubuntu. You also need to
install an NFS daemon to enable directory sharing between host and guest. The
following packages should get you started:
sudo apt install vagrant vagrant-libvirt libvirt-daemon nfs-kernel-serverPrerequisites for testing and documentation
The Nominatim test suite consists of behavioural tests (using pytest-bdd) and
unit tests (using pytest). It has the following additional requirements:
- flake8 (CI always runs the latest version from pip)
- mypy (plus typing information for external libs)
- Python Typing Extensions (for Python < 3.9)
- pytest
- pytest-asyncio
- pytest-bdd
For testing the Python search frontend, you need to install extra dependencies
depending on your choice of webserver framework:
- httpx (Starlette only)
- asgi-lifespan (Starlette only)
The documentation is built with mkdocs:
- mkdocs >= 1.1.2
- mkdocstrings >= 0.25
- mkdocs-material
- mkdocs-gen-files
Installing prerequisites on Ubuntu/Debian
The Python tools should always be run with the most recent version.
The easiest way, to handle these Python dependencies is to run your
development from within a virtual environment.
sudo apt install build-essential libsqlite3-mod-spatialite osm2pgsql \
postgresql-postgis postgresql-postgis-scripts \
pkg-config libicu-dev virtualenvUsing pip
Create a virtual environment and install all dependencies:
virtualenv ~/nominatim-dev-venv
. ~/nominatim-dev-venv/bin/activate
pip install --group runtime --group devTo install dependencies individually:
pip install \
psutil 'psycopg[binary]' PyICU 'SQLAlchemy[asyncio]<2.1' \
python-dotenv jinja2 pyYAML \
mkdocs 'mkdocstrings[python]' mkdocs-gen-files mkdocs-material \
pytest pytest-asyncio pytest-bdd flake8 \
types-jinja2 types-markupsafe types-psutil types-psycopg2 \
types-pygments types-pyyaml types-requests types-ujson \
types-urllib3 typing-extensions gunicorn falcon starlette \
uvicorn mypy osmium aiosqlite mwparserfromhellUsing uv
If you prefer, you can use uv for a faster
development setup. Install uv following the
official instructions,
then from the Nominatim source directory:
uv syncThis creates a virtual environment and installs all Python dependencies
automatically. To run commands in the environment:
uv run nominatim --version
uv run pytest test/python
uv run make lintRunning Nominatim during development
The source code for Nominatim can be found in the src directory and can
be run in-place. The source directory features a special scriptnominatim-cli.py which does the same as the installed 'nominatim' binary
but executes against the code in the source tree. For example:
me@machine:~$ cd Nominatim
me@machine:~Nominatim$ ./nominatim-cli.py --version
Nominatim version 5.1.0-0Make sure you have activated the virtual environment holding all
necessary dependencies.
Executing Tests
All tests are located in the /test directory.
To run all tests, run make from the source root:
make testsThere are also make targets for executing only parts of the test suite.
For example to run linting only use:
make lintThe possible testing targets are: mypy, lint, pytest, bdd.
For more information about the structure of the tests and how to change and
extend the test suite, see the Testing chapter.
Documentation Pages
The Nominatim documentation is
built using the MkDocs static site generation
framework. The master branch is automatically deployed every night on
https://nominatim.org/release-docs/develop/
To build the documentation run
make docFor local testing, you can start webserver:
build> make serve-doc
[server:296] Serving on http://127.0.0.1:8000
[handlers:62] Start watching changesIf you develop inside a Vagrant virtual machine, use a port that is forwarded
to your host:
build> mkdocs serve --dev-addr 0.0.0.0:8088
[server:296] Serving on http://0.0.0.0:8088
[handlers:62] Start watching changesDevelop ICU Tokenizer Modules (`docs/develop/ICU-Tokenizer-Modules.md`)
Writing custom token analysis modules for the ICU tokenizer
The ICU tokenizer provides a
highly customizable method to pre-process and normalize the name information
of the input data before it is added to the search index. It comes with a
selection of token analyzers which you can use to adapt your
installation to your needs. If the provided modules are not enough, you can
also provide your own implementations. This section describes the API
for token analysis.
!!! warning
This API is currently in early alpha status. While this API is meant to
be a public API on which other token analyzers may be
implemented, it is not guaranteed to be stable at the moment.
Using custom token analysis modules
Token analysis names as set in the analyzer property
may refer to externally supplied modules. There are two ways
to include external modules: through a library or from the project directory.
To include a module from a library, use the absolute import path as name and
make sure the library can be found in your PYTHONPATH.
To use a custom module without creating a library, you can put the module
somewhere in your project directory and then use the relative path to the
file. Include the whole name of the file including the .py ending.
Custom token analysis module
::: nominatim_db.tokenizer.token_analysis.base.AnalysisModule
options:
heading_level: 6
::: nominatim_db.tokenizer.token_analysis.base.Analyzer
options:
heading_level: 6
Example: Creating acronym variants for long names
The following example of a token analysis module creates acronyms from
very long names and adds them as a variant:
class AcronymMaker:
""" This class is the actual analyzer.
"""
def __init__(self, norm, trans):
self.norm = norm
self.trans = trans
def get_canonical_id(self, name):
# In simple cases, the normalized name can be used as a canonical id.
return self.norm.transliterate(name.name).strip()
def compute_variants(self, name):
# The transliterated form of the name always makes up a variant.
variants = [self.trans.transliterate(name)]
# Only create acronyms from very long words.
if len(name) > 20:
# Take the first letter from each word to form the acronym.
acronym = ''.join(w[0] for w in name.split())
# If that leds to an acronym with at least three letters,
# add the resulting acronym as a variant.
if len(acronym) > 2:
# Never forget to transliterate the variants before returning them.
variants.append(self.trans.transliterate(acronym))
return variants
# The following two functions are the module interface.
def configure(rules, normalizer, transliterator):
# There is no configuration to parse and no data to set up.
# Just return an empty configuration.
return None
def create(normalizer, transliterator, config):
# Return a new instance of our token analysis class above.
return AcronymMaker(normalizer, transliterator)Given the name Trans-Siberian Railway, the code above would return the full
name Trans-Siberian Railway and the acronym TSR as variant, so that
searching would work for both.
Sanitizers vs. Token analysis - what to use for variants?
It is not always clear when to implement variations in the sanitizer and
when to write a token analysis module. Just take the acronym example
above: it would also have been possible to write a sanitizer which adds the
acronym as an additional name to the name list. The result would have been
similar. So which should be used when?
The most important thing to keep in mind is that variants created by the
token analysis are only saved in the word lookup table. They do not need
extra space in the search index. If there are many spelling variations, this
can mean quite a significant amount of space is saved.
When creating additional names with a sanitizer, these names are completely
independent. In particular, they can be fed into different token analysis
modules. This gives a much greater flexibility but at the price that the
additional names increase the size of the search index.
Develop Indexing (`docs/develop/Indexing.md`)
Indexing Places
In Nominatim, the word indexing refers to the process that takes the raw
OpenStreetMap data from the place table, enriches it with address information
and creates the search indexes. This section explains the basic data flow.
Initial import
After osm2pgsql has loaded the raw OSM data into the place table,
the data is copied to the final search tables placex and location_property_osmline.
While they are copied, some basic properties are added:
- country_code, geometry_sector and partition
- initial search and address rank
In addition the column indexed_status is set to 1 marking the place as one
that needs to be indexed.
All this happens in the triggers placex_insert and osmline_insert.
Indexing
The main work horse of the data import is the indexing step, where Nominatim
takes every place from the placex and location_property_osmline tables where
the indexed_status != 0 and computes the search terms and the address parts
of the place.
The indexing happens in three major steps:
Data preparation - The indexer gets the data for the place to be indexed
from the database.Search name processing - The prepared data is given to the
tokenizer which computes the search terms from the names
and potentially other information.Address processing - The indexer then hands the prepared data and the
tokenizer information back to the database via anINSERTstatement which
also sets the indexed_status to0. This triggers the update triggersplacex_update/osmline_updatewhich do the work of computing address
parts and filling all the search tables.
When computing the address terms of a place, Nominatim relies on the processed
search names of all the address parts. That is why places are processed in rank
order, from smallest rank to largest. To ensure correct handling of linked
place nodes, administrative boundaries are processed before all other places.
Apart from these restrictions, each place can be indexed independently
from the others. This allows a large degree of parallelization during the indexing.
It also means that the indexing process can be interrupted at any time and
will simply pick up where it left of when restarted.
Data preparation
The data preparation step computes and retrieves all data for a place that
might be needed for the next step of processing the search name. That includes
- location information (country code)
- place classification (class, type, ranks)
- names (including names of linked places)
- address information (
addr:*tags)
Data preparation is implemented in pl/PgSQL mostly in the functionsplacex_indexing_prepare() and get_interpolation_address().
`addr:*` tag inheritance
Nominatim has limited support for inheriting address tags from a building
to POIs inside the building. This only works when the address tags are on the
building outline. Any rank 30 object inside such a building or on its outline
inherits all address tags when it does not have any address tags of its own.
The inheritance is computed in the data preparation step.
Search name processing
The prepared place information is handed to the tokenizer next. This is a
Python module responsible for processing the names from both name and address
terms and building up the word index from them. The process is explained in
more detail in the Tokenizer chapter.
Address processing
Finally, the preprocessed place information and the results of the search name
processing are written back to the database. At this point the update trigger
of the placex/location_property_osmline tables take over and fill all the
dependent tables. This makes up the most work-intensive part of the indexing.
Nominatim distinguishes between dependent and independent places.
Dependent places are all places on rank 30: house numbers, POIs etc. These
places don't have a full address of their own. Instead they are attached to
a parent street or place and use the information of the parent for searching
and displaying information. Everything else are independent places: streets,
parks, water bodies, suburbs, cities, states etc. They receive a full address
on their own.
The address processing for both types of places is very different.
Independent places
To compute the address of an independent place Nominatim searches for all
places that cover the place to compute the address for at least partially.
For places with an area, that area is used to check for coverage. For place
nodes an artificial square area is computed according to the rank of
the place. The lower the rank the lager the area. The location_area_large_X
tables are there to facilitate the lookup. All places that can function as
the address of another place are saved in those tables.
addr:* and isin:* tags are taken into account to compute the address, too.
Nominatim will give preference to places with the same name as in these tags
when looking for places in the vicinity. If there are no matching place names
at all, then the tags are at least added to the search index. That means that
the names will not be shown in the result as the 'address' of the place, but
searching by them still works.
Independent places are always added to the global search index search_name.
Dependent places
Dependent places skip the full address computation for performance reasons.
Instead they just find a parent place to attach themselves to.
By default a POI
or house number will be attached to the closest street. That can be any major
or minor street indexed by Nominatim. In the default configuration that means
that it can attach itself to a footway but only when it has a name.
When the dependent place has an addr:street tag, then Nominatim will first
try to find a street with the same name before falling back to the closest
street.
There are also addresses in OSM, where the housenumber does not belong
to a street at all. These have an addr:place tag. For these places, Nominatim
tries to find a place with the given name in the indexed places with an
address rank between 16 and 25. If none is found, then the dependent place
is attached to the closest place in that category and the addr:place name is
added as unlisted place, which indicates to Nominatim that it needs to add
it to the address output, no matter what. This special case is necessary to
cover addresses that don't really refer to an existing object.
When an address has both the addr:street and addr:place tag, then Nominatim
assumes that the addr:place tag in fact should be the city part of the address
and give the POI the usual street number address.
Dependent places are only added to the global search index search_name when
they have either a name themselves or when they have address tags that are not
covered by the places that make up their address. The latter ensures that
addresses are always searchable by those address tags.
Develop Query Processing Modules (`docs/develop/Query-Processing-Modules.md`)
Writing custom query processing modules
Query processing allows to transform incoming queries before handing them
to the search engine. Read more about them in the
Query Preprocessing section.
Using custom preprocessing modules
To use a custom made preprocessing step, simply refer to the preprocessing module
in the step property. There are two ways
to include external modules: through a library or from the project directory.
To include a module from a library, use the absolute import path as name and
make sure the library can be found in your PYTHONPATH.
!!! Example
You have your preprocesser steps in a Python package my_modules and
want to refer to the step implemented in module delete_ip_addresses.py.
```
- step: my_modules.delete_ip_addresses
config: some other config info for the step
```To use a custom module without creating a library, you can put the module
somewhere in your project directory and then use the relative path to the
file. Include the whole name of the file including the .py ending.
!!! Example
You have put your module delete_ip_addresses.py directly into the project
directory.
```
- step: delete_ip_addresses.py
config: some other config info for the step
```Basic sanitizer module setup
A query preprocessor must export a single factory function create with
the following signature:
create(self, config: QueryConfig) -> Callable[[list[Phrase]], list[Phrase]]The function receives the custom configuration for the preprocessor and
returns a callable (function or class) with the actual preprocessing
code. When a query comes in, then the callable gets a list of phrases
and needs to return the transformed list of phrases. The list and phrases
may be changed in place or a completely new list may be generated.
The QueryConfig is a simple dictionary which contains all configuration
options given in the yaml configuration of the ICU tokenizer. It is up to
the function to interpret the values.
A nominatim_api.search.Phrase describes a part of the query that contains one or more independent
search terms. Breaking a query into phrases helps reducing the number of
possible tokens Nominatim has to take into account. However a phrase break
is definitive: a multi-term search word cannot go over a phrase break.
A Phrase object has two fields:
ptypefurther refines the type of phrase (see list below)textcontains the query text for the phrase
The order of phrases matters to Nominatim when doing further processing.
Thus, while you may split or join phrases, you should not reorder them
unless you really know what you are doing.
Phrase types can further help narrowing down how the tokens in the phrase
are interpreted. The following phrase types are known:
| Name | Description |
|---|---|
| PHRASE_ANY | No specific designation (i.e. source is free-form query) |
| PHRASE_AMENITY | Contains name or type of a POI |
| PHRASE_STREET | Contains a street name optionally with a housenumber |
| PHRASE_CITY | Contains the postal city |
| PHRASE_COUNTY | Contains the equivalent of a county |
| PHRASE_STATE | Contains a state or province |
| PHRASE_POSTCODE | Contains a postal code |
| PHRASE_COUNTRY | Contains the country name or code |
Develop Sanitizer Modules (`docs/develop/Sanitizer-Modules.md`)
Writing custom sanitizer modules
Sanitizers are used for preprocessing name and address information
from the OpenStreetMap input data for the search index. Read more about
them in the Customizing sanitizers section.
This section explains how to write your own sanitizer step function.
Using custom sanitizer modules
To use a custom made sanitizer step, simply refer to the sanitizer module
in the step property. There are two ways
to include external modules: through a library or from the project directory.
To include a module from a library, use the absolute import path as name and
make sure the library can be found in your PYTHONPATH.
!!! Example
You have your sanitizer steps in a Python package my_sanitizer and
want to refer to the step implemented in module translate_street.py.
```
- step: my_sanitizer.translate_street
config: some other config info for the step
```To use a custom module without creating a library, you can put the module
somewhere in your project directory and then use the relative path to the
file. Include the whole name of the file including the .py ending.
!!! Example
You have put your module translate_street.py directly into the project
directory.
```
- step: translate_street.py
config: some other config info for the step
```Basic sanitizer module setup
A sanitizer module must export a single factory function create with the
following signature:
def create(config: SanitizerConfig) -> Callable[[ProcessInfo], None]The function receives the custom configuration for the sanitizer and must
return a callable (function or class) that transforms the name and address
terms of a place. When a place is processed, then a ProcessInfo object
is created from the information that was queried from the database. This
object is sequentially handed to each configured sanitizer, so that each
sanitizer receives the result of processing from the previous sanitizer.
After the last sanitizer is finished, the resulting name and address lists
are forwarded to the token analysis module.
Sanitizer functions are instantiated once and then called for each place
that is imported or updated. They don't need to be thread-safe.
If multi-threading is used, each thread creates their own instance of
the function.
Sanitizer configuration
::: nominatim_db.tokenizer.sanitizers.config.SanitizerConfig
options:
heading_level: 6
The main filter function of the sanitizer
The filter function receives a single object of type ProcessInfo
which has with three members:
place: PlaceInfo: read-only information about the place being processed.
See PlaceInfo below.names: List[PlaceName]: The current list of names for the place.address: List[PlaceName]: The current list of address names for the place.
While the place member is provided for information only, the names andaddress lists are meant to be manipulated by the sanitizer. It may add and
remove entries, change information within a single entry (for example by
adding extra attributes) or completely replace the list with a different one.
PlaceInfo - information about the place
::: nominatim_db.data.place_info.PlaceInfo
options:
heading_level: 6
PlaceName - extended naming information
::: nominatim_db.data.place_name.PlaceName
options:
heading_level: 6
Example: Filter for US street prefixes
The following sanitizer removes the directional prefixes from street names
in the US:
!!! example
``` python
import re
def _filter_function(obj):
if obj.place.country_code == 'us' \
and obj.place.rank_address >= 26 and obj.place.rank_address <= 27:
for name in obj.names:
name.name = re.sub(r'^(north|south|west|east) ',
'',
name.name,
flags=re.IGNORECASE)
def create(config):
return _filter_function
```This is the most simple form of a sanitizer module. If defines a single
filter function and implements the required create() function by returning
the filter.
The filter function first checks if the object is interesting for the
sanitizer. Namely it checks if the place is in the US (through country_code)
and it the place is a street (a rank_address of 26 or 27). If the
conditions are met, then it goes through all available names and
removes any leading directional prefix using a simple regular expression.
Save the source code in a file in your project directory, for example asus_streets.py. Then you can use the sanitizer in your icu_tokenizer.yaml:
...
sanitizers:
- step: us_streets.py
...!!! warning
This example is just a simplified show case on how to create a sanitizer.
It is not really meant for real-world use: while the sanitizer would
correctly transform West 5th Street into 5th Street. it would also
shorten a simple North Street to Street.
For more sanitizer examples, have a look at the sanitizers provided by Nominatim.
They can be found in the directorysrc/nominatim_db/tokenizer/sanitizers.
Develop Testing (`docs/develop/Testing.md`)
Nominatim Test Suite
This chapter describes the tests in the /test directory, how they are
structured and how to extend them. For a quick introduction on how to run
the tests, see the Development setup chapter.
Overall structure
There are two kind of tests in this test suite. There are functional tests
which test the API interface using a BDD test framework and there are unit
tests for the Python code.
This test directory is structured as follows:
-+- bdd Functional API tests
| \
| +- steps Step implementations for test descriptions
| +- osm2pgsql Tests for data import via osm2pgsql
| +- db Tests for internal data processing on import and update
| +- api Tests for API endpoints (search, reverse, etc.)
|
+- python Python unit tests
+- testdb Base data for generating API test database
+- testdata Additional test data used by unit testsPython Unit Tests (`test/python`)
Unit tests for Python code can be found in the python/ directory. The goal is
to have complete coverage of the Python library in nominatim.
To execute the tests run
py.test-3 test/pythonor
pytest test/pythonThe name of the pytest binary depends on your installation.
BDD Functional Tests (`test/bdd`)
Functional tests are written as BDD instructions. For more information on
the philosophy of BDD testing, read the Wikipedia article on
Behaviour-driven development.
General Usage
To run the functional tests, do
pytest test/bddYou can run a single feature file using expression matching:
pytest test/bdd -k osm2pgsql/import/entrances.featureThis even works for running single tests by adding the line number of the
scenario header like that:
pytest test/bdd -k 'osm2pgsql/import/entrances.feature and L4'The BDD tests create databases for the tests. You can set name of the databases
through configuration variables in your pytest.ini:
nominatim_test_dbdefines the name of the temporary database created for
a single test (default:test_nominatim)nominatim_api_test_dbdefines the name of the database containing
the API test data, see also below (default:test_api_nominatim)nominatim_template_dbdefines the name of the template database used
for creating the temporary test databases. It contains some static setup
which usually doesn't change between imports of OSM data
(default:test_template_nominatim)
To change other connection parameters for the PostgreSQL database, use
the libpq environment variables.
Never set a password through these variables. Use a
password file instead.
The API test database and the template database are only created once and then
left untouched. This is usually what you want because it speeds up subsequent
runs of BDD tests. If you do change code that has an influence on the content
of these databases, you can run pytest with the --nominatim-purge parameter
and the databases will be dropped and recreated from scratch.
When running the BDD tests with make (using make tests or make bdd), then
the databases will always be purged.
The temporary test database is usually dropped directly after the test, so
it does not take up unnecessary space. If you want to keep the database around,
for example while debugging a specific BDD test, use the parameter--nominatim-keep-db.
API Tests (`test/bdd/api`)
These tests are meant to test the different API endpoints and their parameters.
They require to import several datasets into a test database. This is normally
done automatically during setup of the test. The API test database is then
kept around and reused in subsequent runs of behave. Use --nominatim-purge
to force a reimport of the database.
The official test dataset is saved in the file test/testdb/apidb-test-data.pbf
and compromises the following data:
- Geofabrik extract of Liechtenstein
- extract of Autauga country, Alabama, US (for tests against Tiger data)
- additional data from
test/testdb/additional_api_test.data.osm
API tests should only be testing the functionality of the website frontend code.
Most tests should be formulated as BDD DB creation tests (see below) instead.
DB Creation Tests (`test/bdd/db`)
These tests check the import and update of the Nominatim database. They do not
test the correctness of osm2pgsql. Each test will write some data into the place
table (and optionally the planet_osm_* tables if required) and then run
Nominatim's processing functions on that.
These tests use the template database and create temporary test databases for
each test.
Import Tests (`test/bdd/osm2pgsql`)
These tests check that data is imported correctly into the place table.
These tests also use the template database and create temporary test databases
for each test.
Develop Tokenizers (`docs/develop/Tokenizers.md`)
Tokenizers
The tokenizer is the component of Nominatim that is responsible for
analysing names of OSM objects and queries. Nominatim provides different
tokenizers that use different strategies for normalisation. This page describes
how tokenizers are expected to work and the public API that needs to be
implemented when creating a new tokenizer. For information on how to configure
a specific tokenizer for a database see the
tokenizer chapter in the Customization Guide.
Generic Architecture
About Search Tokens
Search in Nominatim is organised around search tokens. Such a token represents
string that can be part of the search query. Tokens are used so that the search
index does not need to be organised around strings. Instead the database saves
for each place which tokens match this place's name, address, house number etc.
To be able to distinguish between these different types of information stored
with the place, a search token also always has a certain type: name, house number,
postcode etc.
During search an incoming query is transformed into a ordered list of such
search tokens (or rather many lists, see below) and this list is then converted
into a database query to find the right place.
It is the core task of the tokenizer to create, manage and assign the search
tokens. The tokenizer is involved in two distinct operations:
- at import time: scanning names of OSM objects, normalizing them and
building up the list of search tokens. - at query time: scanning the query and returning the appropriate search
tokens.
Importing
The indexer is responsible to enrich an OSM object (or place) with all data
required for geocoding. It is split into two parts: the controller collects
the places that require updating, enriches the place information as required
and hands the place to Postgresql. The collector is part of the Nominatim
library written in Python. Within Postgresql, the placex_update
trigger is responsible to fill out all secondary tables with extra geocoding
information. This part is written in PL/pgSQL.
The tokenizer is involved in both parts. When the indexer prepares a place,
it hands it over to the tokenizer to inspect the names and create all the
search tokens applicable for the place. This usually involves updating the
tokenizer's internal token lists and creating a list of all token IDs for
the specific place. This list is later needed in the PL/pgSQL part where the
indexer needs to add the token IDs to the appropriate search tables. To be
able to communicate the list between the Python part and the pl/pgSQL trigger,
the placex table contains a special JSONB column token_info which is there
for the exclusive use of the tokenizer.
The Python part of the tokenizer returns a structured information about the
tokens of a place to the indexer which converts it to JSON and inserts it into
the token_info column. The content of the column is then handed to the PL/pqSQL
callbacks of the tokenizer which extracts the required information. Usually
the tokenizer then removes all information from the token_info structure,
so that no information is ever persistently saved in the table. All information
that went in should have been processed after all and put into secondary tables.
This is however not a hard requirement. If the tokenizer needs to store
additional information about a place permanently, it may do so in thetoken_info column. It just may never execute searches over it and
consequently not create any special indexes on it.
Querying
At query time, Nominatim builds up multiple interpretations of the search
query. Each of these interpretations is tried against the database in order
of the likelihood with which they match to the search query. The first
interpretation that yields results wins.
The interpretations are encapsulated in the SearchDescription class. An
instance of this class is created by applying a sequence of
search tokens to an initially empty SearchDescription. It is the
responsibility of the tokenizer to parse the search query and derive all
possible sequences of search tokens. To that end the tokenizer needs to parse
the search query and look up matching words in its own data structures.
Tokenizer API
The following section describes the functions that need to be implemented
for a custom tokenizer implementation.
!!! warning
This API is currently in early alpha status. While this API is meant to
be a public API on which other tokenizers may be implemented, the API is
far away from being stable at the moment.
Directory Structure
Nominatim expects two files containing the Python part of the implementation:
src/nominatim_db/tokenizer/<NAME>_tokenizer.pycontains the tokenizer
code used during import andsrc/nominatim_api/search/<NAME>_tokenizer.pyhas the code used during
query time.
<NAME> is a unique name for the tokenizer consisting of only lower-case
letters, digits and underscore. A tokenizer also needs to install some SQL
functions. By convention, these should be placed in lib-sql/tokenizer.
If the tokenizer has a default configuration file, this should be saved insettings/<NAME>_tokenizer.<SUFFIX>.
Configuration and Persistence
Tokenizers may define custom settings for their configuration. All settings
must be prefixed with NOMINATIM_TOKENIZER_. Settings may be transient or
persistent. Transient settings are loaded from the configuration file when
Nominatim is started and may thus be changed at any time. Persistent settings
are tied to a database installation and must only be read during installation
time. If they are needed for the runtime then they must be saved into thenominatim_properties table and later loaded from there.
The Python modules
`src/nominatim_db/tokenizer/`
The import Python module is expected to export a single factory function:
def create(dsn: str, data_dir: Path) -> AbstractTokenizerThe dsn parameter contains the DSN of the Nominatim database. The data_dir
is a directory in the project directory that the tokenizer may use to save
database-specific data. The function must return the instance of the tokenizer
class as defined below.
`src/nominatim_api/search/`
The query-time Python module must also export a factory function:
def create_query_analyzer(conn: SearchConnection) -> AbstractQueryAnalyzerThe conn parameter contains the current search connection. See the
library documentation
for details on the class. The function must return the instance of the tokenizer
class as defined below.
Python Tokenizer Class
All tokenizers must inherit from nominatim_db.tokenizer.base.AbstractTokenizer
and implement the abstract functions defined there.
::: nominatim_db.tokenizer.base.AbstractTokenizer
options:
heading_level: 6
Python Analyzer Class
::: nominatim_db.tokenizer.base.AbstractAnalyzer
options:
heading_level: 6
Python Query Analyzer Class
::: nominatim_api.search.query_analyzer_factory.AbstractQueryAnalyzer
options:
heading_level: 6
PL/pgSQL Functions
The tokenizer must provide access functions for the token_info column
to the indexer which extracts the necessary information for the global
search tables. If the tokenizer needs additional SQL functions for private
use, then these functions must be prefixed with token_ in order to ensure
that there are no naming conflicts with the SQL indexer code.
The following functions are expected:
FUNCTION token_get_name_search_tokens(info JSONB) RETURNS INTEGER[]Return an array of token IDs of search terms that should match
the name(s) for the given place. These tokens are used to look up the place
by name and, where the place functions as part of an address for another place,
by address. Must return NULL when the place has no name.
FUNCTION token_get_name_match_tokens(info JSONB) RETURNS INTEGER[]Return an array of token IDs of full names of the place that should be used
to match addresses. The list of match tokens is usually more strict than
search tokens as it is used to find a match between two OSM tag values which
are expected to contain matching full names. Partial terms should not be
used for match tokens. Must return NULL when the place has no name.
FUNCTION token_get_housenumber_search_tokens(info JSONB) RETURNS INTEGER[]Return an array of token IDs of house number tokens that apply to the place.
Note that a place may have multiple house numbers, for example when apartments
each have their own number. Must be NULL when the place has no house numbers.
FUNCTION token_normalized_housenumber(info JSONB) RETURNS TEXTReturn the house number(s) in the normalized form that can be matched against
a house number token text. If a place has multiple house numbers they must
be listed with a semicolon as delimiter. Must be NULL when the place has no
house numbers.
FUNCTION token_is_street_address(info JSONB) RETURNS BOOLEANReturn true if this is an object that should be parented against a street.
Only relevant for objects with address rank 30.
FUNCTION token_has_addr_street(info JSONB) RETURNS BOOLEANReturn true if there are street names to match against for finding the
parent of the object.
FUNCTION token_has_addr_place(info JSONB) RETURNS BOOLEANReturn true if there are place names to match against for finding the
parent of the object.
FUNCTION token_matches_street(info JSONB, street_tokens INTEGER[]) RETURNS BOOLEANCheck if the given tokens (previously saved from token_get_name_match_tokens())
match against the addr:street tag name. Must return either NULL or FALSE
when the place has no addr:street tag.
FUNCTION token_matches_place(info JSONB, place_tokens INTEGER[]) RETURNS BOOLEANCheck if the given tokens (previously saved from token_get_name_match_tokens())
match against the addr:place tag name. Must return either NULL or FALSE
when the place has no addr:place tag.
FUNCTION token_addr_place_search_tokens(info JSONB) RETURNS INTEGER[]Return the search token IDs extracted from the addr:place tag. These tokens
are used for searches by address when no matching place can be found in the
database. Must be NULL when the place has no addr:place tag.
FUNCTION token_get_address_keys(info JSONB) RETURNS SETOF TEXTReturn the set of keys for which address information is provided. This
should correspond to the list of (relevant) addr:* tags with the addr:
prefix removed or the keys used in the address dictionary of the place info.
FUNCTION token_get_address_search_tokens(info JSONB, key TEXT) RETURNS INTEGER[]Return the array of search tokens for the given address part. key can be
expected to be one of those returned with token_get_address_keys(). The
search tokens are added to the address search vector of the place, when no
corresponding OSM object could be found for the given address part from which
to copy the name information.
FUNCTION token_matches_address(info JSONB, key TEXT, tokens INTEGER[])Check if the given tokens match against the address part key.
Warning: the tokens that are handed in are the lists previously saved
from token_get_name_search_tokens(), not from the match token list. This
is an historical oddity which will be fixed at some point in the future.
Currently, tokenizers are encouraged to make sure that matching works against
both the search token list and the match token list.
FUNCTION token_get_postcode(info JSONB) RETURNS TEXTReturn the postcode for the object, if any exists. The postcode must be in
the form that should also be presented to the end-user.
FUNCTION token_strip_info(info JSONB) RETURNS JSONBReturn the part of the token_info field that should be stored in the database
permanently. The indexer calls this function when all processing is done and
replaces the content of the token_info column with the returned value before
the trigger stores the information in the database. May return NULL if no
information should be stored permanently.
Develop Data Sources (`docs/develop/data-sources.md`)
Additional Data Sources
This guide explains how data sources other than OpenStreetMap mentioned in
the install instructions got obtained and converted.
Country grid
Nominatim uses pre-generated country borders data. In case one imports only
a subset of a country. And to assign each place a partition. Nominatim
database tables are split into partitions for performance.
More details in osm-search/country-grid-data.
US Census TIGER
For the United States you can choose to import additional street-level data.
The data isn't mixed into OSM data but queried as fallback when no OSM
result can be found.
More details in osm-search/TIGER-data.
GB postcodes
For Great Britain you can choose to import Royalmail postcode centroids.
More details in osm-search/gb-postcode-data.
Wikipedia & Wikidata rankings
Nominatim can import "importance" data of place names. This greatly
improves ranking of results.
More details in osm-search/wikipedia-wikidata.
Library Configuration (`docs/library/Configuration.md`)
Configuration
When using Nominatim through the library, it can be configured in exactly
the same way as when running as a service. You may instantiate the library
against the project directory
of your Nominatim installation. It contains all files belonging to the
Nominatim instance. This may include an .env file with configuration options.
Setting configuration parameters via environment variables works as well.
Alternatively to using the operating system's environment, a set of
configuration parameters may also be passed to the Nomiantim API object.
Configuration options are resolved in the following order:
- from the OS environment (or the dictionary given in
environ,
(see NominatimAPI.md#nominatim.api.core.NominatimAPI.init) - from the .env file in the project directory of the installation
- from the default installation in the configuration directory
For more information on configuration via dotenv and a list of possible
configuration parameters, see the Configuration page.
`Configuration` class
::: nominatim_api.Configuration
options:
members:
- get_bool
- get_int
- get_str_list
- get_path
heading_level: 6
show_signature_annotations: True
Library Input Parameter Types (`docs/library/Input-Parameter-Types.md`)
Input Parameter Types
This page describes in more detail some of the input parameter types used
in the query functions of the API object.
Place identification
The details and
lookup functions
require references to places in the database. Below the possible
types for place identification are listed. All types are dataclasses.
PlaceID
::: nominatim_api.PlaceID
options:
heading_level: 6
OsmID
::: nominatim_api.OsmID
options:
heading_level: 6
Geometry types
::: nominatim_api.GeometryFormat
options:
heading_level: 6
members_order: source
Geometry input
Point
::: nominatim_api.Point
options:
heading_level: 6
show_signature_annotations: True
Bbox
::: nominatim_api.Bbox
options:
heading_level: 6
show_signature_annotations: True
members_order: source
group_by_category: False
Layers
Layers allow to restrict the search result to thematic groups. This is
orthogonal to restriction by address ranks, which groups places by their
geographic extent.
::: nominatim_api.DataLayer
options:
heading_level: 6
members_order: source
Library Low Level DB Access (`docs/library/Low-Level-DB-Access.md`)
Low-level connections
The NominatimAPIAsync class allows to directly access the underlying
database connection to explore the raw data. Nominatim uses
SQLAlchemy for building queries. Please
refer to the documentation of the library to understand how to write SQL.
To get access to a search connection, use the begin() function of your
API object. This returns a SearchConnection object described below
wrapped in a context manager. Itst property has definitions for all Nominatim search tables. For an
overview of available tables, refer to the
Development Layout in in the development
chapter. Note that only tables that are needed for search are accessible
as SQLAlchemy tables.
!!! warning
The database layout is not part of the API definition and may change
without notice. If you play with the low-level access functions, you
need to be prepared for such changes.
Here is a simple example, which prints how many places are available in
the placex table:
import asyncio
import sqlalchemy as sa
from nominatim_api import NominatimAPIAsync
async def print_table_size():
api = NominatimAPIAsync()
async with api.begin() as conn:
cnt = await conn.scalar(sa.select(sa.func.count()).select_from(conn.t.placex))
print(f'placex table has {cnt} rows.')
asyncio.run(print_table_size())!!! warning
Low-level connections may only be used to read data from the database.
Do not use it to add or modify data or you might break Nominatim's
normal functions.
SearchConnection class
::: nominatim_api.SearchConnection
options:
members:
- scalar
- execute
- get_db_property
- get_property
heading_level: 6
Library NominatimAPI (`docs/library/NominatimAPI.md`)
The Nominatim API classes
The API classes are the core object of the search library. Always instantiate
one of these classes first. The API classes are not threadsafe. You need
to instantiate a separate instance for each thread.
NominatimAPI
::: nominatim_api.NominatimAPI
options:
members:
- init
- config
- close
- status
- details
- lookup
- reverse
- search
- search_address
- search_category
heading_level: 6
group_by_category: False
NominatimAPIAsync
::: nominatim_api.NominatimAPIAsync
options:
members:
- init
- setup_database
- close
- begin
heading_level: 6
group_by_category: False
Library Result Handling (`docs/library/Result-Handling.md`)
Result handling
The search functions of the Nominatim API always return a result object
with the raw information about the place that is available in the
database. This section discusses data types used in the results and utility
functions that allow further processing of the results.
Result fields
Sources
Nominatim takes the result data from multiple sources. The source_table field
in the result describes, from which source the result was retrieved.
::: nominatim_api.SourceTable
options:
heading_level: 6
members_order: source
Detailed address description
When the address_details parameter is set, then functions return not
only information about the result place but also about the place that
make up the address. This information is almost always required when you
want to present the user with a human-readable description of the result.
See also Localization below.
The address details are available in the address_rows field as a ordered
list of AddressLine objects with the country information last. The list also
contains the result place itself and some artificial entries, for example,
for the house number or the country code. This makes processing and creating
a full address easier.
::: nominatim_api.AddressLine
options:
heading_level: 6
members_order: source
Detailed search terms
The details function can return detailed information about which search terms
may be used to find a place, when the keywords parameter is set. Search
terms are split into terms for the name of the place and search terms for
its address.
::: nominatim_api.WordInfo
options:
heading_level: 6
Localization
Results are always returned with the full list of available names. However, the
default locale_name must be explicitly set using the localize function withinLocales. This parses through the full list of available names to find the one
most preferred by the user. Once this is set, the user can simply use thedisplay_name field within a Result object to retrive the localized name.
Locale
::: nominatim_api.Locales
options:
heading_level: 6
--- METRICS ---
- Files Extracted: 51
- Estimated Token Budget: ~81355 tokens
- Canonical Reference: https://codewiki.google/github.com/osm-search/Nominatim