Skip to content

Commit

Permalink
GPX import/export: speed via TrackPointExtension/v2.
Browse files Browse the repository at this point in the history
Fixed data type for heartrate/speed (no decimals) allowed.
Fixes #335.
Fixes #181.

GPXTrackWriter: set Trackpointv2 header correctly and export time in correct sequence.
  • Loading branch information
dennisguse committed Jul 24, 2020
1 parent c92371b commit 065f42f
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ public class SearchEngineTest {

private final Context context = ApplicationProvider.getApplicationContext();


@Before
public void setUp() {
providerUtils = new ContentProviderUtils(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ public void kml_with_trackdetail() {
// 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
Expand Down Expand Up @@ -138,8 +137,7 @@ public void kml_with_trackdetail_and_sensordata() {
// 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
Expand Down Expand Up @@ -201,8 +199,7 @@ public void gpx() {
// 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
Expand All @@ -229,7 +226,7 @@ public void gpx() {
assertWaypoints();

// 3. trackpoints
assertTrackpoints(false, false, false);
assertTrackpoints(false, true, true);
}

private void assertWaypoints() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@
*
* @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);

Expand All @@ -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);
}

Expand Down Expand Up @@ -90,12 +93,12 @@ public void writeHeader(Track[] tracks) {
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>");
Expand Down Expand Up @@ -180,10 +183,20 @@ public void writeCloseSegment() {
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>");
}

printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(trackPoint.getTime()) + "</time>");

if (trackPoint.hasHeartRate() || trackPoint.hasCyclingCadence()) {
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>");
}
Expand All @@ -195,9 +208,6 @@ public void writeTrackPoint(TrackPoint trackPoint) {
printWriter.println("</gpxtpx:TrackPointExtension></extensions>");
}

printWriter.println(
"<time>" + StringUtils.formatDateTimeIso8601(trackPoint.getTime()) + "</time>");
printWriter.println("<speed>" + trackPoint.getSpeed() + "</speed>");
printWriter.println("</trkpt>");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -248,14 +251,12 @@ protected void onTrackEnd() {
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);
Expand Down Expand Up @@ -416,45 +417,56 @@ private TrackPoint createTrackPoint() throws SAXException {
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

/**
* Imports a GPX file.
* Uses https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd
*
* @author Jimmy Shih
*/
Expand All @@ -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";
Expand All @@ -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.
*
Expand Down Expand Up @@ -117,23 +120,28 @@ public void endElement(String uri, String localName, String tag) throws SAXExcep
time = content.trim();
}
break;
case TAG_SPEED:
case TAG_ELEVATION:
if (content != null) {
speed = content.trim();
altitude = content.trim();
}
case TAG_HEARTRATE:
break;
case TAG_COMMENT:
if (content != null) {
heartrate = content.trim();
waypointType = content.trim();
}
break;
case TAG_ELEVATION:
case TAG_EXTENSION_SPEED:
if (content != null) {
altitude = content.trim();
speed = content.trim();
}
case TAG_EXTENSION_HEARTRATE:
if (content != null) {
heartrate = content.trim();
}
break;
case TAG_COMMENT:
case TAG_EXTENSION_CADENCE:
if (content != null) {
waypointType = content.trim();
cadence = content.trim();
}
break;
}
Expand Down

0 comments on commit 065f42f

Please sign in to comment.