forked from upstream-mirrors/OpenTracks
SENSORPOINT and TRACKPOINT require a distance to be stored.
TrackRecording and TrackStatisticsUpdater. Part of #500 Fixes #1074.
This commit is contained in:
@@ -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() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user