TrackPoint is now a record.

This commit is contained in:
Dennis Guse
2025-12-23 22:58:33 +01:00
committed by Dennis Guse
parent 42ae582cd8
commit 015352c989
48 changed files with 1851 additions and 1310 deletions
@@ -90,20 +90,20 @@ public class CSVTrackExporter implements TrackExporter {
public boolean writeTrack(@NonNull List<Track> tracks, @NonNull OutputStream outputStream) {
List<Column> columns = List.of(
new Column("time", null),
new Column("trackpoint_type", t -> quote(t.getType().name())),
new Column("latitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getPosition().latitude()) : ""),
new Column("longitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getPosition().longitude()) : ""),
new Column("altitude", t -> t.hasAltitude() ? ALTITUDE_FORMAT.format(t.getAltitude().toM()) : ""),
new Column("accuracy_horizontal", t -> t.hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.getHorizontalAccuracy().toM()) : ""),
new Column("accuracy_vertical", t -> t.hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.getVerticalAccuracy().toM()) : ""),
new Column("trackpoint_type", t -> quote(t.type().name())),
new Column("latitude", t -> t.position().hasLocation() ? COORDINATE_FORMAT.format(t.position().latitude()) : ""),
new Column("longitude", t -> t.position().hasLocation() ? COORDINATE_FORMAT.format(t.position().longitude()) : ""),
new Column("altitude", t -> t.position().hasAltitude() ? ALTITUDE_FORMAT.format(t.position().altitude().toM()) : ""),
new Column("accuracy_horizontal", t -> t.position().hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.position().horizontalAccuracy().toM()) : ""),
new Column("accuracy_vertical", t -> t.position().hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.position().verticalAccuracy().toM()) : ""),
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""),
new Column("altitude_gain", t -> t.hasAltitudeGainLoss() ? ALTITUDE_FORMAT.format(t.getAltitudeGainLoss().gain_m()) : ""),
new Column("altitude_loss", t -> t.hasAltitudeGainLoss() ? ALTITUDE_FORMAT.format(t.getAltitudeGainLoss().loss_m()) : ""),
new Column("sensor_distance", t -> t.hasSensorDistance() ? DISTANCE_FORMAT.format(t.getSensorDistance().toM()) : ""),
new Column("heartrate", t -> t.hasHeartRate() ? HEARTRATE_FORMAT.format(t.getHeartRate().getBPM()) : ""),
new Column("cadence", t -> t.hasCadence() ? CADENCE_FORMAT.format(t.getCadence().getRPM()) : ""),
new Column("power", t -> t.hasPower() ? POWER_FORMAT.format(t.getPower().getW()) : ""));
new Column("speed", t -> t.position().hasSpeed() ? SPEED_FORMAT.format(t.position().speed().toKMH()) : ""),
new Column("altitude_gain", t -> t.altitudeGainLoss() != null ? ALTITUDE_FORMAT.format(t.altitudeGainLoss().gain_m()) : ""),
new Column("altitude_loss", t -> t.altitudeGainLoss() != null ? ALTITUDE_FORMAT.format(t.altitudeGainLoss().loss_m()) : ""),
new Column("sensor_distance", t -> t.sensorDistance() != null ? DISTANCE_FORMAT.format(t.sensorDistance().toM()) : ""),
new Column("heartrate", t -> t.heartRate() != null ? HEARTRATE_FORMAT.format(t.heartRate().getBPM()) : ""),
new Column("cadence", t -> t.cadence() != null ? CADENCE_FORMAT.format(t.cadence().getRPM()) : ""),
new Column("power", t -> t.power() != null ? POWER_FORMAT.format(t.power().getW()) : ""));
try {
prepare(outputStream);
@@ -140,7 +140,7 @@ public class GPXTrackExporter implements TrackExporter {
wroteTrack = true;
}
switch (trackPoint.getType()) {
switch (trackPoint.type()) {
case SEGMENT_START_MANUAL ->
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_START_MANUAL.name() + " is not supported.");
case SEGMENT_END_MANUAL -> {
@@ -152,7 +152,7 @@ public class GPXTrackExporter implements TrackExporter {
if (wroteSegment) writeCloseSegment();
writeOpenSegment();
wroteSegment = true;
if (trackPoint.hasLocation()) {
if (trackPoint.position().hasLocation()) {
trackDistance = trackDistance.plus(writeTrackPoint(track.zoneOffset(), trackPoint, sensorPoints, trackDistance));
sensorPoints.clear();
} else {
@@ -165,7 +165,7 @@ public class GPXTrackExporter implements TrackExporter {
writeOpenSegment();
wroteSegment = true;
}
if (trackPoint.hasLocation()) {
if (trackPoint.position().hasLocation()) {
trackDistance = trackDistance.plus(writeTrackPoint(track.zoneOffset(), trackPoint, sensorPoints, trackDistance));
sensorPoints.clear();
} else {
@@ -176,7 +176,7 @@ public class GPXTrackExporter implements TrackExporter {
// Not supported as IDLE-TrackPoints have no location.
}
default ->
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.type());
}
}
@@ -306,10 +306,10 @@ public class GPXTrackExporter implements TrackExporter {
private Distance writeTrackPoint(ZoneOffset zoneOffset, TrackPoint trackPoint, List<TrackPoint> sensorPoints, Distance trackDistance) {
Distance cumulativeDistance;
printWriter.println("<trkpt " + formatLocation(trackPoint.getPosition()) + ">");
printWriter.println("<trkpt " + formatLocation(trackPoint.position()) + ">");
if (trackPoint.hasAltitude()) {
printWriter.println("<ele>" + ALTITUDE_FORMAT.format(trackPoint.getAltitude().toM()) + "</ele>");
if (trackPoint.position().hasAltitude()) {
printWriter.println("<ele>" + ALTITUDE_FORMAT.format(trackPoint.position().altitude().toM()) + "</ele>");
}
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(trackPoint.getTime(), zoneOffset) + "</time>");
@@ -317,41 +317,41 @@ public class GPXTrackExporter implements TrackExporter {
{
String trackPointExtensionV2Content = "";
if (trackPoint.hasHeartRate()) {
trackPointExtensionV2Content += "<gpxtpx:hr>" + HEARTRATE_FORMAT.format(trackPoint.getHeartRate().getBPM()) + "</gpxtpx:hr>\n";
if (trackPoint.heartRate() != null) {
trackPointExtensionV2Content += "<gpxtpx:hr>" + HEARTRATE_FORMAT.format(trackPoint.heartRate().getBPM()) + "</gpxtpx:hr>\n";
}
if (trackPoint.hasCadence()) {
trackPointExtensionV2Content += "<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.getCadence().getRPM()) + "</gpxtpx:cad>\n";
if (trackPoint.cadence() != null) {
trackPointExtensionV2Content += "<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.cadence().getRPM()) + "</gpxtpx:cad>\n";
}
if (trackPoint.hasSpeed()) {
trackPointExtensionV2Content += "<gpxtpx:speed>" + SPEED_FORMAT.format(trackPoint.getSpeed().toMPS()) + "</gpxtpx:speed>\n";
if (trackPoint.position().hasSpeed()) {
trackPointExtensionV2Content += "<gpxtpx:speed>" + SPEED_FORMAT.format(trackPoint.position().speed().toMPS()) + "</gpxtpx:speed>\n";
}
String extensionContent = "";
if (trackPoint.hasPower()) {
extensionContent += "<pwr:PowerInWatts>" + POWER_FORMAT.format(trackPoint.getPower().getW()) + "</pwr:PowerInWatts>\n";
if (trackPoint.power() != null) {
extensionContent += "<pwr:PowerInWatts>" + POWER_FORMAT.format(trackPoint.power().getW()) + "</pwr:PowerInWatts>\n";
}
Double cumulativeGain = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasAltitudeGainLoss() ? (double) tp.getAltitudeGainLoss().gain_m() : null);
Double cumulativeGain = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.altitudeGainLoss() != null ? (double) tp.altitudeGainLoss().gain_m() : null);
if (cumulativeGain != null) {
extensionContent += ("<opentracks:gain>" + ALTITUDE_FORMAT.format(cumulativeGain) + "</opentracks:gain>\n");
}
Double cumulativeLoss = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasAltitudeGainLoss() ? (double) tp.getAltitudeGainLoss().loss_m() : null);
Double cumulativeLoss = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.altitudeGainLoss() != null ? (double) tp.altitudeGainLoss().loss_m() : null);
if (cumulativeLoss != null) {
extensionContent += ("<opentracks:loss>" + ALTITUDE_FORMAT.format(cumulativeLoss) + "</opentracks:loss>\n");
}
if (trackPoint.hasHorizontalAccuracy()) {
extensionContent += ("<opentracks:accuracy_horizontal>" + DISTANCE_FORMAT.format(trackPoint.getHorizontalAccuracy().toM()) + "</opentracks:accuracy_horizontal>");
if (trackPoint.position().hasHorizontalAccuracy()) {
extensionContent += ("<opentracks:accuracy_horizontal>" + DISTANCE_FORMAT.format(trackPoint.position().horizontalAccuracy().toM()) + "</opentracks:accuracy_horizontal>");
}
if (trackPoint.hasVerticalAccuracy()) {
extensionContent += ("<opentracks:accuracy_vertical>" + DISTANCE_FORMAT.format(trackPoint.getVerticalAccuracy().toM()) + "</opentracks:accuracy_vertical>");
if (trackPoint.position().hasVerticalAccuracy()) {
extensionContent += ("<opentracks:accuracy_vertical>" + DISTANCE_FORMAT.format(trackPoint.position().verticalAccuracy().toM()) + "</opentracks:accuracy_vertical>");
}
cumulativeDistance = Distance.ofOrNull(cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasSensorDistance() ? tp.getSensorDistance().toM() : null));
cumulativeDistance = Distance.ofOrNull(cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.sensorDistance() != null ? tp.sensorDistance().toM() : null));
if (cumulativeDistance != null) {
extensionContent += ("<opentracks:distance>" + DISTANCE_FORMAT.format(cumulativeDistance.toM()) + "</opentracks:distance>\n");
extensionContent += ("<cluetrust:distance>" + DISTANCE_FORMAT.format(trackDistance.plus(cumulativeDistance).toM()) + "</cluetrust:distance>\n");
@@ -95,7 +95,7 @@ public class KMLTrackExporter implements TrackExporter {
private final List<Float> powerList = new ArrayList<>();
private final List<Float> cadenceList = new ArrayList<>();
private final List<Float> heartRateList = new ArrayList<>();
private final List<Float> altitudeGainList = new ArrayList<>();
private final List<Float> altitudeGainList = new ArrayList<>(); //TODO Join with altitudeLossList
private final List<Float> altitudeLossList = new ArrayList<>();
private final List<Float> accuracyHorizontal = new ArrayList<>();
private final List<Float> accuracyVertical = new ArrayList<>();
@@ -167,7 +167,7 @@ public class KMLTrackExporter implements TrackExporter {
wroteTrack = true;
}
switch (trackPoint.getType()) {
switch (trackPoint.type()) {
case SEGMENT_START_MANUAL, SEGMENT_START_AUTOMATIC -> {
if (wroteSegment) writeCloseSegment();
writeOpenSegment();
@@ -190,7 +190,7 @@ public class KMLTrackExporter implements TrackExporter {
writeTrackPoint(track.zoneOffset(), trackPoint);
}
default ->
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.type());
}
}
@@ -370,28 +370,28 @@ public class KMLTrackExporter implements TrackExporter {
void writeTrackPoint(ZoneOffset zoneOffset, TrackPoint trackPoint) {
printWriter.println("<when>" + getTime(zoneOffset, trackPoint.getTime()) + "</when>");
trackpointTypeList.add(trackPoint.getType());
trackpointTypeList.add(trackPoint.type());
if (trackPoint.hasLocation()) {
printWriter.println("<coord>" + getCoordinates(trackPoint.getPosition(), " ") + "</coord>");
if (trackPoint.position().hasLocation()) {
printWriter.println("<coord>" + getCoordinates(trackPoint.position(), " ") + "</coord>");
} else {
printWriter.println("<coord/>");
}
speedList.add(trackPoint.hasSpeed() ? (float) trackPoint.getSpeed().toMPS() : null);
speedList.add(trackPoint.position().hasSpeed() ? (float) trackPoint.position().speed().toMPS() : null);
distanceList.add(trackPoint.hasSensorDistance() ? (float) trackPoint.getSensorDistance().toM() : null);
heartRateList.add(trackPoint.hasHeartRate() ? trackPoint.getHeartRate().getBPM() : null);
cadenceList.add(trackPoint.hasCadence() ? trackPoint.getCadence().getRPM() : null);
powerList.add(trackPoint.hasPower() ? trackPoint.getPower().getW() : null);
if (trackPoint.hasAltitudeGainLoss()) {
altitudeGainList.add(trackPoint.getAltitudeGainLoss().loss_m());
altitudeLossList.add(trackPoint.getAltitudeGainLoss().loss_m());
distanceList.add(trackPoint.sensorDistance() != null ? (float) trackPoint.sensorDistance().toM() : null);
heartRateList.add(trackPoint.heartRate() != null ? trackPoint.heartRate().getBPM() : null);
cadenceList.add(trackPoint.cadence() != null ? trackPoint.cadence().getRPM() : null);
powerList.add(trackPoint.power() != null ? trackPoint.power().getW() : null);
if (trackPoint.altitudeGainLoss() != null) {
altitudeGainList.add(trackPoint.altitudeGainLoss().loss_m());
altitudeLossList.add(trackPoint.altitudeGainLoss().loss_m());
} else {
altitudeGainList.add(null);
altitudeLossList.add(null);
}
accuracyHorizontal.add(trackPoint.hasHorizontalAccuracy() ? (float) trackPoint.getHorizontalAccuracy().toM() : null);
accuracyVertical.add(trackPoint.hasVerticalAccuracy() ? (float) trackPoint.getVerticalAccuracy().toM() : null);
accuracyHorizontal.add(trackPoint.position().hasHorizontalAccuracy() ? (float) trackPoint.position().horizontalAccuracy().toM() : null);
accuracyVertical.add(trackPoint.position().hasVerticalAccuracy() ? (float) trackPoint.position().verticalAccuracy().toM() : null);
}
private void writeSimpleArraySensorData(List<Float> list, String name) {
@@ -32,9 +32,13 @@ import java.util.List;
import java.util.Locale;
import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint;
@@ -265,8 +269,8 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
return;
}
TrackPoint first = currentSegment.getFirst();
first.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
TrackPoint first = currentSegment.removeFirst();
currentSegment.addFirst(first.with(TrackPoint.Type.SEGMENT_START_AUTOMATIC));
trackImporter.addTrackPoints(currentSegment);
currentSegment.clear();
@@ -332,59 +336,70 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
}
}
TrackPoint trackPoint = new TrackPoint(
TrackPoint.Type.TRACKPOINT,
new Position(
parsedTime.toInstant(),
latitudeParsed,
longitudeParsed,
accuracyHorizontalParsed,
altitudeParsed,
accuracyVerticalParsed,
null,
speedParsed
));
if (heartrate != null) {
try {
trackPoint.setHeartRate(Float.parseFloat(heartrate));
} catch (NumberFormatException e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse heart rate: %s", heartrate)), e);
}
}
if (cadence != null) {
try {
trackPoint.setCadence(Float.parseFloat(cadence));
} catch (Exception e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse cadence: %s", cadence)), e);
}
}
if (power != null) {
try {
trackPoint.setPower(Float.parseFloat(power));
} catch (NumberFormatException e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse power: %s", power)), e);
}
}
if (gain != null && loss != null) {
try {
trackPoint.setAltitudeGainLoss(Float.parseFloat(gain), Float.parseFloat(loss));
} catch (NumberFormatException e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse altitude gain: %s or los: %s", gain, loss)), e);
}
}
Distance parsedSensorDistance = null;
if (sensorDistance != null) {
try {
trackPoint.setSensorDistance(Distance.of(Float.parseFloat(sensorDistance)));
parsedSensorDistance = Distance.of(Float.parseFloat(sensorDistance));
} catch (NumberFormatException e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse distance: %s", sensorDistance)), e);
}
}
return trackPoint;
HeartRate parsedHeartRate = null;
if (heartrate != null) {
try {
parsedHeartRate = HeartRate.of(Float.parseFloat(heartrate));
} catch (NumberFormatException e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse heart rate: %s", heartrate)), e);
}
}
Cadence parsedCadence = null;
if (cadence != null) {
try {
parsedCadence = Cadence.of(Float.parseFloat(cadence));
} catch (Exception e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse cadence: %s", cadence)), e);
}
}
Power parsedPower = null;
if (power != null) {
try {
parsedPower = Power.of(Float.parseFloat(power));
} catch (NumberFormatException e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse power: %s", power)), e);
}
}
AltitudeGainLoss altitudeGainLoss = null;
if (gain != null && loss != null) {
try {
altitudeGainLoss = new AltitudeGainLoss(Float.parseFloat(gain), Float.parseFloat(loss));
} catch (NumberFormatException e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse altitude gain: %s or los: %s", gain, loss)), e);
}
}
return new TrackPoint(
null,
TrackPoint.Type.TRACKPOINT,
new Position(
parsedTime.toInstant(),
latitudeParsed,
longitudeParsed,
accuracyHorizontalParsed,
altitudeParsed,
accuracyVerticalParsed,
null,
speedParsed
),
parsedSensorDistance,
parsedHeartRate,
parsedCadence,
parsedPower,
altitudeGainLoss
);
}
private void onTrackPointStart(Attributes attributes) {
@@ -425,8 +440,8 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
TrackPoint trackPoint = createTrackPoint();
if (!trackPoint.hasLocation()) {
Log.w(TAG, "Marker with invalid coordinates ignored: " + trackPoint.getPosition());
if (!trackPoint.position().hasLocation()) {
Log.w(TAG, "Marker with invalid coordinates ignored: " + trackPoint.position());
return;
}
markers.add(new Marker(
@@ -435,7 +450,7 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
name != null ? name : "",
description != null ? description : "",
markerType != null ? markerType : "",
trackPoint.getPosition(),
trackPoint.position(),
photoUrl
));
}
@@ -33,9 +33,13 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint;
@@ -95,10 +99,11 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
private ZoneOffset zoneOffset;
private final ArrayList<Instant> whenList = new ArrayList<>();
private final ArrayList<Position> positionList = new ArrayList<>();
private final ArrayList<Position> positionList = new ArrayList<>(); //TODO This is only latitude, longitude, and altitude
private String dataType;
//TODO For ArrayList use specific data models; and why Float? Double would be easier.
private final ArrayList<String> trackpointTypeList = new ArrayList<>();
private final ArrayList<Float> sensorSpeedList = new ArrayList<>();
private final ArrayList<Float> sensorDistanceList = new ArrayList<>();
@@ -151,8 +156,7 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
}
onTrackSegmentStart();
}
case TAG_EXTENDED_DATA, TAG_SIMPLE_ARRAY_DATA, TAG_KML22_SIMPLE_ARRAY_DATA ->
dataType = attributes.getValue(ATTRIBUTE_NAME);
case TAG_EXTENDED_DATA, TAG_SIMPLE_ARRAY_DATA, TAG_KML22_SIMPLE_ARRAY_DATA -> dataType = attributes.getValue(ATTRIBUTE_NAME);
}
}
@@ -317,62 +321,77 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
for (int i = 0; i < positionList.size(); i++) {
Instant time = whenList.get(i);
Position position = positionList.get(i);
TrackPoint trackPoint;
if (position == null) {
trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, Position.of(time));
position = Position.of(time);
} else {
trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, position.with(time));
position = position.with(time);
}
TrackPoint.Type type = null;
Speed speed = null;
Distance sensorDistance = null;
HeartRate heartRate = null;
Cadence cadence = null;
Power power = null;
AltitudeGainLoss altitudeGainLoss = null;
Distance horizontalAccuracy = null;
Distance verticalAccuracy = null;
if (i < trackpointTypeList.size() && trackpointTypeList.get(i) != null) {
TrackPoint.Type type = TrackPoint.Type.valueOf(trackpointTypeList.get(i));
trackPoint.setType(type);
type = TrackPoint.Type.valueOf(trackpointTypeList.get(i));
}
if (i < sensorSpeedList.size() && sensorSpeedList.get(i) != null) {
trackPoint.setSpeed(Speed.of(sensorSpeedList.get(i)));
speed = Speed.of(sensorSpeedList.get(i));
}
if (i < sensorDistanceList.size() && sensorDistanceList.get(i) != null) {
trackPoint.setSensorDistance(Distance.of(sensorDistanceList.get(i)));
sensorDistance = Distance.of(sensorDistanceList.get(i));
}
if (i < sensorHeartRateList.size() && sensorHeartRateList.get(i) != null) {
trackPoint.setHeartRate(sensorHeartRateList.get(i));
if (i < sensorHeartRateList.size()) {
heartRate = HeartRate.ofOrNull(sensorHeartRateList.get(i));
}
if (i < sensorCadenceList.size() && sensorCadenceList.get(i) != null) {
trackPoint.setCadence(sensorCadenceList.get(i));
cadence = Cadence.of(sensorCadenceList.get(i));
}
if (i < sensorPowerList.size() && sensorPowerList.get(i) != null) {
trackPoint.setPower(sensorPowerList.get(i));
power = Power.of(sensorPowerList.get(i));
}
if (i < altitudeGainList.size() && i < altitudeLossList.size()) {
if (altitudeGainList.get(i) != null && altitudeLossList.get(i) != null) {
trackPoint.setAltitudeGainLoss(altitudeGainList.get(i), altitudeLossList.get(i));
}
if ((i < altitudeGainList.size() && altitudeGainList.get(i) != null)
&& (i < altitudeLossList.size() && altitudeLossList.get(i) != null)) {
altitudeGainLoss = new AltitudeGainLoss(altitudeGainList.get(i), altitudeLossList.get(i));
}
if (i < accuracyHorizontal.size() && accuracyHorizontal.get(i) != null) {
trackPoint.setHorizontalAccuracy(Distance.of(accuracyHorizontal.get(i)));
horizontalAccuracy = Distance.of(accuracyHorizontal.get(i));
}
if (i < accuracyVertical.size() && accuracyVertical.get(i) != null) {
trackPoint.setVerticalAccuracy(Distance.of(accuracyVertical.get(i)));
verticalAccuracy = Distance.of(accuracyVertical.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 {
if (wasCreatedAutomatically(position)) {
type = TrackPoint.Type.SEGMENT_START_AUTOMATIC;
} else {
type = TrackPoint.Type.SEGMENT_START_MANUAL;
}
} else if (i == positionList.size() - 1 && !trackPoint.wasCreatedManually()) {
} else if (i == positionList.size() - 1 && !wasCreatedAutomatically(position)) {
//last
type = TrackPoint.Type.SEGMENT_END_MANUAL;
}
trackPoint.setType(type);
trackImporter.addTrackPoint(trackPoint);
trackImporter.addTrackPoint(new TrackPoint(
null,
type != null ? type : TrackPoint.Type.TRACKPOINT,
position
.with(speed)
.withHorizontalAccuracy(horizontalAccuracy)
.withVerticalAccuracy(verticalAccuracy),
sensorDistance,
heartRate,
cadence,
power,
altitudeGainLoss
));
}
}
@@ -392,6 +411,7 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
}
private Position createPosition(String latitude, String longitude, String altitude) {
//TODO Do not use Location.
if (longitude != null && latitude != null) {
Location location = new Location("import");
try {
@@ -439,12 +459,9 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE -> sensorCadenceList.add(value);
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_GAIN -> altitudeGainList.add(value);
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_LOSS -> altitudeLossList.add(value);
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL ->
accuracyHorizontal.add(value);
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_VERTICAL ->
accuracyVertical.add(value);
default ->
Log.w(TAG, "Data from extended data " + dataType + " is not (yet) supported.");
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL -> accuracyHorizontal.add(value);
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_VERTICAL -> accuracyVertical.add(value);
default -> Log.w(TAG, "Data from extended data " + dataType + " is not (yet) supported.");
}
}
@@ -457,6 +474,10 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
trackImporter.finish();
}
public boolean wasCreatedAutomatically(Position position) {
return position.hasLocation() || position.hasSpeed();
}
@Override
public DefaultHandler getHandler() {
return this;
@@ -176,21 +176,19 @@ public class TrackImporter {
TrackPoint previous = trackPoints.get(i - 1);
TrackPoint current = trackPoints.get(i);
if (current.hasSensorDistance() || (previous.hasLocation() && current.hasLocation())) {
if (current.sensorDistance() != null || (previous.position().hasLocation() && current.position().hasLocation())) {
TrackPoint newCurrent = current;
Distance distanceToPrevious = current.distanceToPrevious(previous);
if (!current.hasSpeed()) {
if (!current.position().hasSpeed()) {
Duration timeDifference = Duration.between(previous.getTime(), current.getTime());
current.setSpeed(Speed.of(distanceToPrevious, timeDifference));
newCurrent = newCurrent.with(newCurrent.position().with(Speed.of(distanceToPrevious, timeDifference)));
}
if (!current.hasBearing()) {
previous.bearingTo(current)
.ifPresent(current::setBearing);
}
if (current.getType().equals(TrackPoint.Type.TRACKPOINT) && distanceToPrevious.greaterThan(maxRecordingDistance)) {
current.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
if (current.type().equals(TrackPoint.Type.TRACKPOINT) && distanceToPrevious.greaterThan(maxRecordingDistance)) {
newCurrent = newCurrent.with(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
}
trackPoints.set(i, newCurrent);
}
}
}