Skip to content

komoot/photon

Repository files navigation

About photon

Continuous Integration

photon is an open source geocoder built for OpenStreetMap data. It is based on elasticsearch/OpenSearch - an efficient, powerful and highly scalable search platform.

photon was started by komoot who also provide the public demo server at photon.komoot.io.

Features

  • high performance
  • highly scalability
  • search-as-you-type
  • multilingual search
  • location bias
  • typo tolerance
  • filter by osm tag and value
  • filter by bounding box
  • reverse geocode a coordinate to an address
  • OSM data import (built upon Nominatim) inclusive continuous updates
  • import and export as JSON dumps

Demo server

You can try out photon via the demo server at https://photon.komoot.io. You are welcome to use the API for your project as long as the number of requests stay in a reasonable limit. Extensive usage will be throttled or completely banned. We do not give guarantees for availability and reserve the right to implement changes without notice.

If you have a larger number of requests to make, please consider setting up your own private instance. It is as simple as downloading two files and starting the server. See instructions below.

Installation and Usage

photon ElasticSearch vs. photon OpenSearch

photon was originally built on ElasticSearch. For technical reasons, we are stuck with a very old an unsupported version 5.6 of ElasticSearch. Over the last year, photon has been ported to the latest version of OpenSearch. This version can now be considered stable. When setting up a new instance of photon, please use photon-opensearch. This will become the default and only available version soon.

Requirements

photon requires Java, version 11+.

If you want to run against an external database instead of using the embedded server, ElasticSearch 5.6 or OpenSearch 2.x is needed.

A planet-wide database requires about 220GB disk space (as of 2025, grows by about 10% a year). Using SSDs for storage is strongly recommended, NVME would even be better. At least 64GB RAM are recommended for smooth operations, more, if the server takes significant load. Running photon with less RAM is possible but consider increasing the available heap (using java -Xmx8G -jar ... for example). Be careful to make sure that there remains enough free RAM that the system doesn't start swapping.

If you want to import data from Nominatim, there are additional requirements for Nominatim.

Using the release binaries and extracts

This is the easiest way to set up a self-hosted photon instance. Pre-built jar files can be downloaded from the Github release page. Preferably get the photon-opensearch jar file.

GraphHopper kindly provides weekly updated dumps of the photon database at https://download1.graphhopper.com/public. The dumps are available for the world-wide dataset and for selected country datasets. The dumps contain names in English, German, French and local language. There is no support for structured queries or full geometry output. If you need these features, you need to import your own database, see below.

For ElasticSearch photon use:

  • world-wide: https://download1.graphhopper.com/public/
  • country extracts: https://download1.graphhopper.com/public/extracts/

For OpenSearch photon use:

  • world-wide: https://download1.graphhopper.com/public/experimental/
  • country extracts: https://download1.graphhopper.com/public/experimental/extracts/

Make sure you have bzip2 or pbzip2 installed and execute one of these two commands in your shell. This will download, uncompress and extract the huge database in one step:

wget -O - https://download1.graphhopper.com/public/photon-db-latest.tar.bz2 | bzip2 -cd | tar x
# you can significantly speed up extracting using pbzip2 (recommended):
wget -O - https://download1.graphhopper.com/public/photon-db-latest.tar.bz2 | pbzip2 -cd | tar x

Don't forget to adapt the directory to match your photon version.

Building photon from scratch

photon uses gradle for building. To build the package from source make sure you have a JDK installed. Then run:

./gradlew build

This will build and test photon in the ElasticSearch and OpenSearch version. The final jar can be found in the target directory.

Usage

Start photon with the following command:

java -jar photon-*.jar

Use -data-dir to point to the parent directory of the photon_data directory with the database. By default photon will look for a photob_data directory in the current working directory.

Alternatively, you can make photon connect to an external ElasticSearch/OpenSearch instance. Use the parameter -transport-addresses to define a comma-separated list of addresses of external nodes.

Running photon

When photon is started without any parameters, it starts up a webserver with the photon API at http://localhost:2322. You can change the listening address with -listen-ip <ip address> and the port with -listen-port <port number>. A description of the API endpoints is at the end of this README.

To enable CORS (cross-site requests), use -cors-any to allow any origin or -cors-origin with a specific origin as the argument. By default, CORS support is disabled.

-languages <languages> restricts which languages are supported to be searched and returned by the API. This may be used to temporarily restrict the language to a different set than what was originally imported.

-default-language allows to chose in which language results are returned when the user hasn't explicitly (or implicitly via the Accept-Language header) chosen a language. The default is to return all results in the local language.

