diff --git a/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java b/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java
index c98547df5..6d828abd0 100644
--- a/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java
+++ b/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java
@@ -65,6 +65,7 @@ public class TestDataUtil {
trackPoint.setAccuracy((float) i / 100.0f);
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
trackPoint.setTime(i + 1);
+ trackPoint.setSpeed(5f + (i / 10));
trackPoint.setHeartRate_bpm(100f + i);
trackPoint.setCyclingCadence_rpm(200f + i);
diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java
index cf6945386..cf3bb591f 100644
--- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java
+++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java
@@ -36,7 +36,6 @@ import static org.junit.Assert.assertNotNull;
* Export a track to {@link TrackFileFormat} and verify that the import is identical.
*
* TODO: test ignores {@link TrackStatistics} for now.
- * TODO: enable verify speed.
*/
@RunWith(JUnit4.class)
public class ExportImportTest {
@@ -130,7 +129,7 @@ public class ExportImportTest {
assertWaypoints();
// 3. trackpoints
- assertTrackpoints(false, false, false, false);
+ assertTrackpoints(false, false, false);
}
@LargeTest
@@ -165,7 +164,7 @@ public class ExportImportTest {
assertWaypoints();
// 3. trackpoints
- assertTrackpoints(false, true, true, true);
+ assertTrackpoints(true, true, true);
}
@LargeTest
@@ -230,8 +229,7 @@ public class ExportImportTest {
assertWaypoints();
// 3. trackpoints
- //TODO Verify speed
- assertTrackpoints(false, false, false, false);
+ assertTrackpoints(false, false, false);
}
private void assertWaypoints() {
@@ -253,7 +251,7 @@ public class ExportImportTest {
}
}
- private void assertTrackpoints(boolean verifySpeed, boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence) {
+ private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence) {
List importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
assertEquals(trackPoints.size(), importedTrackPoints.size());
@@ -269,9 +267,7 @@ public class ExportImportTest {
assertEquals(trackPoint.getLatitude(), importedTrackPoint.getLatitude(), 0.001);
assertEquals(trackPoint.getLongitude(), importedTrackPoint.getLongitude(), 0.001);
assertEquals(trackPoint.getAltitude(), importedTrackPoint.getAltitude(), 0.001);
- if (verifySpeed) {
- assertEquals(trackPoint.getSpeed(), importedTrackPoint.getSpeed(), 0.01);
- }
+ assertEquals(trackPoint.getSpeed(), importedTrackPoint.getSpeed(), 0.001);
if (verifyHeartrate) {
assertEquals(trackPoint.getHeartRate_bpm(), importedTrackPoint.getHeartRate_bpm(), 0.01);
}
diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/GpxTrackWriter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/GpxTrackWriter.java
index a1d0672fd..b5e84d4d5 100644
--- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/GpxTrackWriter.java
+++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/GpxTrackWriter.java
@@ -176,6 +176,7 @@ public class GpxTrackWriter implements TrackWriter {
}
printWriter.println(
"");
+ printWriter.println("" + trackPoint.getSpeed() + "");
printWriter.println("");
}
}
diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java
index 03704f62b..883fd2e29 100644
--- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java
+++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java
@@ -46,9 +46,10 @@ public class KmlTrackWriter implements TrackWriter {
private static final String TRACK_STYLE = "track";
private static final String SCHEMA_ID = "schema";
- public static final String SENSOR_TYPE_CADENCE = "cadence";
- public static final String SENSOR_TYPE_HEART_RATE = "heart_rate";
- public static final String SENSOR_TYPE_POWER = "power";
+ public static final String EXTENDED_DATA_TYPE_SPEED = "speed";
+ public static final String EXTENDED_DATA_TYPE_CADENCE = "cadence";
+ public static final String EXTENDED_DATA_TYPE_HEART_RATE = "heart_rate";
+ public static final String EXTENDED_DATA_TYPE_POWER = "power";
private static final String WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
private static final String START_ICON = "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png";
@@ -64,6 +65,7 @@ public class KmlTrackWriter implements TrackWriter {
private final ContentProviderUtils contentProviderUtils;
private PrintWriter printWriter;
+ private final List speedList = new ArrayList<>();
private final List powerList = new ArrayList<>();
private final List cadenceList = new ArrayList<>();
private final List heartRateList = new ArrayList<>();
@@ -123,10 +125,12 @@ public class KmlTrackWriter implements TrackWriter {
writePlacemarkerStyle(WAYPOINT_STYLE, WAYPOINT_ICON, 20, 2);
printWriter.println("");
+ writeSimpleArrayStyle(EXTENDED_DATA_TYPE_SPEED, context.getString(R.string.description_speed_ms));
+
if (exportSensorData) {
- writeSensorStyle(SENSOR_TYPE_POWER, context.getString(R.string.description_sensor_power));
- writeSensorStyle(SENSOR_TYPE_CADENCE, context.getString(R.string.description_sensor_cadence));
- writeSensorStyle(SENSOR_TYPE_HEART_RATE, context.getString(R.string.description_sensor_heart_rate));
+ writeSimpleArrayStyle(EXTENDED_DATA_TYPE_POWER, context.getString(R.string.description_sensor_power));
+ writeSimpleArrayStyle(EXTENDED_DATA_TYPE_CADENCE, context.getString(R.string.description_sensor_cadence));
+ writeSimpleArrayStyle(EXTENDED_DATA_TYPE_HEART_RATE, context.getString(R.string.description_sensor_heart_rate));
}
printWriter.println("");
}
@@ -229,6 +233,7 @@ public class KmlTrackWriter implements TrackWriter {
public void writeOpenSegment() {
if (printWriter != null) {
printWriter.println("");
+ speedList.clear();
powerList.clear();
cadenceList.clear();
heartRateList.clear();
@@ -240,15 +245,18 @@ public class KmlTrackWriter implements TrackWriter {
if (printWriter != null) {
printWriter.println("");
printWriter.println("");
+ if (speedList.size() > 0) {
+ writeSimpleArrayData(speedList, EXTENDED_DATA_TYPE_SPEED);
+ }
if (exportSensorData) {
if (powerList.size() > 0) {
- writeSensorData(powerList, SENSOR_TYPE_POWER);
+ writeSimpleArrayData(powerList, EXTENDED_DATA_TYPE_POWER);
}
if (cadenceList.size() > 0) {
- writeSensorData(cadenceList, SENSOR_TYPE_CADENCE);
+ writeSimpleArrayData(cadenceList, EXTENDED_DATA_TYPE_CADENCE);
}
if (heartRateList.size() > 0) {
- writeSensorData(heartRateList, SENSOR_TYPE_HEART_RATE);
+ writeSimpleArrayData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE);
}
}
printWriter.println("");
@@ -266,6 +274,10 @@ public class KmlTrackWriter implements TrackWriter {
printWriter.println("" + getCoordinates(trackPoint.getLocation(), " ") + "");
+ if (trackPoint.hasSpeed()) {
+ speedList.add(trackPoint.getSpeed());
+ }
+
if (exportSensorData) {
if (trackPoint.hasHeartRate()) {
heartRateList.add(trackPoint.getHeartRate_bpm());
@@ -281,12 +293,12 @@ public class KmlTrackWriter implements TrackWriter {
}
/**
- * Writes the sensor data.
+ * Writes the simple array data.
*
- * @param list a list of sensor data
- * @param name the name of the sensor data
+ * @param list a list of simple array data
+ * @param name the name of the simple array data
*/
- private void writeSensorData(List list, String name) {
+ private void writeSimpleArrayData(List list, String name) {
printWriter.println("");
for (int i = 0; i < list.size(); i++) {
printWriter.println("" + list.get(i) + "");
@@ -446,14 +458,14 @@ public class KmlTrackWriter implements TrackWriter {
}
/**
- * Writes a sensor style.
+ * Writes a simple array style.
*
- * @param name the name of the sesnor
- * @param sensorType the sensor display name
+ * @param name the name of the simple array.
+ * @param extendedDataType the extended data display name
*/
- private void writeSensorStyle(String name, String sensorType) {
+ private void writeSimpleArrayStyle(String name, String extendedDataType) {
printWriter.println("");
- printWriter.println("" + StringUtils.formatCData(sensorType) + "");
+ printWriter.println("" + StringUtils.formatCData(extendedDataType) + "");
printWriter.println("");
}
}
diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java
index 30e7efbd5..e07905d3c 100644
--- a/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java
+++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java
@@ -80,6 +80,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
protected String longitude;
protected String altitude;
protected String time;
+ protected String speed;
protected String waypointType;
protected String photoUrl;
@@ -319,20 +320,22 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
// Calculate derived attributes from the previous point
if (trackData.lastLocationInCurrentSegment != null && trackData.lastLocationInCurrentSegment.getTime() != 0) {
- long timeDifference = trackPoint.getTime() - trackData.lastLocationInCurrentSegment.getTime();
+ if (!trackPoint.hasSpeed()) {
+ long timeDifference = trackPoint.getTime() - trackData.lastLocationInCurrentSegment.getTime();
- // Check for negative time change
- if (timeDifference <= 0) {
- Log.w(TAG, "Time difference not positive.");
- } else {
+ // Check for negative time change
+ if (timeDifference <= 0) {
+ Log.w(TAG, "Time difference not positive.");
+ } else {
- /*
- * We don't have a speed and bearing in GPX, make something up from the last two points.
- * GPS points tend to have some inherent imprecision, speed and bearing will likely be off, so the statistics for things like max speed will also be off.
- */
- double duration = timeDifference * UnitConversions.MS_TO_S;
- double speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / duration;
- trackPoint.setSpeed((float) speed);
+ /*
+ * We don't have a speed and bearing in GPX, make something up from the last two points.
+ * GPS points tend to have some inherent imprecision, speed and bearing will likely be off, so the statistics for things like max speed will also be off.
+ */
+ double duration = timeDifference * UnitConversions.MS_TO_S;
+ double speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / duration;
+ trackPoint.setSpeed((float) speed);
+ }
}
trackPoint.setBearing(trackData.lastLocationInCurrentSegment.bearingTo(trackPoint));
}
@@ -427,7 +430,20 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse time: %s", time)), e);
}
}
- return new TrackPoint(latitudeValue, longitudeValue, altitudeValue, timeValue);
+
+ TrackPoint trackPoint = new TrackPoint(latitudeValue, longitudeValue, altitudeValue, timeValue);
+
+ float speedValue;
+ if (speed != null) {
+ try {
+ speedValue = Float.valueOf(speed);
+ trackPoint.setSpeed(speedValue);
+ } catch (Exception e) {
+ throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse speed: %s", speed)), e);
+ }
+ }
+
+ return trackPoint;
}
/**
diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporter.java
index 5497e11f3..897d445a7 100644
--- a/src/main/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporter.java
+++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporter.java
@@ -39,6 +39,7 @@ 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_TRACK = "trk";
private static final String TAG_TRACK_POINT = "trkpt";
private static final String TAG_TRACK_SEGMENT = "trkseg";
@@ -115,6 +116,11 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
time = content.trim();
}
break;
+ case TAG_SPEED:
+ if (content != null) {
+ speed = content.trim();
+ }
+ break;
case TAG_ELEVATION:
if (content != null) {
altitude = content.trim();
@@ -149,6 +155,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
longitude = attributes.getValue(ATTRIBUTE_LON);
altitude = null;
time = null;
+ speed = null;
}
/**
diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java
index 32234b74b..13e9b68ad 100644
--- a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java
+++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java
@@ -61,8 +61,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
private static final String ATTRIBUTE_NAME = "name";
private boolean trackStarted = false;
- private String sensorType;
+ private String extendedDataType;
private ArrayList trackPoints;
+ private ArrayList speedList;
private ArrayList cadenceList;
private ArrayList heartRateList;
private ArrayList powerList;
@@ -102,7 +103,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
onTrackSegmentStart();
break;
case TAG_GX_SIMPLE_ARRAY_DATA:
- onSensorDataStart(attributes);
+ onExtendedDataStart(attributes);
break;
}
}
@@ -124,7 +125,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
} else if (tag.equals(TAG_GX_COORD)) {
onTrackPointEnd();
} else if (tag.equals(TAG_GX_VALUE)) {
- onSensorValueEnd();
+ onExtendedDataValueEnd();
} else if (tag.equals(TAG_NAME)) {
if (content != null) {
name = content.trim();
@@ -209,6 +210,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
protected void onTrackSegmentStart() {
super.onTrackSegmentStart();
trackPoints = new ArrayList<>();
+ speedList = new ArrayList<>();
heartRateList = new ArrayList<>();
cadenceList = new ArrayList<>();
powerList = new ArrayList<>();
@@ -222,6 +224,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
for (int i = 0; i < trackPoints.size(); i++) {
TrackPoint trackPoint = trackPoints.get(i);
+ if (i < speedList.size()) {
+ trackPoint.setSpeed(speedList.get(i));
+ }
if (i < heartRateList.size()) {
trackPoint.setHeartRate_bpm(heartRateList.get(i));
}
@@ -261,16 +266,16 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
}
/**
- * On sensor data start. gx:SimpleArrayData start tag.
+ * On extended data start. gx:SimpleArrayData start tag.
*/
- private void onSensorDataStart(Attributes attributes) {
- sensorType = attributes.getValue(ATTRIBUTE_NAME);
+ private void onExtendedDataStart(Attributes attributes) {
+ extendedDataType = attributes.getValue(ATTRIBUTE_NAME);
}
/**
- * On sensor value end. gx:value end tag.
+ * On extended data value end. gx:value end tag.
*/
- private void onSensorValueEnd() throws SAXException {
+ private void onExtendedDataValueEnd() throws SAXException {
if (content == null) {
return;
}
@@ -284,18 +289,21 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
} catch (NumberFormatException e) {
throw new SAXException(createErrorMessage("Unable to parse gx:value:" + content), e);
}
- switch (sensorType) {
- case KmlTrackWriter.SENSOR_TYPE_POWER:
+ switch (extendedDataType) {
+ case KmlTrackWriter.EXTENDED_DATA_TYPE_SPEED:
+ speedList.add(value);
+ break;
+ case KmlTrackWriter.EXTENDED_DATA_TYPE_POWER:
powerList.add(value);
break;
- case KmlTrackWriter.SENSOR_TYPE_HEART_RATE:
+ case KmlTrackWriter.EXTENDED_DATA_TYPE_HEART_RATE:
heartRateList.add(value);
break;
- case KmlTrackWriter.SENSOR_TYPE_CADENCE:
+ case KmlTrackWriter.EXTENDED_DATA_TYPE_CADENCE:
cadenceList.add(value);
break;
default:
- Log.w(TAG, "Data from sensor " + sensorType + " is not (yet) supported.");
+ Log.w(TAG, "Data from extended data " + extendedDataType + " is not (yet) supported.");
}
}
}
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index f851b69d5..4fb14ad5e 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -193,6 +193,7 @@ limitations under the License.
Pace (min/mi)
Pace (min/km)
Recorded: %1$s
+ Speed (m/s)
Cadence (rpm)
Heart rate (bpm)
Power (W)