mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-07 20:03:06 +02:00
Merging GPX import fix
This commit is contained in:
@@ -20,6 +20,7 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
@@ -28,11 +29,8 @@ import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SimpleTimeZone;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
@@ -54,22 +52,6 @@ import org.xml.sax.helpers.DefaultHandler;
|
||||
*/
|
||||
public class GpxImporter extends DefaultHandler {
|
||||
|
||||
/*
|
||||
* Different date formats used in GPX files
|
||||
*/
|
||||
static final SimpleDateFormat DATE_FORMAT1 = new SimpleDateFormat(
|
||||
"yyyy-MM-dd'T'HH:mm:ssZ");
|
||||
static final SimpleDateFormat DATE_FORMAT2 = new SimpleDateFormat(
|
||||
"yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
static final SimpleDateFormat DATE_FORMAT3 = new SimpleDateFormat(
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSZ");
|
||||
static final SimpleTimeZone UTC_TIMEZONE = new SimpleTimeZone(0, "UTC");
|
||||
static {
|
||||
DATE_FORMAT1.setTimeZone(UTC_TIMEZONE);
|
||||
DATE_FORMAT2.setTimeZone(UTC_TIMEZONE);
|
||||
DATE_FORMAT3.setTimeZone(UTC_TIMEZONE);
|
||||
}
|
||||
|
||||
/*
|
||||
* GPX-XML tag names and attributes.
|
||||
*/
|
||||
@@ -211,7 +193,7 @@ public class GpxImporter extends DefaultHandler {
|
||||
this.providerUtils = providerUtils;
|
||||
tracksWritten = new ArrayList<Long>();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
String newContent = new String(ch, start, length);
|
||||
@@ -453,7 +435,16 @@ public class GpxImporter extends DefaultHandler {
|
||||
private void onTimeElementEnd() throws SAXException {
|
||||
if (location == null) { return; }
|
||||
|
||||
long time = parseTimeForAllFormats(content);
|
||||
// Parse the time
|
||||
long time;
|
||||
try {
|
||||
time = StringUtils.parseXmlDateTime(content.trim());
|
||||
} catch (IllegalArgumentException e) {
|
||||
String msg = createErrorMessage("Unable to parse time: " + content);
|
||||
throw new SAXException(msg, e);
|
||||
}
|
||||
|
||||
// Calculate derived attributes from previous point
|
||||
if (lastSegmentLocation != null) {
|
||||
long timeDifference = time - lastSegmentLocation.getTime();
|
||||
|
||||
@@ -473,7 +464,9 @@ public class GpxImporter extends DefaultHandler {
|
||||
location.setBearing(lastSegmentLocation.bearingTo(location));
|
||||
}
|
||||
|
||||
// Fill in the time
|
||||
location.setTime(time);
|
||||
|
||||
// initialize start time with time of first track point
|
||||
if (statsBuilder == null) {
|
||||
statsBuilder = new TripStatisticsBuilder();
|
||||
@@ -511,44 +504,6 @@ public class GpxImporter extends DefaultHandler {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse time trying different formats used in GPX files.
|
||||
*
|
||||
* @param timeContents string with time information
|
||||
* @return time as long
|
||||
* @throws SAXException on time parsing errors
|
||||
*/
|
||||
private long parseTimeForAllFormats(String timeContents) throws SAXException {
|
||||
long time;
|
||||
timeContents = timeContents.trim();
|
||||
|
||||
// 1st try with time zone at end a la "+0000"
|
||||
time = parseTime(timeContents, DATE_FORMAT1);
|
||||
if (time > -1) { return time; }
|
||||
|
||||
// if that fails, try with a literal "Z" at the end
|
||||
// (this is not according to xml standard, but some gpx files are like
|
||||
// that):
|
||||
time = parseTime(timeContents, DATE_FORMAT2);
|
||||
if (time > -1) { return time; }
|
||||
|
||||
// some gpx timestamps have 3 additional digits at the end.
|
||||
time = parseTime(timeContents, DATE_FORMAT3);
|
||||
if (time > -1) { return time; }
|
||||
|
||||
// everything failed - abort the import
|
||||
String msg = createErrorMessage("Invalid time format: " + timeContents);
|
||||
throw new SAXException(msg);
|
||||
}
|
||||
|
||||
private long parseTime(String timeContents, SimpleDateFormat format) {
|
||||
try {
|
||||
return format.parse(timeContents).getTime();
|
||||
} catch (ParseException ex) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the last point inserted into the database.
|
||||
*/
|
||||
|
||||
@@ -16,17 +16,22 @@
|
||||
package com.google.android.apps.mytracks.util;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.apps.mytracks.content.DescriptionGenerator;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.DescriptionGenerator;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.SimpleTimeZone;
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Various string manipulation methods.
|
||||
@@ -76,6 +81,75 @@ public class StringUtils implements DescriptionGenerator {
|
||||
return "<![CDATA[" + escaped + "]]>";
|
||||
}
|
||||
|
||||
private static final SimpleDateFormat BASE_XML_DATE_FORMAT =
|
||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
static {
|
||||
BASE_XML_DATE_FORMAT.setTimeZone(new SimpleTimeZone(0, "UTC"));
|
||||
}
|
||||
private static final Pattern XML_DATE_EXTRAS_PATTERN =
|
||||
Pattern.compile("^(\\.\\d+)?(?:Z|([+-])(\\d{2}):(\\d{2}))?$");
|
||||
|
||||
/**
|
||||
* Parses an XML dateTime element as defined by
|
||||
* {@link http://www.w3.org/TR/xmlschema-2/#dateTime}.
|
||||
*/
|
||||
public static long parseXmlDateTime(String xmlTime) {
|
||||
// Parse the base date (fixed format)
|
||||
ParsePosition position = new ParsePosition(0);
|
||||
Date date = BASE_XML_DATE_FORMAT.parse(xmlTime, position);
|
||||
if (date == null) {
|
||||
throw new IllegalArgumentException("Invalid XML dateTime value: '" + xmlTime
|
||||
+ "' (at position " + position.getErrorIndex() + ")");
|
||||
}
|
||||
|
||||
// Parse the extras
|
||||
Matcher matcher =
|
||||
XML_DATE_EXTRAS_PATTERN.matcher(xmlTime.substring(position.getIndex()));
|
||||
if (!matcher.matches()) {
|
||||
// This will match even an empty string as all groups are optional,
|
||||
// so a non-match means some other garbage was there
|
||||
throw new IllegalArgumentException("Invalid XML dateTime value: " + xmlTime);
|
||||
}
|
||||
|
||||
long time = date.getTime();
|
||||
|
||||
// Account for fractional seconds
|
||||
String fractional = matcher.group(1);
|
||||
if (fractional != null) {
|
||||
// Regex ensures fractional part is in (0,1(
|
||||
float fractionalSeconds = Float.parseFloat(fractional);
|
||||
long fractionalMillis = (long) (fractionalSeconds * 1000.0f);
|
||||
time += fractionalMillis;
|
||||
}
|
||||
|
||||
// Account for timezones
|
||||
String sign = matcher.group(2);
|
||||
String offsetHoursStr = matcher.group(3);
|
||||
String offsetMinsStr = matcher.group(4);
|
||||
if (sign != null && offsetHoursStr != null && offsetMinsStr != null) {
|
||||
// Regex ensures sign is + or -
|
||||
boolean plusSign = sign.equals("+");
|
||||
int offsetHours = Integer.parseInt(offsetHoursStr);
|
||||
int offsetMins = Integer.parseInt(offsetMinsStr);
|
||||
|
||||
// Regex ensures values are >= 0
|
||||
if (offsetHours > 14 || offsetMins > 59) {
|
||||
throw new IllegalArgumentException("Bad timezone in " + xmlTime);
|
||||
}
|
||||
|
||||
long totalOffsetMillis = (offsetMins + offsetHours * 60L) * 60000L;
|
||||
|
||||
// Make time go back to UTC
|
||||
if (plusSign) {
|
||||
time -= totalOffsetMillis;
|
||||
} else {
|
||||
time += totalOffsetMillis;
|
||||
}
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a number of milliseconds as a string.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user