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