package org.pyneo.tabulae.track;
import android.util.Log;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.database.sqlite.SQLiteDatabase;
import static org.pyneo.tabulae.track.Constants.*;
class TrackGpxParser {
/* parse xml like:
* <gpx ...><name>...</name><desc /><trk><trkseg><trkpt lon="..." lat="..."><ele>..</ele><time>2015-08-11T00:00:00Z</time>...
* see https://de.wikipedia.org/wiki/GPS_Exchange_Format
* see http://www.topografix.com/GPX/1/1/
*/
static final SimpleDateFormat[] simpleDateFormats = new SimpleDateFormat[]{
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US),
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US),
new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ", Locale.US),
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US),
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.US),
new SimpleDateFormat("yyyy-MM-dd HH:mmZ", Locale.US),
new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US),
new SimpleDateFormat("yyyy-MM-dd", Locale.US),
};
private static final String CMT = "cmt";
private static final String DESC = "desc";
private static final String ELE = "ele";
private static final String LAT = "lat";
private static final String LON = "lon";
private static final String NAME = "name";
private static final String TIME = "time";
private static final String TRKPT = "trkpt";
private static final String TRK = "trk";
private static final String WPT = "wpt";
static SAXParserFactory factory = SAXParserFactory.newInstance();
static {
final TimeZone UTC = TimeZone.getTimeZone("UTC");
for (SimpleDateFormat sdf : simpleDateFormats) {
sdf.setTimeZone(UTC);
}
}
SQLiteDatabase db;
SimpleDateFormat successSdf = simpleDateFormats[0];
TrackItem trackItem;
public TrackGpxParser(File file, SQLiteDatabase db) throws Exception {
SAXParser parser = factory.newSAXParser();
parser.parse(file, new Handler());
}
Date parseDate(final String str) {
try { // try the last successful format to parse
return successSdf.parse(str);
}
catch (ParseException ignore) {
}
for (SimpleDateFormat sdf : simpleDateFormats) {
try {
successSdf = sdf;
return sdf.parse(str);
}
catch (ParseException e) {
// if (DEBUG) Log.e(TAG, e.toString(), e);
// ignore parser errors
}
}
return new Date(0);
}
class Handler extends DefaultHandler {
StringBuilder cdata = new StringBuilder();
TrackPointItem trackPointItem;
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
cdata.append(ch, start, length);
super.characters(ch, start, length);
}
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
cdata.delete(0, cdata.length());
if (localName.equalsIgnoreCase(TRKPT)) {
if (trackPointItem != null) Log.e(TAG, "trkpt in trkpt");
trackPointItem = new TrackPointItem(
Double.parseDouble(attributes.getValue(LAT)), // latitude
Double.parseDouble(attributes.getValue(LON)) // longitude
);
} else if (localName.equalsIgnoreCase("gpx")) {
if (DEBUG)
Log.d(TAG, "start gpx version=" + attributes.getValue("version") + ", creator=" + attributes.getValue("creator"));
trackItem = new TrackItem();
} else if (localName.equalsIgnoreCase("rte")) {
if (DEBUG) Log.d(TAG, "This is probably a route file");
} else if (localName.equalsIgnoreCase(WPT)) {
if (DEBUG) Log.d(TAG, "This is probably a way point file");
}
super.startElement(uri, localName, name, attributes);
}
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
if (trackItem != null) { // we need a TRK tag around
if (localName.equalsIgnoreCase(NAME)) {
trackItem.setName(cdata.toString().trim());
} else if (localName.equalsIgnoreCase(CMT)) {
trackItem.setComment(cdata.toString().trim());
} else if (localName.equalsIgnoreCase(DESC)) {
trackItem.setDescription(cdata.toString().trim());
}
}
if (trackPointItem != null) { // we need a TRKPT tag around
if (localName.equalsIgnoreCase(ELE)) {
trackPointItem.setAltitude((int) Double.parseDouble(cdata.toString().trim()));
} else if (localName.equalsIgnoreCase(TIME)) {
trackPointItem.setTimestamp(parseDate(cdata.toString().trim()));
} else if (localName.equalsIgnoreCase("sym")) {
trackPointItem.setAttribute(Integer.parseInt(cdata.toString().trim()));
} else if (localName.equalsIgnoreCase(TRKPT)) {
try {
trackItem.add(db, trackPointItem);
}
catch (Exception e) {
throw new SAXException(e.toString(), e);
}
trackPointItem = null;
}
}
super.endElement(uri, localName, name);
}
}
}