Geocoding mit Open Street Map API

Julian Lang
Julian Lang

20. Apr, 2022 | 3 Min. Lesezeit

Manchmal braucht man zu einer Adresse die Geo-Koordinaten. Für diese Aufgabe kann man die kostenfreie API von Open Street Map verwenden.

Beispiel

Du legst einen Custom Post Type: standort an, weil du auf einer Kunden Webseite die Niederlassungen der Firma darstellen willst. Später möchtest du diese mit Google Maps darstellen oder eine Umkreissuche für diesen Post Type bauen.

Dazu musst du die vom Benutzer eingegebene Adresse des Standortes in Geo-Koordinaten umwandeln.

Zusätzlich erstellst du für den Post Type noch ein Textfeld z.B. mit ACF: adresse in das die Benutzer die Adresse des Standortes als Freitext eintippen können.

Die Open Street Map API (Nominatim)

Hier geht es zur Dokumentation: https://nominatim.org/release-docs/develop/api/Search/

In unserem Beispiel verwenden wir die Freitextsuche du kannst aber auch spezifischer suchen.

Endpunkt Freitextsuche

https://nominatim.openstreetmap.org/search?q=Musterstraße 1, 11111 Musterstadt

Endpunkt spezifische Suche

https://nominatim.openstreetmap.org/search?street=Musterstraße 1&postalcode=11111&city=Musterstadt

Ausgabe / Response als JSON

Um die Suchergebnisse als JSON zu erhalten müssen wir noch den format=json Parameter anfügen:

https://nominatim.openstreetmap.org/search?q=Hauptbahnhof Berlin&format=json

Damit erhalten wir, sofern die Suche ein Ergebnis findet schon die Koordinaten (lat, lon) die wir benötigen:

[
  {
    "place_id": 22416464,
    "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
    "osm_type": "node",
    "osm_id": 2459919675,
    "boundingbox": [
      "52.5196361",
      "52.5296361",
      "13.364861",
      "13.374861"
    ],
    "lat": "52.5246361",
    "lon": "13.369861",
    "display_name": "Berlin Hauptbahnhof (tief), Eingangsebene, Moabit, Mitte, Berlin, 10557, Deutschland",
    "class": "railway",
    "type": "station",
    "importance": 0.6988462520106494,
    "icon": "https://nominatim.openstreetmap.org/ui/mapicons//transport_train_station2.p.20.png"
  },
]

Zusätzliche Parameter

Man kann auch noch weitere Parameter anfügen um noch mehr Details zu erhalten.

addressdetails

https://nominatim.openstreetmap.org/search?q=Hauptbahnhof Berlin&format=json&addressdetails=1

Erweitert die Response um folgende Daten:

"address": {
    "railway": "Berlin Hauptbahnhof (tief)",
    "road": "Eingangsebene",
    "suburb": "Moabit",
    "borough": "Mitte",
    "city": "Berlin",
    "state": "Berlin",
    "postcode": "10557",
    "country": "Deutschland",
    "country_code": "de"
}

extratags

https://nominatim.openstreetmap.org/search?q=Hauptbahnhof Berlin&format=json&extratags=1

Erweitert die Response um öffentlich verfügbare Daten wie z.B. Wikipedia

"extratags": {
    "layer": "-2",
    "train": "yes",
    "uic_ref": "8098160",
    "operator": "DB Netz AG",
    "uic_name": "Berlin Hauptbahnhof (tief)",
    "wikidata": "Q1097",
    "wikipedia": "de:Berlin Hauptbahnhof",
    "start_date": "2006-05-28",
    "wheelchair": "yes",
    "railway:ref": "BL",
    "public_transport": "station",
    "toilets:wheelchair": "yes",
    "railway:station_category": "1"
}

namedetails

https://nominatim.openstreetmap.org/search?q=Hauptbahnhof Berlin&format=json&namedetails=1

Erweitert die Response um alternative Namen der Adresse

"namedetails": {
    "name": "Berlin Hauptbahnhof (tief)",
    "name:de": "Berlin Hauptbahnhof (tief)",
    "short_name": "Berlin Hbf (tief)",
    "official_name": "Berlin Hauptbahnhof - Lehrter Bahnhof"
}

Eine Kontakt E-Mail angeben (optional)

Verwendest du die API regelmäßig und in hoher Frequenz wird darum gebeten eine Kontakt E-Mail anzugeben.

https://nominatim.openstreetmap.org/search?q=Hauptbahnhof Berlin&format=json&[email protected]

Die API Klasse

Füge diese Klasse in dein Theme oder Plugin ein.

class Nominatim
{

    public static function endpoint( $endpoint ) {
        return [
            'endpoint' => 'https://nominatim.openstreetmap.org/'.$endpoint
        ];
    }


    public static function search( $string ) {

        $auth = self::endpoint( 'search/?format=json&q='.$string.'&addressdetails=1&[email protected]' );

        $response = wp_remote_get( $auth['endpoint'] );

        if ( is_wp_error( $response ) ) {
            if ( defined('WP_DEBUG_LOG') && true === WP_DEBUG_LOG ) {
                error_log( $response->get_error_message() );
            }
            return 0;
        }

        $response = wp_remote_retrieve_body( $response );
        $response = json_decode( $response );

        if ( empty( $response ) ) {
            return 0;
        }

        // Nimm das erste Item, falls es mehrere Treffer gibt ist das erste meist das Beste
        $response = $response[0];
        return $response;

    }


    public static function get_coords( $string ) {

        $response = self::search( $string );

        $lat = $response->lat;
        $lon = $response->lon;

        $coords = [
            'lat' => $lat,
            'lon' => $lon
        ];

        return $coords;

    }
}

Mit der Funktion get_coords( $string ) erhalten wir nur die Koordinaten die wir benötigen.

Die Koordinaten zu dem Post abspeichern, wenn ein Post aktualisiert wird

Wir verwenden den ACF Hook save_post um bei Update eines Standort Posts die Koordinaten über die API zu holen und speichern diese als Custom Fields ins Post Meta

// ACF Hook nach Update eines Postes
add_action( 'acf/save_post', 'update_coords' );

function update_coords( $post_id ) {

    // Ermittelt den Post Type
    $post_type = get_post_type( $post_id );

    // Update nur bei "Standort" Posts
    if ( 'standort' === $post_type ) {

        // Erhalte alle ACF Felder des Postes
        $fields = get_fields( $post_id );

        // Erhalte den Wert des neuen Adresse Feldes
        $adresse = $fields['adresse'];

        // Ruft die OSM Api Klasse auf und gibt die Koordinaten zurück
        $coords = \Nominatim::get_coords( $adresse );
        $lat = $coords['lat'];
        $lon = $coords['lon'];

        // Updated die Koordinaten als normale Custom Fields
        update_post_meta( $post_id, 'lat', $lat );
        update_post_meta( $post_id, 'lon', $lon );

    }    

}

Links

Usage Policy: https://operations.osmfoundation.org/policies/nominatim/

API Dokumentation: https://nominatim.org/release-docs/develop/api/Search/

ACF: save_posts: https://www.advancedcustomfields.com/resources/acf-save_post/

geschrieben von

Autor Avatar
Julian Lang

ist PHP / WordPress Entwickler. Arbeitet außerdem als Allrounder bei docrelations.de und entwickelt zwei coole Projekte: jadento.de | lifeisabinge.com

Schreibe einen Kommentar

© 2015 - 2022 | Julian Lang Webentwickler | WordPress Entwickler | Webdesigner in Bayreuth und Umgebung