Reverse Geocoding auf Basis von GeoNames.org-Daten
Über IGL 2 Analysen und betriebswirtschaftliche Auswertungen von Logistik-Prozessen Anwendungsentwicklung im Oracle-Umfeld, ab 2003 mit HTMLDB/Apex Selbständig seit 2007 als Freelancer mit Spezialisierung auf Apex 2009 Gründung der IGL GmbH mit dem Schwerpunkt Entwicklung von Standardapplikationen im Logistikumfeld Auftragsabwicklung für Logistikdienstleistungen Betriebswirtschaftliche Auswertung von Fuhrparkleistung auf Basis von Telematikdaten Seit 2013 neues Projekt Modulon Jens Gauger jens.gauger@igl-systems.de Mobil: 0178/3457824 http://www.log-cc.de http://www.igl-systems.de http://www.modulon.de https://www.xing.com/ profile/jens_gauger
Über Modulon 3 Modulon Webservice GmbH, gegründet 2013 Automatisierte Spesen- und Lohnberechnung auf Basis von Telematikdaten, z.b. aus Weitergehende Analysen wie Systemverkehre, Stundenauswertung etc. Kundenportal 100% Apex http://www.modulon.de
4 Reverse Geocoding
Use Case 5 Datenbasis für die Spesenermittlung sind Daten aus Telematiksystemen oder auch aus dem digitalen Tachografen eines LKW (DTCO) In der Regel enthalten die Daten Geoinformationen in Form von Längenund Breitengraden aber ohne zugehörige Positionsdaten Für die Berechnung von Auslandsspesen ist das Wissen um das zuletzt befahrene Land (nicht DE) an einem Tag erforderlich => Positionsinformationen (Ort und LKZ) müssen per Reverse Geocoding extrahiert werden
Reverse Geocoding selbst gebaut 6 Auch ohne datenbank-spezifische Funktionen lassen sich die Entfernungen zwischen zwei Punkten auf einer kugelförmigen Oberfläche berechnen https://de.wikipedia.org/wiki/ Orthodrome Nachteil: Sehr langsam, da kein Geometrie-Index verfügbar DECLARE l_lat_start NUMBER; c_pi NUMBER := 3.14159265;... BEGIN l_lat_start := p_lat_start * c_pi / 180; l_long_start := p_long_start * c_pi / 180; l_lat_stop := p_lat_stop * c_pi / 180; l_long_stop := p_long_stop * c_pi / 180; n1 := SIN(l_lat_start) * SIN(l_lat_stop) + COS(l_lat_start) * COS(l_lat_stop) * COS((p_long_start-p_long_stop)*c_pi/180); IF (p_long_start-p_long_stop) < 0 THEN n2 := 0; ELSE n2 := 360; END IF; richtung := ABS(ACOS(SIN(l_lat_stop) / SIN(ACOS(n1)) / COS(l_lat_start) - TAN(l_lat_start) / TAN(ACOS(n1))) * 180 / c_pi - n2); naut_miles := 6371.229 / 1.852 * ACOS(N1); kilometer := naut_miles * 1.852; END;
Oracle Locator vs. Spatial 7 Oracle Locator ist Bestandteil der Datenbank ab 11g SE1 SDO_GEOMETRY-Objekt Basisfunktionen für die Verwaltung von Spatial Daten Details siehe z.b. http://docs.oracle.com/cd/e11882_01/appdev.112/e11830/sdo_locator.htm#spatl340 Oracle Spatial ist lizenzpflichtige Zusatzoption für EE Geeignet für komplexe GIS-Anwendungen Unterstützung von 3D-Geometrien u.v.m. Routenplanung, Geocodierung etc. out-of-the-box
Oracle Locator vs. Spatial 8
Quellen für Geodaten 9 Google Maps Geocoding API Nur online, lizenzpflichtiges Produkt Oracle Partner, z.b. HERE Geocoder API (ehemals Navteq, Nokia) Online, lizenzpflichtiges Produkt Offline => Beispieldaten für Spatial Option verfügbar unter http://www.oracle.com/technetwork/database/options/spatial/spatial-partners-data-087203.html MapQuest Nomination API (AOL) Nur online, limitierte Anfragen, lizenzfrei OpenStreetMap, PickPoint, OpenCage Nur online, limitierte Anfragen, lizenzfrei OpenGeoDB oder GeoNames Offline verfügbar, lizenzfrei
GeoNames 10 www.geonames.org Datenbank mit über 10 Millionen Datensätzen Daten stammen aus öffentlichen Quellen in den jeweiligen Ländern Lizensiert unter Creative Commons Lizenz Zugriff über Webservices und täglichem Datenbankexport Kostenlose Version umfasst PLZ-basierte Positionsdaten für 252 Länder Datensatz enthält u.a. Längen- und Breitengrad in Format wgs84* ISO2-Ländercode, Postleitzahl, Ortsname *wgs84 - World Geodetic System 1984, einheitliche Grundlage für Positionsangaben
Vorgehensweise 11 1. Download eines Geonames-Export 2. Import der Exportdatei in Oracle-Datenbank per Data Loader 3. Aufbereitung der Daten 4. Erstellung eines Spatial Index 5. Daten abfragen
Daten laden und aufbereiten 12 Download der Datei allcountries.zip von http://download.geonames.org/ export/zip/ entpacken und in einem Verzeichnis auf dem DB-Host speichern Directory-Eintrag für das Quellverzeichnis erstellen * CREATE DIRECTORY GEONAMES_DIR FOR '/path/to/file' Daten als externe Tabelle einbinden (Step 1) Daten in neue Tabelle transformieren (Step 2) Werte für SDO_GEOMETRY-Spalte berechnen (Step 3) * https://wiki.gutzmann.com/confluence/display/howto/geonames.org+-+load+dumps+as+external+oracle+tables
Daten als externe Tabellen einbinden 13 CREATE TABLE "GEO_ALL_COUNTRIES" ( "COUNTRY_CODE" VARCHAR2(2 BYTE), "POSTAL_CODE" VARCHAR2(40 BYTE), "PLACE_NAME" VARCHAR2(400 BYTE), ) ORGANIZATION EXTERNAL ( TYPE ORACLE_LOADER DEFAULT DIRECTORY "GEONAMES_DIR" ACCESS PARAMETERS ( records delimited BY '\n' CHARACTERSET UTF8 BADFILE DATA_PUMP_DIR:'allCountries.bad' DISCARDFILE DATA_PUMP_DIR:'allCountries.discard' LOGFILE DATA_PUMP_DIR:'allCountries.log' skip 0 fields terminated BY X'09' missing field VALUES are NULL ( country_code CHAR(4000), postal_code CHAR(4000), place_name CHAR(4000), ) ) LOCATION ( 'allcountries.txt' )) REJECT LIMIT UNLIMITED; Probleme: Alle Spalten sind CHAR Transformation erforderlich für Latitude/Longitude Weitere Spalte vom Typ SDO_GEOMETRY wird benötigt, um einen Spatial Index erstellen zu können Fazit: Weitere Tabelle muss erstellt werden
Daten in neue Tabelle transformieren 14 CREATE TABLE "GEONAMES_ALL_COUNTRIES" ( "COUNTRY_CODE" VARCHAR2(2 BYTE), "POSTAL_CODE" VARCHAR2(40 BYTE), "PLACE_NAME" VARCHAR2(400 BYTE), "ADMIN_NAME1" VARCHAR2(400 BYTE), "ADMIN_CODE1" VARCHAR2(40 BYTE), "ADMIN_NAME2" VARCHAR2(400 BYTE), "ADMIN_CODE2" VARCHAR2(40 BYTE), "ADMIN_NAME3" VARCHAR2(400 BYTE), "ADMIN_CODE3" VARCHAR2(40 BYTE), "LATITUDE" NUMBER, "LONGITUDE" NUMBER, "ACCURACY" NUMBER(1,0), "GEOMETRY" "MDSYS"."SDO_GEOMETRY" ) SEGMENT CREATION IMMEDIATE; Neue Zieltabelle erstellen Spalte vom Typ SDO_GEOMETRY hinzufügen Spatial Index auf Geometrie-Spalte erstellen CREATE INDEX "GEONAMES_ALL_COUNTRIES_INDEX1" ON "GEONAMES_ALL_COUNTRIES" ("LATITUDE", "COUNTRY_CODE"); CREATE INDEX "GEONAMES_ALL_COUNTRIES_INDEX2" ON "GEONAMES_ALL_COUNTRIES" ("COUNTRY_CODE"); CREATE INDEX "GEONAMES_ALL_COUNTRIES_SPX" ON "GEONAMES_ALL_COUNTRIES" ("GEOMETRY") INDEXTYPE IS "MDSYS"."SPATIAL_INDEX" ;
Werte für SDO_GEOMETRY-Spalte berechnen 15 Die Spalte vom Typ SDO_GEOMETRY wird mit folgender Anweisung gefüllt UPDATE TABLE GEONAMES_ALL_COUNTRIES SET geometry=sdo_geometry( 2001, Zwei-dimensionales Objekt/Einzelner Punkt 4326, Koordinatensystem WGS84 / GPS Standard SDO_POINT_TYPE( SDO-POINT longitude, x-achse latitude, y-achse NULL), z-achse NULL, SDO_ELEM_INFO, hier nicht relevant NULL) SDO_ORDINATES, hier nicht relevant
Geokoordinaten auflösen 16 Aus dem Use Case ergibt sich die Anforderung: Finde den Eintrag in der Orte-Datenbank, der am nächsten zu einer Koordinate liegt! Der relevante Locator-Operator, den Oracle dafür anbietet, ist SDO_NN Eine Abfrage kann wie folgt aussehen SELECT country_code, postal_code, place_name, admin_code1 FROM geonames_all_countries ac WHERE SDO_NN( ac.geometry, Spalte mit SDO_GEOMETRY-Wert SDO_GEOMETRY( Geometrie-Objekt für Referenzpunkt 2001, 4326, SDO_POINT_TYPE (p_longitude, p_latitude, null) Referenzpunkt NULL, NULL), 'sdo_num_res=1') Nur ein Ergebniswert wird benötigt ='TRUE';
Geocoding-Methoden in Oracle 17 Bereits in der Standard Edition der Datenbank stehen viele weitere Funktionen für die Arbeit mit Geodaten zur Verfügung Weitere interessante Locator-Operatoren sind z.b. SDO_NN_DISTANCE ermittelt die Distanz für SDO_NN SDO_WITHIN_DISTANCE prüft, ob zwei Punkte innerhalb einer bestimmten Distanz liegen u.v.m
Ausführung und Zeitverhalten 18 Telematikdaten werden in einer Event- Tabelle abgelegt Reverse Geocoding erfolgt über einen Before-Update-Trigger Dauer: < 0,02 sek Ca. 500.000 Vorgänge je Monat Entspricht ca. 5 min Rechenzeit / Tag BEFORE INSERT ON POSITIONED_EV_DATA FOR EACH ROW BEGIN IF :NEW.ped_longitude IS NOT NULL AND :NEW.ped_latitude IS NOT NULL THEN MODULON_GEODB.geonames_api.get_nn( p_longitude => :NEW.ped_longitude, p_latitude => :NEW.ped_latitude, o_country_code => :NEW.ped_geonames_country_code, o_postal_code => :NEW.ped_geonames_postal_code, o_place_name => :NEW.ped_geonames_place_name, END IF; END; o_state_name => :NEW.ped_geonames_state_name);
Anwendung 19 Demo