OpenStreetMap

Erste Gehversuche II

Posted by mawi2002 on 17 March 2019 in German (Deutsch).

« Blog: Erste Gehversuche I

Datengewinnung

mit großem Dank an Harald Hartmann

Grundsätzlich könnte es so laufen

JOSM

  • Installiere und starte JOSM (Java OpenStreetmap Editor)
  • Hier gibt man nun das Gebiet ein, dessen Grenzen heruntergeladen werden sollen (z.B. Sachsen)
  • Daten herunterladen
  • Glücklich sein

Leider sagt das Programm: “Der OSM-Server api.openstreetmap meldete eine fehlerhafte Anfrage. Das angeforderte Gebiet ist zu groß oder enthält zu viele Daten. Versuchen Sie, ein kleineres Gebiet herunterzuladen oder nutzen Sie einen Datenbankexport.”

Die Meldung ist verständlich, wenn man bedenkt, wieviele Daten in einer kleinen Fläche stecken - geschweige denn in einer großen. Wir sind hier aber nur an den Grenzen interessiert, was die Datenmenge wiederum überschaubar macht.

geofabrik

wambacher’s boundaries map

  • https://wambachers-osm.website/boundaries/
  • Download in verschiedenen Formaten möglich
  • Zum Download muss man angemeldet sein; Anmeldung möglich per SSO mit den Daten von OSM
  • Nur Länder- aber keine Bundesländergrenzen
  • Download per Kommandozeile; die Anweisung wird generiert und angezeigt, die man dann nur noch in das eigene Kommandozeilentool copy&pasten muss

overpass-turbo

  • http://overpass-turbo.eu/
  • Format: shp
  • Trotz Wizard muss man schon Vorwissen mitbringen. Die Abfrage zum Beispiel Sachsen lautet:
[out:json][timeout:25];
(
  relation(62467);
);
out body;
>;
out skel qt;

Mein Versuch im Wizard mit “way:border in Sachsen” führte zu diesem Code:

[out:json][timeout:25];
{{geocodeArea:Sachsen}}->.searchArea;
(
  way(area.searchArea);
);
out body;
>;
out skel qt;

Beim Ausführen gab es dann diese Meldung:

Ein Fehler ist während der Ausführung der Abfrage aufgetreten. Folgendes hat die API als Fehlermeldung zurückgegeben: runtime error: Query run out of memory using about 2048 MB of RAM.

Man sollte also durchaus wissen, was man macht ;o)

osmfilter und osmconvert

Leider muss man auch hier einiges Vorwissen mitbringen, um mit dem Tool arbeiten zu können.

Performance und Speicherplatz

Zu viele Punkte später aus der Datenbank on-the-fly in einen Layer zu lesen ist performance-kritisch. Daher schaue ich schon hier, was sich ergibt.

Beispiel für die Grenze des Bundeslandes Sachsen:

  • Anzahl Knoten: ca. 38.000
  • Anzahl Wege: ca 500
  • Speichergröße in der Datenbank: ca. 7 MB

Wenn man später die Daten zum angrenzenden Bundesland Brandenburg prüft, findet man knapp 70 gemeinsame Wege, d.h. diese Daten werden dann nicht noch einmal heruntergeladen. Das verkürzt zumindest die Zeit zum Befüllen der Datenbank ein wenig.

Im Vorfeld hatte ich mir Daten der Bundesländer-Grenzen von ArcGIS heruntergeladen, mit QGIS zu gpx gewandelt und in diesem Format eine lokale Datenbank befüllt. Anschließend hatte ich per Script die Koordinaten aus dieser Datenbank gelesen, zu einem Layer gewandelt und auf einer OSM Karte angezeigt. Die Performance bei diesen ~20.000 Knoten war beeindruckend gut. Wenn nun allein Sachsen 38.000 Punkte liefert, bleibt abzuwarten, wie sich das gleiche Script mit den OSM-Nodes für ganz Deutschland verhält.

Das Programm zum Datenlesen

Yet another Tool… Dies ist ein php Script, das die Linien und daraus die Knoten jeder Linie ausliest und in einer lokalen Datenbank speichert (hier: mysql).

Beim Start wird per GET-Variable die Nummer der Relation übergeben, z.B. könnte die URL dann so aussehen: http://localhost/~ich/myProject/get_relation.php?relation=62504

Code von get_relation.php:

