In preparation for a presentation on Oracle Spatial for DBAs I decided to import some GPS-data. I have an app called GPSLogger on my Android mobile that logs GPS data to a file. The format for this is GPS Exchange Format (GPX). Since I spent some time understanding the XML side of this I decided to blog about it.
Note, there are two versions of the GPX schema, 1.0 and 1.1. My app uses the version 1.0, if you try to import a GPX file, have a look in it to verify which schema definition it uses (Look for xsi:schemaLocation in the head of the GPX file).
I followed the information from this blog post (that actually uses version 1.1) together with info from Oracle Support, and lots of googling.
The app can upload to Dropbox which makes it easy to transfer the file to the database server. In addition I needed the schema definition, which you can get from Topographix.com, you'll find the link to the XSD file in the GPX file as mentioned, in my case it was http://www.topografix.com/GPX/1/0/gpx.xsd
However, the file needs some adjustment in order to have Oracle parse values of type xsd:dateTime correctly. Open it in an editor and look for the following block:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:gpx="http://www.topografix.com/GPX/1/0" targetNamespace="http://www.topografix.com/GPX/1/0" elementFormDefault="qualified">
At the end of this, add one line so it looks like this:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:gpx="http://www.topografix.com/GPX/1/0" targetNamespace="http://www.topografix.com/GPX/1/0" elementFormDefault="qualified" xmlns:xdb="http://xmlns.oracle.com/xdb>"
Then you have to search through the file and search for every definition that uses type "xsd:dateTime" and add:
xdb:sqltype="TIMESTAMP WITH TIME ZONE"
so it looks like this:
xsd:element minoccurs="0" name="time" type="xsd:dateTime"
xdb:sqltype="TIMESTAMP WITH TIME ZONE"(Take away the line break, I added it to make it readable).
There are four entries, so this manual job does not take a long time. I transferred this file together with my GPX file to a directory on my server (/u01/app/oracle/load_dir). Here is the first part to get started, the first part is executed as SYSTEM:
create user oyvind identified by oracle default tablespace users temporary tablespace temp quota unlimited on users; grant create session, create table, create view, create procedure to oyvind; grant alter session to oyvind; grant create type to oyvind; create directory LOAD_DIR as '/u01/app/oracle/load_dir'; grant read, write on directory load_dir to oyvind;
The first step is to load the XML schema definition to the database. I copied the function getClobDocument from the mentioned blog, it is used to load files as CLOB:
create or replace function getClobDocument( p_directoryname In varchar2, p_filename In varchar2, p_charset In varchar2 default NULL) return CLOB deterministic is v_file bfile := bfilename(p_directoryname,p_filename); v_charContent CLOB := ' '; v_targetFile bfile; v_lang_ctx number := DBMS_LOB.default_lang_ctx; v_charset_id number := 0; v_src_offset number := 1 ; v_dst_offset number := 1 ; v_warning number; begin if p_charset is not null then v_charset_id := NLS_CHARSET_ID(p_charset); end if; v_targetFile := v_file; DBMS_LOB.fileopen(v_targetFile, DBMS_LOB.file_readonly); DBMS_LOB.LOADCLOBFROMFILE(v_charContent, v_targetFile, DBMS_LOB.getLength(v_targetFile), v_src_offset, v_dst_offset, v_charset_id, v_lang_ctx, v_warning); DBMS_LOB.fileclose(v_targetFile); return v_charContent; end; /
The following code worked for me to create resource:
declare schemaURL varchar2(256) := 'http://www.topografix.com/GPX/1/0/gpx.xsd'; schemaDoc varchar2(30) := 'gpx.xsd'; xmlSchema xmlType; res boolean; begin xmlSchema := XMLTYPE(getCLOBDocument('LOAD_DIR',schemaDoc,'UTF8')); if (dbms_xdb.existsResource('/home/' ||schemaDoc)) then dbms_xdb.deleteResource('/home/' ||schemaDoc); end if; res := dbms_xdb.createResource('/home/' || schemaDoc,xmlSchema); if res then dbms_output.put_Line('OK'); else dbms_output.put_Line('NOK'); end if; end; /Make sure you turn on server output and get a proper message to indicate if it succeeded or not; no error message was returned when it failed.
The rest of this is executed as user oyvind (Update: also create the getClobDocument from above to this user). Next step is to register the schema, I used the following code:
declare schemaURL varchar2(256) := 'http://www.topografix.com/GPX/1/0/gpx.xsd'; schemaDoc varchar2(30) := 'gpx.xsd'; begin dbms_xmlschema.registerSchema ( schemaURL, XMLTYPE(getCLOBDocument('LOAD_DIR',schemaDoc,'AL32UTF8')), local => true, genTypes => true, genTables => false, enableHierarchy => dbms_xmlschema.ENABLE_HIERARCHY_NONE ); end; /Use the following code to create a table to store the document as XMLType:
CREATE TABLE gpx OF XMLType (CHECK (XMLIsValid(object_value) = 1)) XMLSCHEMA "http://www.topografix.com/GPX/1/0/gpx.xsd" ELEMENT "gpx";
Then, finally, you can load the GPX file with this:
INSERT INTO GPX VALUES (XMLTYPE(getCLOBDocument('LOAD_DIR','20161001.gpx'))); commit;
To see some of the data, you can extract what you find interesting with:
SELECT to_number(EXTRACTVALUE(VALUE(t), 'trkpt/@lon')) longitude, to_number(EXTRACTVALUE(VALUE(t), 'trkpt/@lat')) latitude, TO_NUMBER(EXTRACTVALUE(VALUE(t), 'trkpt/ele')) Elevation, EXTRACTVALUE(value(t),'trkpt/src') src FROM GPX g, TABLE(XMLSequence(extract(g.OBJECT_VALUE,'/gpx/trk/trkseg/trkpt','xmlns="http://www.topografix.com/GPX/1/0"'))) t;
This is an example of what comes out (it was a short walk):
LONGITUDE | LATITUDE | ELEVATION | SRC |
---|---|---|---|
10.79831903 | 59.83451229 | 85 | gps |
10.80009538 | 59.83263633 | 170 | gps |
10.80033655 | 59.83156553 | 146 | gps |
10.8003321 | 59.8303762 | network |
In next post I plan to use the Spatial datatype to store these points. I may do another test with GPX version 1.1 in the mean time since that is the version used by my Garmin GPS.
Some tips if you need to clean up in case of errors; if you have the recyclebin enabled, do a "purge recyclebin" after dropping the table with the XMLType. I used to following command to delete the schema:
purge recyclebin; exec dbms_xmlschema.deleteschema(schemaurl => 'http://www.topografix.com/GPX/1/0/gpx.xsd');
Get in touch if you need a copy of the final gpx.xsd file.