Geocodierung mit der Nominatim API

Nominatim ist ein Werkzeug, um OpenStreetMap-Daten nach Name und Adresse zu durchsuchen und Adressen oder bekannte Orte in OpenStreetMap-Koordinaten zu ermitteln oder ein Reverse-Geocodierung (Georeferenzierung)

Reverse Geocoding ist der Prozess der Umwandlung eines Standorts, wie er durch geografische Koordinaten (Breitengrad, Längengrad) beschrieben wird, in eine für Menschen lesbare Adresse oder einen Ortsnamen. Es ist das Gegenteil von Vorwärts-Geokodierung (oft als Adress-Geokodierung oder einfach „Geokodierung“ bezeichnet), daher der Begriff umgekehrt. Die umgekehrte Geokodierung ermöglicht die Identifizierung von nahegelegenen Straßenadressen, Orten und/oder Gebietsunterteilungen wie Stadtteilen, Landkreisen, Bundesstaaten oder Ländern.

https://en.wikipedia.org/wiki/Reverse_geocoding

Die Geocodierungsdaten können über die Nominatim API kostenlos abgerufen und verwendet werden. Die API unterliegt keinerlei Limits und es ist auch kein API-Key oder eine OAuth Authentifizierung nötig.
Die wichtigsten JSON Endpunkte der Nominatim API sind die folgenden:

  • /search 
    Suche nach OpenStreetMap-Objekten nach Namen oder Typ
  • /reverse 
    Suche nach OpenStreetMap-Objekten nach ihrem Standort
  • /lookup 
    Adressdetails für OpenStreetMap-Objekte anhand ihrer ID suchen
  • /status – Abfrage des Serverstatus

Es gibt noch ein paar zusätzliche Enpunkte, auf die ich hier aber nicht weiter eingehen werde. Die Liste aller Endpunkte kann unter https://nominatim.org/release-docs/develop/api/Overview/ nachgelesen werden.

Eine OpenStreetMap Nominatim API Demo mit aktuellen Daten kann man unter https://nominatim.openstreetmap.org aufrufen. In dem Suchfeld kann man einfach einen beliebigen Begriff eingeben und bekommt dann eine Liste von möglichen Treffern zurück die man dann auf der OSM-Karte darstellen kann.

Nominatim API Ergebnis für „Herne“

Auf dem Bild sieht man gerade das Ergebnis für die Suche nach „Herne“. Die JSON Daten zu dem Ergebnis können unter https://nominatim.openstreetmap.org/search.php?q=Herne&polygon_geojson=1&format=jsonv2 abgerufen werden.

Als Format ist hier jsonv2 angegeben. Daneben sind auch noch die folgenden Formate möglich.

format=[xml|json|jsonv2|geojson|geocodejson]

In dem vorliegenden JSON String ist ein Array von „places“ das wie folgt aufgebaut ist:

Alle „Places“ besitzen neben der Id, dem Display-Namen, den GPS Koordinaten und diverser anderer Eigenschaften auch ein Polygon mit dem die genauen Grenzen des Objektes gezeichnet werden können.

Um die Daten z.B. in einem Java Programm weiter zu verarbeiten benötigt man entsprechende Java Klassen. Ich habe es mir hier einfach gemacht und mit dem TODO eine Klasse generiert, mit der die Daten schnell und einfach als Java Object verwendet werden können.

-----------------------------------com.jentsch.Geojson.java-----------------------------------
package com.jentsch;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Generated;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
        "type",
        "coordinates"
})
@Generated("jsonschema2pojo")
public class Geojson {

    @JsonProperty("type")
    private String type;
    @JsonProperty("coordinates")
    private List<List<Double>> coordinates = null;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap<String, Object>();

    @JsonProperty("type")
    public String getType() {
        return type;
    }

    @JsonProperty("type")
    public void setType(String type) {
        this.type = type;
    }

    @JsonProperty("coordinates")
    public List<List<Double>> getCoordinates() {
        return coordinates;
    }

    @JsonProperty("coordinates")
    public void setCoordinates(List<List<Double>> coordinates) {
        this.coordinates = coordinates;
    }

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return this.additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }

}
-----------------------------------com.jentsch.Place.java-----------------------------------

