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 0e3a76e63..e24bf65ee 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 @@ -38,7 +38,6 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.content.data.Distance; @@ -129,21 +128,21 @@ public class ExportImportTest { trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:02Z"), ZoneId.of("CET"))); trackId = service.startNewTrack(); - service.stopUpdateRecordingData(); Distance sensorDistance = Distance.of(10); // recording distance interval - mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f); sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:03Z"), 3, 14, 10, 15, 10, 1); service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null); // A sensor-only TrackPoint - mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f); + trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:04Z"), ZoneId.of("CET"))); mockAltitudeChange(trackPointCreator, 1); - sendSensor(trackPointCreator, Instant.parse("2020-02-02T02:02:04Z")); - mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f); - sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:05Z"), 3, 14.002, 10, 15, 10, 0); + + trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:05Z"), ZoneId.of("CET"))); + mockBLESensorData(trackPointCreator, 5f, Distance.of(2), 66f, 3f, 50f); // Distance will be added to next TrackPoint + + sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:05Z"), 3, 14.001, 10, 15, 10, 0); service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null); trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:06Z"), ZoneId.of("CET"))); @@ -152,16 +151,11 @@ public class ExportImportTest { trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:20Z"), ZoneId.of("CET"))); service.resumeCurrentTrack(); - service.stopUpdateRecordingData(); - trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:21Z"), ZoneId.of("CET"))); - mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f); - sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:21Z"), 3, 14.003, 10, 15, 10, 0); + sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:21Z"), 3, 14.002, 10, 15, 10, 0); - mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f); sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:22Z"), 3, 16, 10, 15, 10, 0); - mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f); sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:23Z"), 3, 16.001, 10, 15, 10, 0); trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:24Z"), ZoneId.of("CET"))); @@ -213,7 +207,8 @@ public class ExportImportTest { // 2. trackpoints TrackPointAssert a = new TrackPointAssert() .noAccuracy(); - a.assertEquals(trackPoints, TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId)); + List actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId); + a.assertEquals(trackPoints, actual); // 3. trackstatistics TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics(); @@ -226,24 +221,24 @@ public class ExportImportTest { TrackStatistics originalTrackStatistics = track.getTrackStatistics(); assertEquals(originalTrackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime()); - assertEquals(Duration.ofSeconds(8), importedTrackStatistics.getTotalTime()); + assertEquals(Duration.ofSeconds(7), importedTrackStatistics.getTotalTime()); assertEquals(originalTrackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime()); - assertEquals(Duration.ofSeconds(4), importedTrackStatistics.getMovingTime()); + assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getMovingTime()); // Distance assertEquals(originalTrackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance()); - assertEquals(Distance.of(60), importedTrackStatistics.getTotalDistance()); + assertEquals(123.16, importedTrackStatistics.getTotalDistance().toM(), 0.01); // Speed assertEquals(originalTrackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed()); - assertEquals(Speed.of(15), importedTrackStatistics.getMaxSpeed()); + assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01); assertEquals(originalTrackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed()); - assertEquals(Speed.of(7.5), importedTrackStatistics.getAverageSpeed()); + assertEquals(17.59, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01); assertEquals(originalTrackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed()); - assertEquals(Speed.of(15), importedTrackStatistics.getAverageMovingSpeed()); + assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01); // Altitude assertEquals(originalTrackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01); @@ -322,17 +317,52 @@ public class ExportImportTest { // 2. trackpoints // The GPX exporter does not support exporting TrackPoints without lat/lng. // Therefore, the track segmentation is changes. - List trackPointsWithCoordinates = trackPoints.stream().filter(it -> TrackPoint.Type.SEGMENT_START_AUTOMATIC.equals(it.getType()) || TrackPoint.Type.TRACKPOINT.equals(it.getType())).collect(Collectors.toList()); - trackPointsWithCoordinates.get(0).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC); - trackPointsWithCoordinates.get(1).setSensorDistance(Distance.of(20)); - trackPointsWithCoordinates.get(1).setAltitudeGain(1f); - trackPointsWithCoordinates.get(1).setAltitudeLoss(1f); - trackPointsWithCoordinates.get(2).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC); TrackPointAssert a = new TrackPointAssert() .setDelta(0.05) .noAccuracy(); // speed is not fully - a.assertEquals(trackPointsWithCoordinates, TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId)); + List actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId); + a.assertEquals(List.of( + new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:03Z")) + .setLatitude(3) + .setLongitude(14) + .setAltitude(10) + .setSpeed(Speed.of(15)) + .setAltitudeLoss(1f) + .setAltitudeGain(1f), + new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:05Z")) + .setLatitude(3) + .setLongitude(14.001) + .setAltitude(10) + .setSpeed(Speed.of(5)) + .setAltitudeLoss(1f) + .setAltitudeGain(1f) + .setSensorDistance(Distance.of(12)) + .setHeartRate_bpm(66f) + .setPower(50f) + .setCadence_rpm(3f), + new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:21Z")) + .setLatitude(3) + .setLongitude(14.002) + .setAltitude(10) + .setAltitudeLoss(0f) + .setAltitudeGain(0f) + .setSpeed(Speed.of(15)), + new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:22Z")) + .setLatitude(3) + .setLongitude(16) + .setAltitude(10) + .setAltitudeLoss(0f) + .setAltitudeGain(0f) + .setSpeed(Speed.of(15)), + new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:23Z")) + .setLatitude(3) + .setLongitude(16.001) + .setAltitude(10) + .setAltitudeLoss(0f) + .setAltitudeGain(0f) + .setSpeed(Speed.of(15)) + ), actual); // 3. trackstatistics TrackStatistics trackStatistics = track.getTrackStatistics(); @@ -343,16 +373,16 @@ public class ExportImportTest { assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime()); assertEquals(Instant.parse("2020-02-02T02:02:23Z"), importedTrackStatistics.getStopTime()); - assertEquals(Duration.ofSeconds(4), importedTrackStatistics.getTotalTime()); - assertEquals(Duration.ofSeconds(4), importedTrackStatistics.getMovingTime()); + assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getTotalTime()); + assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getMovingTime()); // Distance - assertEquals(Distance.of(60), importedTrackStatistics.getTotalDistance()); + assertEquals(123.16, importedTrackStatistics.getTotalDistance().toM(), 0.01); // Speed - assertEquals(Speed.of(15), importedTrackStatistics.getMaxSpeed()); - assertEquals(Speed.of(15), importedTrackStatistics.getAverageSpeed()); - assertEquals(Speed.of(15), importedTrackStatistics.getAverageMovingSpeed()); + assertEquals(41.05, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01); + assertEquals(41.05, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01); + assertEquals(41.05, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01); // Altitude assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01); @@ -424,19 +454,21 @@ public class ExportImportTest { Mockito.when(cadence.getValue()).thenReturn(cyclingCadence); sensorDataSet.set(cadence); - SensorDataCycling.DistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCycling.DistanceSpeed.Data.class); - Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance); - Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed)); - SensorDataCycling.DistanceSpeed distanceSpeed = Mockito.mock(SensorDataCycling.DistanceSpeed.class); - Mockito.when(distanceSpeed.hasValue()).thenReturn(true); - Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData); - - sensorDataSet.set(distanceSpeed); + if (distance != null && speed != null) { + SensorDataCycling.DistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCycling.DistanceSpeed.Data.class); + Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance); + Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed)); + SensorDataCycling.DistanceSpeed distanceSpeed = Mockito.mock(SensorDataCycling.DistanceSpeed.class); + Mockito.when(distanceSpeed.hasValue()).thenReturn(true); + Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData); + sensorDataSet.set(distanceSpeed); + } sensorDataSet.fillTrackPoint(trackPoint); return sensorDataSet; } }); + trackPointCreator.onChange(null); } private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) { @@ -445,11 +477,6 @@ public class ExportImportTest { altitudeSumManager.setAltitudeLoss_m(altitudeGain); } - private void sendSensor(TrackPointCreator trackPointCreator, Instant time) { - trackPointCreator.setClock(Clock.fixed(time, ZoneId.of("CET"))); - trackPointCreator.onNewTrackPointWithoutGPS(); - } - private void sendLocation(TrackPointCreator trackPointCreator, Instant time, double latitude, double longitude, float accuracy, float speed, float altitude, float altitudeGain) { Location location = new Location("mock"); location.setLatitude(latitude); diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTest.java index 398a23ad4..66ecc163d 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTest.java @@ -62,6 +62,7 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.io.file.importer.TrackPointAssert; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; import de.dennisguse.opentracks.services.sensors.AltitudeSumManager; +import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager; import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.stats.TrackStatistics; @@ -465,8 +466,10 @@ public class TrackRecordingServiceTest { // Reset service (if some previous test failed) TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class))) .getService(); - service.endCurrentTrack(); + + service.getTrackPointCreator().setRemoteSensorManager(new BluetoothRemoteSensorManager(context, service.getTrackPointCreator())); service.getTrackPointCreator().setClock(Clock.systemUTC()); + service.endCurrentTrack(); service.sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null); } } diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestLocation.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestLocation.java index 968dd062d..16f777242 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestLocation.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestLocation.java @@ -382,7 +382,6 @@ public class TrackRecordingServiceTestLocation { ), trackPoints); } - @Deprecated // Will be superseded when fixing #500 @MediumTest @Test public void testOnLocationChangedAsync_idle_withSensorDistance() { @@ -407,23 +406,25 @@ public class TrackRecordingServiceTestLocation { // when altitudeSumManager.addAltitudeGain_m(6f); altitudeSumManager.addAltitudeLoss_m(6f); - remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(0))); - remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(2))); + remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(0))); //Should be ignored + remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(2))); //TODO Should be ignored; distance will be added to TrackPoint TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 15); remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(12))); - TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 2, 15); - remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(13))); - TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 3, 15); + remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(13))); //Should be ignored + altitudeSumManager.addAltitudeGain_m(6f); altitudeSumManager.addAltitudeLoss_m(6f); - remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(14))); - TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 4, 15); + remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(14))); //Should be ignored + TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 4, 15); //Should be ignored altitudeSumManager.addAltitudeGain_m(6f); altitudeSumManager.addAltitudeLoss_m(6f); remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(16))); + + altitudeSumManager.addAltitudeGain_m(7f); + altitudeSumManager.addAltitudeLoss_m(7f); service.endCurrentTrack(); // then @@ -434,34 +435,31 @@ public class TrackRecordingServiceTestLocation { .ignoreTime(); a.assertEquals(List.of( new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, null), + new TrackPoint(TrackPoint.Type.SENSORPOINT, null) // TODO Should be ignored; is stored as it assumed to be first in current segment. + .setAltitudeGain(6f) + .setAltitudeLoss(6f) + .setSpeed(Speed.of(5)) + .setSensorDistance(Distance.of(2)), new TrackPoint(TrackPoint.Type.TRACKPOINT, null) .setLatitude(45) .setLongitude(35) .setHorizontalAccuracy(Distance.of(1)) .setSpeed(Speed.of(5)) - .setAltitudeGain(6f) - .setAltitudeLoss(6f) - .setSensorDistance(Distance.of(2)), - new TrackPoint(TrackPoint.Type.TRACKPOINT, null) - .setLatitude(45) - .setLongitude(35) - .setHorizontalAccuracy(Distance.of(2)) + .setSensorDistance(Distance.of(0)), + new TrackPoint(TrackPoint.Type.SENSORPOINT, null) .setSpeed(Speed.of(5)) .setSensorDistance(Distance.of(10)), - new TrackPoint(TrackPoint.Type.TRACKPOINT, null) - .setLatitude(45) - .setLongitude(35) - .setHorizontalAccuracy(Distance.of(4)) + new TrackPoint(TrackPoint.Type.SENSORPOINT, null) .setSpeed(Speed.of(5)) - .setAltitudeGain(6f) - .setAltitudeLoss(6f) - .setSensorDistance(Distance.of(2)), + .setAltitudeGain(12f) + .setAltitudeLoss(12f) + .setSensorDistance(Distance.of(4)), new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, null) .setSensorDistance(Distance.of(11)) .setSpeed(Speed.of(5)) - .setAltitudeGain(6f) - .setAltitudeLoss(6f) - .setSensorDistance(Distance.of(2)) + .setAltitudeGain(7f) + .setAltitudeLoss(7f) + .setSensorDistance(Distance.of(0)) ), trackPoints); } diff --git a/src/main/java/de/dennisguse/opentracks/content/data/Altitude.java b/src/main/java/de/dennisguse/opentracks/content/data/Altitude.java index 17d30a4b2..ae1f9ed50 100644 --- a/src/main/java/de/dennisguse/opentracks/content/data/Altitude.java +++ b/src/main/java/de/dennisguse/opentracks/content/data/Altitude.java @@ -59,5 +59,12 @@ public abstract class Altitude { return new EGM2008(altitude_m); } } + + @Override + public String toString() { + return "Altitude{" + + "altitude_m=" + altitude_m + this.getClass().getSimpleName() + + '}'; + } } diff --git a/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java b/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java index ebc2444cc..24f69b6dd 100644 --- a/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java +++ b/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java @@ -64,7 +64,7 @@ public class TrackPoint { SEGMENT_START_AUTOMATIC(-1), //Start of a segment due to too much distance from previous TrackPoint TRACKPOINT(0), //Just GPS data and may contain BLE sensor data - SENSORPOINT(2), //Just BLE sensor data + SENSORPOINT(2), //Just BLE sensor data; required to have speed and sensorDistance SEGMENT_END_MANUAL(1); //End of a segment @@ -109,13 +109,7 @@ public class TrackPoint { public TrackPoint(@NonNull Type type, @NonNull Location location, @NonNull Instant time) { this(type, time); - this.latitude = location.getLatitude(); - this.longitude = location.getLongitude(); - this.altitude = location.hasAltitude() ? Altitude.WGS84.of(location.getAltitude()) : null; - this.speed = location.hasSpeed() ? Speed.of(location.getSpeed()) : null; - this.horizontalAccuracy = location.hasAccuracy() ? Distance.of(location.getAccuracy()) : null; - - //TODO Should we copy the bearing? + setLocation(location); } @VisibleForTesting @@ -152,6 +146,10 @@ public class TrackPoint { return type == Type.SEGMENT_END_MANUAL; } + public boolean wasCreatedManually() { + return hasLocation() || hasSpeed(); + } + /** * May be null if the track was not loaded from the database. */ @@ -211,6 +209,17 @@ public class TrackPoint { return location; } + public TrackPoint setLocation(@NonNull Location location) { + this.latitude = location.getLatitude(); + this.longitude = location.getLongitude(); + this.altitude = location.hasAltitude() ? Altitude.WGS84.of(location.getAltitude()) : null; + this.speed = location.hasSpeed() ? Speed.of(location.getSpeed()) : null; + this.horizontalAccuracy = location.hasAccuracy() ? Distance.of(location.getAccuracy()) : null; + + //TODO Should we copy the bearing? + return this; + } + public boolean hasAltitudeGain() { return altitudeGain_m != null; } @@ -310,13 +319,13 @@ public class TrackPoint { return this; } - @Nullable - public Distance distanceToPrevious(@Nullable TrackPoint previous) { + @NonNull + public Distance distanceToPrevious(@NonNull TrackPoint previous) { if (hasSensorDistance()) { return getSensorDistance(); } - if (previous == null || !(hasLocation() && previous.hasLocation())) { - return null; + if (!hasLocation() || hasLocation() != previous.hasLocation()) { + throw new RuntimeException("Cannot compute distance."); } return Distance.of(getLocation().distanceTo(previous.getLocation())); @@ -411,7 +420,7 @@ public class TrackPoint { public String toString() { String result = "time=" + getTime() + " (type=" + getType() + ")"; if (hasLocation()) { - result += ": lat=" + getLatitude() + " lng=" + getLongitude(); + result += ": lat=" + getLatitude() + " lng=" + getLongitude() + " alt=" + getAltitude(); } if (hasHorizontalAccuracy()) { result += " acc=" + getHorizontalAccuracy(); @@ -419,6 +428,12 @@ public class TrackPoint { if (hasSensorDistance()) { result += " distance=" + getSensorDistance(); } + if (hasAltitudeGain()) { + result += " altitudeGain= " + getAltitudeGain(); + } + if (hasAltitudeLoss()) { + result += " altitudeLoss= " + getAltitudeLoss(); + } return result; } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlTrackImporter.java index 9b1f2cbd9..385eedef7 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlTrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlTrackImporter.java @@ -306,23 +306,10 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac Instant time = whenList.get(i); Location location = locationList.get(i); - TrackPoint trackPoint; - if (i == 0) { - //first - if (location == null) { - trackPoint = TrackPoint.createSegmentStartManualWithTime(time); - } else { - trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, location, time); - } - } else if (i == locationList.size() - 1 && location == null) { - //last - trackPoint = TrackPoint.createSegmentEndWithTime(time); - } else { - if (location == null) { - trackPoint = new TrackPoint(TrackPoint.Type.SENSORPOINT, time); - } else { - trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, location, time); - } + TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SENSORPOINT, time); + if (location != null) { + trackPoint.setType(TrackPoint.Type.TRACKPOINT); + trackPoint.setLocation(location); } if (i < sensorSpeedList.size() && sensorSpeedList.get(i) != null) { @@ -347,6 +334,21 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac trackPoint.setAltitudeLoss(altitudeLossList.get(i)); } + // Update TrackPoint type for START / STOP. + TrackPoint.Type type = trackPoint.getType(); + if (i == 0) { + //first + if (!trackPoint.wasCreatedManually()) { + type = TrackPoint.Type.SEGMENT_START_MANUAL; + } else { + type = TrackPoint.Type.SEGMENT_START_AUTOMATIC; + } + } else if (i == locationList.size() - 1 && !trackPoint.wasCreatedManually()) { + //last + type = TrackPoint.Type.SEGMENT_END_MANUAL; + } + trackPoint.setType(type); + trackImporter.addTrackPoint(trackPoint); } } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/TrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/TrackImporter.java index 31e67dd5f..fe6067d48 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/TrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/TrackImporter.java @@ -198,8 +198,8 @@ public class TrackImporter { TrackPoint previous = trackPoints.get(i - 1); TrackPoint current = trackPoints.get(i); - Distance distanceToPrevious = current.distanceToPrevious(previous); - if (distanceToPrevious != null) { + if (current.hasSensorDistance() || (previous.hasLocation() && current.hasLocation())) { + Distance distanceToPrevious = current.distanceToPrevious(previous); if (!current.hasSpeed()) { Duration timeDifference = Duration.between(previous.getTime(), current.getTime()); current.setSpeed(Speed.of(distanceToPrevious, timeDifference)); diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java index 5b3528c59..dd13fac79 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java @@ -39,8 +39,6 @@ class TrackRecordingManager { private Track.Id trackId; private TrackStatisticsUpdater trackStatisticsUpdater; - //TDOO use lastStoredTrackPoint? - private boolean currentSegmentHasTrackPoint; private TrackPoint lastTrackPoint; private TrackPoint lastStoredTrackPoint; private TrackPoint lastStoredTrackPointWithLocation; @@ -68,8 +66,6 @@ class TrackRecordingManager { track.setName(TrackNameUtils.getTrackName(context, trackId, track.getStartTime())); contentProviderUtils.updateTrack(track); - currentSegmentHasTrackPoint = false; - return trackId; } @@ -85,7 +81,6 @@ class TrackRecordingManager { trackStatisticsUpdater = new TrackStatisticsUpdater(track.getTrackStatistics()); insertTrackPoint(trackId, segmentStartTrackPoint); - currentSegmentHasTrackPoint = false; lastTrackPoint = null; lastStoredTrackPoint = null; lastStoredTrackPointWithLocation = null; @@ -94,7 +89,6 @@ class TrackRecordingManager { void pause(TrackPointCreator trackPointCreator) { insertTrackPoint(trackId, trackPointCreator.createSegmentEnd()); - currentSegmentHasTrackPoint = false; lastTrackPoint = null; lastStoredTrackPoint = null; lastStoredTrackPointWithLocation = null; @@ -107,7 +101,6 @@ class TrackRecordingManager { trackId = null; trackStatisticsUpdater = null; - currentSegmentHasTrackPoint = false; lastTrackPoint = null; lastStoredTrackPoint = null; lastStoredTrackPointWithLocation = null; @@ -161,44 +154,43 @@ class TrackRecordingManager { /** * @return TrackPoint was stored? */ - boolean onNewTrackPoint(TrackPoint trackPoint) { + boolean onNewTrackPoint(@NonNull TrackPoint trackPoint) { //Storing trackPoint // Always insert the first segment location - if (!currentSegmentHasTrackPoint) { + if (lastStoredTrackPoint == null) { insertTrackPoint(trackId, trackPoint); - currentSegmentHasTrackPoint = true; return true; } - Distance distanceToLastStoredTrackPoint = trackPoint.distanceToPrevious(lastStoredTrackPoint); - if (distanceToLastStoredTrackPoint != null) { - if (distanceToLastStoredTrackPoint.greaterThan(maxRecordingDistance)) { - trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC); - insertTrackPoint(trackId, trackPoint); - return true; - } - - if (distanceToLastStoredTrackPoint.greaterOrEqualThan(recordingDistanceInterval) && trackPoint.isMoving()) { - insertTrackPoint(trackId, trackPoint); - return true; - } - - if (trackPoint.hasLocation()) { - if (lastStoredTrackPointWithLocation == null) { - insertTrackPoint(trackId, trackPoint); - return true; - } - - Distance distanceToLastStoredTrackPointWithLocation = trackPoint.distanceToPrevious(lastStoredTrackPointWithLocation); - if (distanceToLastStoredTrackPointWithLocation != null && distanceToLastStoredTrackPointWithLocation.greaterOrEqualThan(recordingDistanceInterval)) { - insertTrackPoint(trackId, trackPoint); - return true; - } - } + if (trackPoint.hasLocation() && lastStoredTrackPointWithLocation == null) { + insertTrackPoint(trackId, trackPoint); + return true; } - if (lastStoredTrackPoint != null && trackPoint.isMoving() != lastStoredTrackPoint.isMoving()) { + if (!trackPoint.hasLocation() && !trackPoint.hasSensorDistance()) { + Log.d(TAG, "Ignoring TrackPoint as it has no distance."); + return false; + } + TrackPoint distanceTo = lastStoredTrackPoint; + if (trackPoint.hasLocation() && !lastStoredTrackPoint.hasLocation()) { + distanceTo = lastStoredTrackPointWithLocation; + } + + Distance distanceToLastStoredTrackPoint = trackPoint.distanceToPrevious(distanceTo); + if (distanceToLastStoredTrackPoint.greaterThan(maxRecordingDistance)) { + trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC); + insertTrackPoint(trackId, trackPoint); + return true; + } + + if (distanceToLastStoredTrackPoint.greaterOrEqualThan(recordingDistanceInterval) + && trackPoint.isMoving()) { + insertTrackPoint(trackId, trackPoint); + return true; + } + + if (trackPoint.isMoving() != lastStoredTrackPoint.isMoving()) { // Moving from non-moving to moving or vice versa; required to compute moving time correctly. insertTrackPoint(trackId, trackPoint); return true; @@ -206,6 +198,7 @@ class TrackRecordingManager { Log.d(TAG, "Not recording TrackPoint"); lastTrackPoint = trackPoint; + return false; } @@ -213,6 +206,7 @@ class TrackRecordingManager { return trackStatisticsUpdater.getTrackStatistics(); } + //TODO Should be only be called from onNewTrackPoint(). private void insertTrackPoint(@NonNull Track.Id trackId, @NonNull TrackPoint trackPoint) { if (lastTrackPoint != null) { if (lastStoredTrackPoint != null && lastTrackPoint.getTime().equals(lastStoredTrackPoint.getTime())) { diff --git a/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java b/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java index 5fd0e09a4..49b1709d4 100644 --- a/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java +++ b/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java @@ -26,6 +26,7 @@ import java.time.Instant; import de.dennisguse.opentracks.content.data.Altitude; import de.dennisguse.opentracks.content.data.Distance; import de.dennisguse.opentracks.content.data.Speed; +import de.dennisguse.opentracks.content.data.TrackPoint; /** * Statistical data about a {@link de.dennisguse.opentracks.content.data.Track}. @@ -161,6 +162,7 @@ public class TrackStatistics { public void setStopTime(Instant stopTime) { if (stopTime.isBefore(startTime)) { + // Time must be monotonically increasing, but we might have events at the same point in time (BLE and GPS) throw new RuntimeException("stopTime cannot be less than startTime: " + startTime + " " + stopTime); } this.stopTime = stopTime; @@ -199,8 +201,15 @@ public class TrackStatistics { this.movingTime = movingTime; } + public void addMovingTime(TrackPoint trackPoint, TrackPoint lastTrackPoint) { + addMovingTime(Duration.between(lastTrackPoint.getTime(), trackPoint.getTime())); + } + @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public void addMovingTime(Duration time) { + if (time.isNegative()) { + throw new RuntimeException("Moving time cannot be negative"); + } movingTime = movingTime.plus(time); } diff --git a/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java b/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java index b26071fe7..f2ec77d57 100644 --- a/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java +++ b/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java @@ -148,23 +148,19 @@ public class TrackStatisticsUpdater { if (trackPoint.hasSensorDistance()) { // Sensor-based distance/speed currentSegment.addTotalDistance(trackPoint.getSensorDistance()); - } else if (lastTrackPoint != null && trackPoint.isMoving()) { + } else if (lastTrackPoint != null + && lastTrackPoint.hasLocation() + && trackPoint.hasLocation() && trackPoint.isMoving()) { // GPS-based distance/speed // Assumption: we ignore TrackPoints that are not moving as those are likely imprecise GPS measurements Distance movingDistance = trackPoint.distanceToPrevious(lastTrackPoint); - if (movingDistance != null) { - currentSegment.addTotalDistance(movingDistance); - } + currentSegment.addTotalDistance(movingDistance); } // Update moving time if (trackPoint.isMoving() && lastTrackPoint != null && lastTrackPoint.isMoving()) { - Duration movingTime = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime()); - if (movingTime.isNegative()) { - throw new RuntimeException("Moving time cannot be negative"); - } - currentSegment.addMovingTime(movingTime); + currentSegment.addMovingTime(trackPoint, lastTrackPoint); // Update max speed updateSpeed(trackPoint, lastTrackPoint);