-enable-update-api adds a special endpoint that triggers updates from a Nominatim API, see "Updating data via Nominatim" below. If this option is enabled, you also need to add the connection parameters for the database.

-max-results and -max-reverse-results can be used to change the upper limit of the limit parameter, which defines the maximum number of results returned, for forward (/api) and reverse searches respectively. The limit parameter will be silenty trimmed to what is set here. The default is 50 results.

-query-timeout sets the number of seconds after which a query to the database will be canceled and an error returned to the user.

Photon has limited support for synonyms. Check out the synonym documentation for more information.

Importing data from Nominatim

If you want to have a database with a different set of data than provided by the dumps, you can create a photon database by importing the data from a Nominatim instance. To learn how to set up a Nominatim database, refer to the installation documentation.

If you haven't already set a password for your Nominatim database user, do it now (change user name and password as you like, below):

psql -d nominatim -c "ALTER USER nominatim WITH ENCRYPTED PASSWORD 'mysecretpassword'"

Import the data to photon:

java -jar photon-*.jar -nominatim-import -host localhost -port 5432 -database nominatim -user nominatim -password mysecretpassword -languages es,fr

The import of worldwide data set takes about half a day. SSD/NVME disks are recommended to accelerate Nominatim queries.

There are a couple of parameters that influence which kind of data will be imported:

-languages <languages> states which languages in addition to the local name are added to the database. Give the languages as a comma-separated list of language codes. Note that a very long list of languages will slow down queries. This is a known restriction we are working on. You can still import a large list but then might need to restrict the languages that are actively used when starting the API service.

-country-codes allows to filter the data to be imported by country. Set this to a comma-separated list of two-letter language codes.

-extra-tags defines a set of tags to add from Nominatim's extratags column. Either give a comma-separated list of OSM tags to include or set the special keyword ALL to unconditionally include all extra tags. The default is to include nothing.

When -import-geometry-column is set, photon will not only include centroid and bounding box in results but the full geometry as well. WARNING: enabling this option will more than double the size of the database. (Experimental Feature!)

-structured adds the indexes to the database that are necessary for structured queries, see structured queries. (Experimental Feature!)

Updating data via Nominatim

Once you have imported your own photon database from a Nominatim source, you can keep up-to-date with the source through regular updates.

First prepare the Nominatim database with the appropriate triggers:

java -jar photon-*.jar -database nominatim -user nominatim -password ... -nominatim-update-init-for update_user

This script must be run with a user that has the right to create tables, functions and triggers.

'update-user' is the PostgreSQL user that will be used when updating the Photon database. The user needs to already have read rights on the database. It also needs to be able to update the status table. The necessary update rights will be granted during initialisation.

Now you can run updates on Nominatim using the usual methods as described in the documentation. To bring the Photon database up-to-date, make sure the Nominatim updates are not running and then run the Photon update process:

java -jar photon-*.jar -nominatim-update -database nominatim -user nominatim -password ...

Alternatively, you can trigger updates via a special API endpoint. To be able to do so, you need to run photon with the update API enabled:

java -jar photon-*.jar -enable-update-api -database nominatim -user nominatim -password ...

The update is then triggered like this:

curl http://localhost:2322/nominatim-update

This will trigger a single updates run. If another update is already in progress, the request returns with an error. You can also check if the updates have finished by using the status API:

curl http://localhost:2322/nominatim-update/status

It returns a single JSON string: "BUSY" when updates are in progress, or "OK" when another update round can be started.

For your convenience, this repository contains a script to continuously update both Nominatim and Photon using Photon's update API. Make sure you have Photon started with -enable-update-api and then run:

export NOMINATIM_DIR=/srv/nominatim/...
./continuously_update_from_nominatim.sh

where NOMINATIM_DIR is the project directory of your Nominatim installation.

Exporting data to a JSON dump

Imports normally write the data directly into a ElasticSearch/OpenSearch database. When adding the parameter -json <filename> the data is written out as a JSON dump file instead. All parameter that influence a database import are valid for a JSON dump as well. To dump the data to standard output (for example, to directly pack the data), use -json -.

Importing data from a JSON dump

To read a JSON dump previously created with photon, use the following command:

java -jar photon-*.jar -nominatim-import -import-file <filename>

When the filename is -, then photon reads from standard input allowing you to unpack dump files on the fly.

The file import accepts all the parameters of an import from a Nominatim database. That means that you can create a dump file of a full Nominatim database and then later import it into different photon databases with different filtering optins like -country-code, -language or -extra-tags.

Photon API

photon has three default endpoints: /api for forward search, /reverse for reverse geocding and /status as a health check of the server.