package com.jentsch;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Generated;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
        "place_id",
        "licence",
        "osm_type",
        "osm_id",
        "boundingbox",
        "lat",
        "lon",
        "display_name",
        "place_rank",
        "category",
        "type",
        "importance",
        "geojson"
})
@Generated("jsonschema2pojo")
public class Place {

    @JsonProperty("place_id")
    private Integer placeId;
    @JsonProperty("licence")
    private String licence;
    @JsonProperty("osm_type")
    private String osmType;
    @JsonProperty("osm_id")
    private Integer osmId;
    @JsonProperty("boundingbox")
    private List<String> boundingbox = null;
    @JsonProperty("lat")
    private String lat;
    @JsonProperty("lon")
    private String lon;
    @JsonProperty("display_name")
    private String displayName;
    @JsonProperty("place_rank")
    private Integer placeRank;
    @JsonProperty("category")
    private String category;
    @JsonProperty("type")
    private String type;
    @JsonProperty("importance")
    private Double importance;
    @JsonProperty("geojson")
    private Geojson geojson;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap<String, Object>();

    @JsonProperty("place_id")
    public Integer getPlaceId() {
        return placeId;
    }

    @JsonProperty("place_id")
    public void setPlaceId(Integer placeId) {
        this.placeId = placeId;
    }

    @JsonProperty("licence")
    public String getLicence() {
        return licence;
    }

    @JsonProperty("licence")
    public void setLicence(String licence) {
        this.licence = licence;
    }

    @JsonProperty("osm_type")
    public String getOsmType() {
        return osmType;
    }

    @JsonProperty("osm_type")
    public void setOsmType(String osmType) {
        this.osmType = osmType;
    }

    @JsonProperty("osm_id")
    public Integer getOsmId() {
        return osmId;
    }

    @JsonProperty("osm_id")
    public void setOsmId(Integer osmId) {
        this.osmId = osmId;
    }

    @JsonProperty("boundingbox")
    public List<String> getBoundingbox() {
        return boundingbox;
    }

    @JsonProperty("boundingbox")
    public void setBoundingbox(List<String> boundingbox) {
        this.boundingbox = boundingbox;
    }

    @JsonProperty("lat")
    public String getLat() {
        return lat;
    }

    @JsonProperty("lat")
    public void setLat(String lat) {
        this.lat = lat;
    }

    @JsonProperty("lon")
    public String getLon() {
        return lon;
    }

    @JsonProperty("lon")
    public void setLon(String lon) {
        this.lon = lon;
    }

    @JsonProperty("display_name")
    public String getDisplayName() {
        return displayName;
    }

    @JsonProperty("display_name")
    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    @JsonProperty("place_rank")
    public Integer getPlaceRank() {
        return placeRank;
    }

    @JsonProperty("place_rank")
    public void setPlaceRank(Integer placeRank) {
        this.placeRank = placeRank;
    }

    @JsonProperty("category")
    public String getCategory() {
        return category;
    }

    @JsonProperty("category")
    public void setCategory(String category) {
        this.category = category;
    }

    @JsonProperty("type")
    public String getType() {
        return type;
    }

    @JsonProperty("type")
    public void setType(String type) {
        this.type = type;
    }

    @JsonProperty("importance")
    public Double getImportance() {
        return importance;
    }

    @JsonProperty("importance")
    public void setImportance(Double importance) {
        this.importance = importance;
    }

    @JsonProperty("geojson")
    public Geojson getGeojson() {
        return geojson;
    }

    @JsonProperty("geojson")
    public void setGeojson(Geojson geojson) {
        this.geojson = geojson;
    }

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return this.additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }

}

Als JSON Library verwende ich hier https://github.com/FasterXML/jackson, damit habe ich schon einige gute Erfahrungen gemacht und außerdem wird die Jackson JSON Library von dem von mir verwendeten Code-Generator https://www.jsonschema2pojo.org/ unterstützt :-).

Dieses einfache Beispiel zeigt, wie man eine einfache Geocodierung mit der Nominatim API durchführen kann, ohne eine eigene Datenbank und einen Server aufzusetzen. Die frei verfügbaren Endpunkte der Schnittstelle bieten noch viele andere Möglichkeiten. Alles Details zu der Nominatim Search API kann man unter https://nominatim.org/release-docs/latest/api/Search/ nachlesen.