forked from upstream-mirrors/OpenTracks
GPX import/export: speed via TrackPointExtension/v2.
Fixed data type for heartrate/speed (no decimals) allowed. Fixes #335. Fixes #181. GPXTrackWriter: set Trackpointv2 header correctly and export time in correct sequence.
This commit is contained in:
@@ -58,7 +58,6 @@ public class SearchEngineTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
providerUtils = new ContentProviderUtils(context);
|
||||
|
||||
+4
-7
@@ -103,8 +103,7 @@ public class ExportImportTest {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackFileFormat trackFileFormat = TrackFileFormat.KML_WITH_TRACKDETAIL;
|
||||
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track});
|
||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL.newTrackExporter(context, new Track[]{track});
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
@@ -138,8 +137,7 @@ public class ExportImportTest {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackFileFormat trackFileFormat = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA;
|
||||
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track});
|
||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.newTrackExporter(context, new Track[]{track});
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
@@ -201,8 +199,7 @@ public class ExportImportTest {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackFileFormat trackFileFormat = TrackFileFormat.GPX;
|
||||
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track});
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.newTrackExporter(context, new Track[]{track});
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
@@ -229,7 +226,7 @@ public class ExportImportTest {
|
||||
assertWaypoints();
|
||||
|
||||
// 3. trackpoints
|
||||
assertTrackpoints(false, false, false);
|
||||
assertTrackpoints(false, true, true);
|
||||
}
|
||||
|
||||
private void assertWaypoints() {
|
||||
|
||||
@@ -33,11 +33,11 @@ import de.dennisguse.opentracks.util.StringUtils;
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
//TODO Can we export SensorData in GPX?
|
||||
public class GpxTrackWriter implements TrackWriter {
|
||||
|
||||
private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat COORDINATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat SPEED_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat HEARTRATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat CADENCE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
|
||||
@@ -53,10 +53,13 @@ public class GpxTrackWriter implements TrackWriter {
|
||||
COORDINATE_FORMAT.setMaximumIntegerDigits(3);
|
||||
COORDINATE_FORMAT.setGroupingUsed(false);
|
||||
|
||||
HEARTRATE_FORMAT.setMaximumFractionDigits(1);
|
||||
SPEED_FORMAT.setMaximumFractionDigits(2);
|
||||
SPEED_FORMAT.setGroupingUsed(false);
|
||||
|
||||
HEARTRATE_FORMAT.setMaximumFractionDigits(0);
|
||||
HEARTRATE_FORMAT.setGroupingUsed(false);
|
||||
|
||||
CADENCE_FORMAT.setMaximumFractionDigits(1);
|
||||
CADENCE_FORMAT.setMaximumFractionDigits(0);
|
||||
CADENCE_FORMAT.setGroupingUsed(false);
|
||||
}
|
||||
|
||||
@@ -90,12 +93,12 @@ public class GpxTrackWriter implements TrackWriter {
|
||||
printWriter.println("xmlns=\"http://www.topografix.com/GPX/1/1\"");
|
||||
printWriter.println("xmlns:topografix=\"http://www.topografix.com/GPX/Private/TopoGrafix/0/1\"");
|
||||
printWriter.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
|
||||
printWriter.println("xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1"
|
||||
+ " http://www.topografix.com/GPX/1/1/gpx.xsd"
|
||||
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1"
|
||||
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd\"");
|
||||
printWriter.println("xmlns:gpxtpx=\"http://www.garmin.com/xmlschemes/TrackPointExtension/v1\">");
|
||||
printWriter.println("xmlns:gpxtpx=\"http://www.garmin.com/xmlschemes/TrackPointExtension/v2\"");
|
||||
printWriter.println("xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
|
||||
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1 http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd"
|
||||
+ " http://www.garmin.com/xmlschemas/TrackPointExtension/v2 https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd\">");
|
||||
printWriter.println("<metadata>");
|
||||
|
||||
Track track = tracks[0];
|
||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
||||
@@ -180,10 +183,20 @@ public class GpxTrackWriter implements TrackWriter {
|
||||
public void writeTrackPoint(TrackPoint trackPoint) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<trkpt " + formatLocation(trackPoint.getLocation()) + ">");
|
||||
if (trackPoint.hasAltitude()) { printWriter.println("<ele>" + ELEVATION_FORMAT.format(trackPoint.getAltitude()) + "</ele>"); }
|
||||
if (trackPoint.hasAltitude()) {
|
||||
printWriter.println("<ele>" + ELEVATION_FORMAT.format(trackPoint.getAltitude()) + "</ele>");
|
||||
}
|
||||
|
||||
if (trackPoint.hasHeartRate() || trackPoint.hasCyclingCadence()) {
|
||||
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(trackPoint.getTime()) + "</time>");
|
||||
|
||||
if (trackPoint.hasSpeed() || trackPoint.hasHeartRate() || trackPoint.hasCyclingCadence()) {
|
||||
printWriter.println("<extensions><gpxtpx:TrackPointExtension>");
|
||||
|
||||
|
||||
if (trackPoint.hasSpeed()) {
|
||||
printWriter.println("<gpxtpx:speed>" + SPEED_FORMAT.format(trackPoint.getSpeed()) + "</gpxtpx:speed>");
|
||||
}
|
||||
|
||||
if (trackPoint.hasHeartRate()) {
|
||||
printWriter.println("<gpxtpx:hr>" + HEARTRATE_FORMAT.format(trackPoint.getHeartRate_bpm()) + "</gpxtpx:hr>");
|
||||
}
|
||||
@@ -195,9 +208,6 @@ public class GpxTrackWriter implements TrackWriter {
|
||||
printWriter.println("</gpxtpx:TrackPointExtension></extensions>");
|
||||
}
|
||||
|
||||
printWriter.println(
|
||||
"<time>" + StringUtils.formatDateTimeIso8601(trackPoint.getTime()) + "</time>");
|
||||
printWriter.println("<speed>" + trackPoint.getSpeed() + "</speed>");
|
||||
printWriter.println("</trkpt>");
|
||||
}
|
||||
}
|
||||
|
||||
+35
-23
@@ -83,6 +83,9 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
protected String altitude;
|
||||
protected String time;
|
||||
protected String speed;
|
||||
protected String heartrate;
|
||||
protected String cadence;
|
||||
protected String power;
|
||||
protected String waypointType;
|
||||
protected String photoUrl;
|
||||
protected String uuid;
|
||||
@@ -248,14 +251,12 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
trackData.track.setName(name);
|
||||
}
|
||||
|
||||
UUID uuidParsed;
|
||||
try {
|
||||
uuidParsed = UUID.fromString(uuid);
|
||||
trackData.track.setUuid(UUID.fromString(uuid));
|
||||
} catch (IllegalArgumentException | NullPointerException e) {
|
||||
Log.w(TAG, "could not parse Track UUID, generating a new one.");
|
||||
uuidParsed = UUID.randomUUID();
|
||||
trackData.track.setUuid(UUID.randomUUID());
|
||||
}
|
||||
trackData.track.setUuid(uuidParsed);
|
||||
|
||||
if (description != null) {
|
||||
trackData.track.setDescription(description);
|
||||
@@ -416,45 +417,56 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
if (latitude == null || longitude == null) {
|
||||
return null;
|
||||
}
|
||||
double latitudeValue;
|
||||
double longitudeValue;
|
||||
|
||||
TrackPoint trackPoint = new TrackPoint();
|
||||
|
||||
try {
|
||||
latitudeValue = Double.parseDouble(latitude);
|
||||
longitudeValue = Double.parseDouble(longitude);
|
||||
trackPoint.setLatitude(Double.parseDouble(latitude));
|
||||
trackPoint.setLongitude(Double.parseDouble(longitude));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse latitude longitude: %s %s", latitude, longitude)), e);
|
||||
}
|
||||
Double altitudeValue = null;
|
||||
if (altitude != null) {
|
||||
try {
|
||||
altitudeValue = Double.parseDouble(altitude);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse altitude: %s", altitude)), e);
|
||||
}
|
||||
}
|
||||
|
||||
long timeValue;
|
||||
if (time == null) {
|
||||
timeValue = trackData.importTime;
|
||||
trackPoint.setTime(trackData.importTime);
|
||||
} else {
|
||||
try {
|
||||
timeValue = StringUtils.parseTime(time);
|
||||
trackPoint.setTime(StringUtils.parseTime(time));
|
||||
} catch (Exception e) {
|
||||
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse time: %s", time)), e);
|
||||
}
|
||||
}
|
||||
|
||||
TrackPoint trackPoint = new TrackPoint(latitudeValue, longitudeValue, altitudeValue, timeValue);
|
||||
if (altitude != null) {
|
||||
try {
|
||||
trackPoint.setAltitude(Double.parseDouble(altitude));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse altitude: %s", altitude)), e);
|
||||
}
|
||||
}
|
||||
|
||||
float speedValue;
|
||||
if (speed != null) {
|
||||
try {
|
||||
speedValue = Float.parseFloat(speed);
|
||||
trackPoint.setSpeed(speedValue);
|
||||
trackPoint.setSpeed(Float.parseFloat(speed));
|
||||
} catch (Exception e) {
|
||||
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse speed: %s", speed)), e);
|
||||
}
|
||||
}
|
||||
if (heartrate != null) {
|
||||
try {
|
||||
trackPoint.setHeartRate_bpm(Float.parseFloat(heartrate));
|
||||
} catch (Exception e) {
|
||||
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse heart rate: %s", heartrate)), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (cadence != null) {
|
||||
try {
|
||||
trackPoint.setCyclingCadence_rpm(Float.parseFloat(cadence));
|
||||
} catch (Exception e) {
|
||||
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse cadence: %s", cadence)), e);
|
||||
}
|
||||
}
|
||||
|
||||
return trackPoint;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
|
||||
/**
|
||||
* Imports a GPX file.
|
||||
* Uses https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@@ -39,8 +40,6 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
private static final String TAG_GPX = "gpx";
|
||||
private static final String TAG_NAME = "name";
|
||||
private static final String TAG_TIME = "time";
|
||||
private static final String TAG_SPEED = "speed";
|
||||
private static final String TAG_HEARTRATE = "gpx:hr";
|
||||
private static final String TAG_TRACK = "trk";
|
||||
private static final String TAG_TRACK_POINT = "trkpt";
|
||||
private static final String TAG_TRACK_SEGMENT = "trkseg";
|
||||
@@ -50,6 +49,10 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
private static final String ATTRIBUTE_LAT = "lat";
|
||||
private static final String ATTRIBUTE_LON = "lon";
|
||||
|
||||
private static final String TAG_EXTENSION_SPEED = "gpxtpx:speed";
|
||||
private static final String TAG_EXTENSION_HEARTRATE = "gpxtpx:hr";
|
||||
private static final String TAG_EXTENSION_CADENCE = "gpxtpx:cad";
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -117,15 +120,6 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
time = content.trim();
|
||||
}
|
||||
break;
|
||||
case TAG_SPEED:
|
||||
if (content != null) {
|
||||
speed = content.trim();
|
||||
}
|
||||
case TAG_HEARTRATE:
|
||||
if (content != null) {
|
||||
heartrate = content.trim();
|
||||
}
|
||||
break;
|
||||
case TAG_ELEVATION:
|
||||
if (content != null) {
|
||||
altitude = content.trim();
|
||||
@@ -136,6 +130,20 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
waypointType = content.trim();
|
||||
}
|
||||
break;
|
||||
case TAG_EXTENSION_SPEED:
|
||||
if (content != null) {
|
||||
speed = content.trim();
|
||||
}
|
||||
case TAG_EXTENSION_HEARTRATE:
|
||||
if (content != null) {
|
||||
heartrate = content.trim();
|
||||
}
|
||||
break;
|
||||
case TAG_EXTENSION_CADENCE:
|
||||
if (content != null) {
|
||||
cadence = content.trim();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset element content
|
||||
|
||||
Reference in New Issue
Block a user