<?php
    // ----- Check Database Connection ------------------------------ //
    $mysqli = new mysqli(_fill_your_own_data_);
  
    if ($mysqli->connect_errno) {
        printf("Connect failed: %s\n", $mysqli->connect_error);
        exit();
    }
    
    // -------------------------------------------------------------- //
    // ----- Relation >> Way, get and store rel-way relationships --- //
    // -------------------------------------------------------------- //
    $urlMain  = "https://www.openstreetmap.org/api/0.6/";
    $relation = $_GET["relation"];
    $year     = 2019;
    $wayArr   = array(); //array with all ways according to our relation
    
    // Is this relation already in DB? If yes, get $wayArr from here  //
    $sql = "SELECT id FROM relations WHERE id = $relation;";
    $result = $mysqli->query($sql);
   
    if ($result->num_rows > 0) {
        
        echo "Relation-Ways already stored";
        flush();

        $sql = "SELECT member_id FROM relation_members ".
               "WHERE relation_id = $relation AND member_type = 'way'";

        $result2 = $mysqli->query( $sql );
        if (!($result2->num_rows > 0)) return;
        
        while ($row = $result2->fetch_assoc())
        {
            array_push($wayArr,$row["member_id"]);
        }
        $result2->free();
    } 
    else // Relation not yet in DB
    {
        // ----- In case SOME members are stored already ------------ //
        $sql = "DELETE FROM relation_members ".
               "WHERE relation_id = $relation";
        $mysqli->query( $sql );
        
        // ----- Read Relation-XML-File from OSM -------------------- //
        // store Data in $relWayArr
        $xmlDataRel = file_get_contents($urlMain."relation/".$relation);
  
        $p = xml_parser_create();
        xml_parse_into_struct($p, $xmlDataRel, $relWayArr, $index);
        xml_parser_free($p);

        $lauf = 0;
        foreach ($relWayArr as &$member)
        {
            if ($member["tag"] != "MEMBER") {continue;}
            if ($member["attributes"]["TYPE"] != "way") {continue;}
        
            $lauf++;        

            $sql = "INSERT INTO relation_members ".
                "(relation_id,members_order,member_type,member_id,role)".
                " VALUES ($relation,$lauf,'"
                            .$member["attributes"]["TYPE"]."',"
                            .$member["attributes"]["REF"].",'"
                            .$member["attributes"]["ROLE"]."');";
            $mysqli->query( $sql );     

            array_push($wayArr,$member["attributes"]["REF"]);
        }

        // ----- Store Relation itself ------------------------------ //
        // means: if ths entry is in Database, then all relation-way 
        // relationships are included in the database, too
        $sql = "INSERT INTO relations(id,description,valid_from,valid_to) ".
               "VALUES ($relation,'',$year,9999);";
        $mysqli->query( $sql );
    }

    $result->free();
    echo "
This relation consists of ".sizeOf($wayArr)." Ways"; flush(); // -------------------------------------------------------------- // // ----- Way >> Node, get and store way-node relationships ------ // // -------------------------------------------------------------- // $idx = 0; foreach ($wayArr as &$member) { $idx++; // Is this relation already in DB? If yes, get $wayArr from here $sql = "SELECT id from ways WHERE id = $member"; $result = $mysqli->query($sql); if ($result->num_rows > 0) { echo "
($idx/".sizeOf($wayArr) .") Way $member already stored"; flush(); continue; } else { // --- In case SOME way-node relations are stored already // $sql = "DELETE FROM way_nodes WHERE way_id = $member"; $mysqli->query( $sql ); $xmlDataWay = file_get_contents($urlMain."way/".$member); $p = xml_parser_create(); xml_parse_into_struct($p, $xmlDataWay, $wayNodeArr, $index); xml_parser_free($p); $lauf = 0; foreach ($wayNodeArr as &$node) { if ($node["tag"] != "ND") {continue;} $lauf++; $sql = "INSERT INTO way_nodes ". "(way_id,nodes_order,node_id)". " VALUES (".$member."," .$lauf."," .$node["attributes"]["REF"].");"; echo "
Insert way -- node: $member -- " . $node["attributes"]["REF"]; flush(); $mysqli->query( $sql ); // -------------------------------------------------- // // ----- Node, get and store node coordinates ------- // // -------------------------------------------------- // $xmlDataNode = file_get_contents($urlMain."node/" .$node["attributes"]["REF"]); $p = xml_parser_create(); xml_parse_into_struct($p, $xmlDataNode, $nodeXML, $index); xml_parser_free($p); $att = $nodeXML[1]["attributes"]; $sql = "INSERT INTO nodes(id,description,lon,lat) ". "VALUES (".$att["ID"] .",''," .$att["LON"]."," .$att["LAT"].");"; $mysqli->query( $sql ); } // ----- Store Relation itself -------------------------- // // means: if ths entry is in Database, then all relation-way // relationships are included in the database, too $sql = "INSERT INTO ways(id,description) ". "VALUES ($member,'');"; echo "
($idx/".sizeOf($wayArr).") Way " .$member." with all nodes complete"; flush(); $mysqli->query( $sql ); } $result->free(); } $mysqli->close(); ?>

Discussion

Comment from Harald Hartmann on 17 March 2019 at 16:11

Datenbankexport entfällt für mich - zu groß. Kleineres Gebiet auswählen macht wenig Sinn, wenn ich alle Punkte der Grenze von Sachsen bekommen möchte. Also muss ich entweder mit anderen Tools arbeiten (welche?) oder mir selbst helfen. Wenn hier jemand eine gute Idee hat - dann bitte einen Kommentar hinterlassen!

Also:

  1. Datenextrakt der geofabrik
  2. osmfilter und osmconvert
  3. Grenzen würdest du in unterschiedlichen Formaten bei wambacher’s boundaries map bekommen
  4. mit einer overpass Abfrage die Daten ermitteln

Die genannten Punkte sind alle mit ODER verknüpft ;-)

Comment from Harald Hartmann on 18 March 2019 at 07:08

Problem Dateigröße! Beispiel Sachsen: 170 - 280 MB

Wieso ist die Dateigröße ein Problem? Nur zur Klarstellung: in dieser Datei ist wirklich ALLES drin, d.h. wenn man nur bestimmte Teile davon haben möchte, kann man sich diese eben mit osmconvert und osmconvert in kleinere Dateien herausfiltern.

Nur Länder- aber keine Bundesländergrenzen

Klar kann wambacher’s Boundaries Map auch Bundesländergrenzen, sogar bis runter auf Ortsebene, wenn man möchte … bzw. sofern erfasst. Man muss dazu nur links sich im Baum entsprechend durchhangeln ;-)

overpass-turbo: Trotz Wizard muss man schon Vorwissen mitbringen.

Der Wizard ist eher für einfache Abfragen gedacht, z.B. “Sitzbank in Dresden” oder so. Eine Grenze via Wizard abzufragen ist in der Tat wohl nicht vorgesehen.

Log in to leave a comment