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
- http://download.geofabrik.de
- Bundesländergrenzen: http://download.geofabrik.de/europe/germany.html
- Download-Formate: osm.pbf, shp, osm.bz2
- Problem Dateigröße! Beispiel Sachsen: 170 - 280 MB
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
- https://wiki.openstreetmap.org/wiki/DE:Osmfilter
- Kommandozeilentool
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
Also:
Die genannten Punkte sind alle mit ODER verknüpft ;-)
Comment from Harald Hartmann on 18 March 2019 at 07:08
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.
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 ;-)
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.