User:Oli-Wan/Wall-E/Tech
Der Code von Wall·E ist bislang nicht öffentlich. Dies hat drei Gründe:
- Der Code ist schlicht noch nicht veröffentlichungsreif.
- Der Code ist in Emacs Lisp geschrieben, einer im OSM-Umfeld wenig verbreiteten Sprache.
- Mit den vorhandenen Programmen lassen sich mit geringem Aufwand auch problematische Massenedits durchführen. Daher habe ich Bedenken, sie durch Veröffentlichung (auch) in die falschen Hände fallen zu lassen.
Daher gibt es hier nur eine Beschreibung der Algorithmen, jedoch nicht deren fertige Implementierung.
Im folgenden werden die verwendeten Algorithmen am Beispiel der Korrektur von Straßennamen näher erläutert; die übrigen Korrekturen sind weitestgehend analog umgesetzt. Zum grundsätzlichen Verständnis der Vorgehensweise sind diese Einzelheiten nicht nötig; die Informationen hier sind als Starthilfe für den Fall gedacht, daß jemand ein ähnliches Werkzeug entwickeln möchte. (Es geht hier ausschließlich um technische Aspekte; für welche Aufgaben ein Boteinsatz sinnvoll ist, wird hier nicht thematisiert und wird im konkreten Fall mit der Community zu diskutieren sein. Auch an die Mechanical Edit Policy sei erinnert.)
Filterung
Vorüberlegungen
Es sollen Kandidaten gefunden werden, die folgenden Kriterien entsprechen
- Objekttyp Weg
- highway=* mit einem straßentypischen Wert (alles von highway=path bis highway=motorway)
- "fehlerhaftes" name=*-Tag
- vollständig innerhalb Deutschlands
Die Filterung nach Tags darf ruhig etwas grob sein, also zu viele Objekte erfassen, da bei der Korrektur dieses Kriterium ohnehin noch einmal überprüft wird. Dagegen muß die Prüfung der räumlichen Lage zuverlässig und exakt erfolgen, da die zur Korrektur verwendete Plattform hierauf nicht ausgelegt ist.
Als Ausgangsmaterial dient ein Deutschland-Extrakt der Geofabrik im pbf-Format. Dieses wird von einem Filterprogramm eingelesen und Objekt für Objekt auf die Filterkriterien überprüft. Dabei werden jedoch die Abhängigkeiten nicht berücksichtigt, sodaß anschließend die zu einem Wege gehörenden Knoten (und Elemente von Relationen) fehlen. Diese werden von einem weiteren Programm nachträglich aus dem Extrakt gelesen und anschließend durch osmosis mit ihren "Eltern" zusammengeführt. Das Ergebnis ist eine osm-Datei mit den gefilterten Wegen inklusive ihrer Knoten.
Die Geofabrik-Extrakte sind prinzipbedingt stets etwas zu groß, d.h. der Filtervorgang bis hier wird auch Wege außerhalb Deutschlands (insbesondere in der Schweiz) liefern. Mit Hilfe der nachgeladenen Knoten werden die Wege dahingehend überprüft, ob sie vollständig innerhalb des Grenzpolygons liegen. Für diese Filterung kommt osmfilter zum Einsatz (gepatcht um eine höhere Grenze für die Größe von Polygonen).
Algorithmen zur Erkennung der diversen Fehlertypen
Die Algorithmen sind hier schematisch als vereinfachter Auszug aus dem C++-Code dargestellt. Qualifizierer, Elementzugriffe usw. sind der Einfachheit halber teilweise ausgelassen. Die angegebenen Regexe etc. dienen nur der Illustration und nicht notwendigerweise vollständig bzw. aktuell. Übertragungsfehler sind ebenfalls wahrscheinlich.
Straßennamen
bool-Funktion; true im Falle eines Treffers. Wird verwendet in Kombination mit einer Tagfilter-Klasse.
if (!get_value_by_key ("highway") || !get_value_by_key ("name")) return false; return (regex_match (get_value_by_key ("highway", "primary(_link)?|secondary(_link)?|...") && regex_search (get_value_by_key ("name", "[Ss](tr(asse|\\.)?( |$)|..."));
Adressen
bool-Funktion; true im Falle eines Treffers. Wird verwendet in Kombination mit einer Tagfilter-Klasse. t ist ein Taglist-Objekt.
- Falls addr:country vorhanden: Veto auf Werte NL,BE,FR,CH,...; Treffer bei sonstigen Werten außer DE.
if (tag_value = t.get_value_by_key (key_addr_country)) { if (regex_match (tag_value, "AT|CH|FR|LU|BE|NL|DK|PL|CZ")) return false; if (!regex_match (tag_value, "DE")) return true; }
- Falls addr:postcode vorhanden: Treffer, wenn nicht aus fünf Ziffern bestehend
if (tag_value = t.get_value_by_key (key_addr_postcode.c_str())) { if (!regex_match (tag_value, "[[:digit:]]{5}")) return true; }
- Falls addr:street vorhanden: Treffer bei falschen Schreibweisen oder Ziffern, zu letzterem jedoch etliche Ausnahmen
if (tag_value = t.get_value_by_key (key_addr_street)) { if (regex_search (tag_value, "[Ss]tr(\\.|asse)?( |$)|...") return true; if (regex_search (tag_value, "[[:digit:]]") && !(regex_match (tag_value, "(Straße|Platz) des [[:digit:]]{1,2}\\. (Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)") || regex_match (tag_value, "(An der (alten )?)?([ABLK] ?|((Bundes|Landes|Kreis)straße) )[[:digit:]]+") || ... // weitere Ausnahmen )) return true; }
- Falls addr:housenumber vorhanden: Treffer bei typischem Fehlermuster "Straße Hausnummer", jedoch verschiedene Ausnahmen
if (tag_value = t.get_value_by_key (key_addr_housenumber)) { if ((regex_search (tag_value, "[[:digit:]]|^[[:lower:]]|[[:lower:]][[:upper:]]")) && !(regex_match (tag_value, housenumber_exception1)) && !(regex_match (tag_value, housenumber_exception2))) return true; }
- Falls addr:city vorhanden: Treffer bei Ziffer, Kleinbuchstabe am Anfang oder Kombination Kleinbuchstabe-Großbuchstabe
if (tag_value = t.get_value_by_key (key_addr_city)) { if (regex_search (tag_value, "[[:digit:]]|^[[:lower:]]|[[:lower:]][[:upper:]]")) return true; }
Leerraum
bool-Funktion; true im Falle eines Treffers. Wird verwendet in Kombination mit einer Tagfilter-Klasse. Der Iterator bezieht sich auf ein Taglist-Objekt.
for (const_iterator i = begin(); i != end(); ++i) { if ((regex_search (i->value(), "[[:blank:]]$|^[[:blank:]]") && !(regex_search (i->key(), "note|..."))) || regex_search (i->key(), "[[:blank:]]$|^[[:blank:]]")) return true; } return false;
Mehrfache Knoten
Funktion void way() von RepeatedNodesHandler<THandler> als abgeleiteter Klasse von Osmium::Handler::Forward<THandler>.
osm_object_id_t previous (-1); bool repeatedNode = false; for (const_iterator i = way->nodes().begin(); i != way->nodes().end(); ++i) { if (i->ref() == previous) { repeatedNode = true; break; } previous = i->ref(); } if (repeatedNode) Forward<THandler>::way (way);
Wege mit nur einem Knoten
Funktion void way() von SingleNodeWaysHandler<THandler> als abgeleiteter Klasse von Osmium::Handler::Forward<THandler>
if (way->nodes().size() < 2) Osmium::Handler::Forward<THandler>::way (way);
Implementierung
Details folgen
(Anstelle des hier zuvor gezeigten AWK-Programms wird ein vielfach schnelleres C++-Programm eingesetzt. Der Quellcode wird später bereitgestellt werden.)
Korrektur
Details folgen