For the optional /structured endpoint for structured queries, see docs/structured.md.

The /update endpoint for triggering updates is described in the section "Updating data via Nominatim" above.

Search

A simple forward search for a place looks like this:

http://localhost:2322/api?q=berlin

Location Bias

http://localhost:2322/api?q=berlin&lon=10&lat=52&zoom=12&location_bias_scale=0.1

There are two optional parameters to influence the location bias. 'zoom' describes the radius around the center to focus on. This is a number that should correspond roughly to the map zoom parameter of a corresponding map. The default is zoom=16.

The location_bias_scale describes how much the prominence of a result should still be taken into account. Sensible values go from 0.0 (ignore prominence almost completely) to 1.0 (prominence has approximately the same influence). The default is 0.2.

Filter results by bounding box

http://localhost:2322/api?q=berlin&bbox=9.5,51.5,11.5,53.5

The expected format for the bounding box is minLon,minLat,maxLon,maxLat.

Reverse

The basic lookup of a coordinate looks like this:

http://localhost:2322/reverse?lon=10&lat=52&radius=10

The optional radius parameter can be used to specify a value in kilometers to reverse geocode within. The value has to be between 0 and 5000 km.

Parameters common to Search and Reverse

The following parameters work for search, reverse search and structured search.

Adapt Number of Results

http://localhost:2322/api?q=berlin&limit=2

Adjust Language

http://localhost:2322/api?q=berlin&lang=it

If omitted the 'accept-language' HTTP header will be used (browsers set this by default). If neither is set the local name of the place is returned. In OpenStreetMap data that's usually the value of the name tag, for example the local name for Tokyo is 東京都.

Filter results by tags and values

Note: the filter only works on principal OSM tags and not all OSM tag/value combinations can be searched. The actual list depends on the import style used for the Nominatim database (e.g. settings/import-full.style). All tag/value combinations with a property 'main' are included in the photon database. If one or many query parameters named osm_tag are present, photon will attempt to filter results by those tags. In general, here is the expected format (syntax) for the value of osm_tag request parameters.

  1. Include places with tag: osm_tag=key:value
  2. Exclude places with tag: osm_tag=!key:value
  3. Include places with tag key: osm_tag=key
  4. Include places with tag value: osm_tag=:value
  5. Exclude places with tag key: osm_tag=!key
  6. Exclude places with tag value: osm_tag=:!value

For example, to search for all places named berlin with tag of tourism=museum, one should construct url as follows:

http://localhost:2322/api?q=berlin&osm_tag=tourism:museum

Or, just by they key

http://localhost:2322/api?q=berlin&osm_tag=tourism

You can also use this feature for reverse geocoding. Want to see the 5 pharmacies closest to a location ?

http://localhost:2322/reverse?lon=10&lat=52&osm_tag=amenity:pharmacy&limit=5

Filter results by layer

List of available layers:

  • house
  • street
  • locality
  • district
  • city
  • county
  • state
  • country
  • other (e.g. natural features)
http://localhost:2322/api?q=berlin&layer=city&layer=locality

Example above will return both cities and localities.

Results for Search and Reverse

Photon returns a response in GeocodeJson format with the following extra fields added:

  • extra is an object containing any extra tags, if available.

Example response:

json
{
  "features": [
    {
      "properties": {
        "name": "Berlin",
        "state": "Berlin",
        "country": "Germany",
        "countrycode": "DE",
        "osm_key": "place",
        "osm_value": "city",
        "osm_type": "N",
        "osm_id": 240109189
      },
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [13.3888599, 52.5170365]
      }
    },
    {
      "properties": {
        "name": "Berlin Olympic Stadium",
        "street": "Olympischer Platz",
        "housenumber": "3",
        "postcode": "14053",
        "state": "Berlin",
        "country": "Germany",
        "countrycode": "DE",
        "osm_key": "leisure",
        "osm_value": "stadium",
        "osm_type": "W",
        "osm_id": 38862723,
        "extent": [13.23727, 52.5157151, 13.241757, 52.5135972]
      },
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [13.239514674078611, 52.51467945]
      }
    }
  ]
}

Status

http://localhost:2322/status

returns a JSON document containing the status and the last update date of the data. (That is the date, when the data is from, not when it was imported into photon.)

Contributing

All code contributions and bug reports are welcome.

For questions please either use Github discussions or join the OpenStreetMap forum.

License

photon software is open source and licensed under Apache License, Version 2.0

Related Projects

  • photon's search configuration was developed with a specific test framework. It is written in Python and hosted separately.
  • There is a leaflet-plugin for displaying a search box with a photon server in the backend.