forked from upstream-mirrors/OpenTracks
+10
-10
@@ -406,18 +406,18 @@ public class ExportImportTest {
|
|||||||
// TODO Not exported for GPX/KML
|
// TODO Not exported for GPX/KML
|
||||||
// assertEquals(trackPoint.getAccuracy(), importedTrackPoint.getAccuracy(), 0.01);
|
// assertEquals(trackPoint.getAccuracy(), importedTrackPoint.getAccuracy(), 0.01);
|
||||||
|
|
||||||
assertEquals(trackPoint.hasLocation(), importedTrackPoint.hasLocation());
|
assertEquals("" + i, trackPoint.hasLocation(), importedTrackPoint.hasLocation());
|
||||||
if (trackPoint.hasLocation()) {
|
if (trackPoint.hasLocation()) {
|
||||||
assertEquals(trackPoint.getLatitude(), importedTrackPoint.getLatitude(), 0.001);
|
assertEquals("" + i, trackPoint.getLatitude(), importedTrackPoint.getLatitude(), 0.001);
|
||||||
assertEquals(trackPoint.getLongitude(), importedTrackPoint.getLongitude(), 0.001);
|
assertEquals("" + i, trackPoint.getLongitude(), importedTrackPoint.getLongitude(), 0.001);
|
||||||
}
|
}
|
||||||
assertEquals(trackPoint.hasSpeed(), importedTrackPoint.hasSpeed());
|
assertEquals("" + i, trackPoint.hasSpeed(), importedTrackPoint.hasSpeed());
|
||||||
if (trackPoint.hasSpeed()) {
|
if (trackPoint.hasSpeed()) {
|
||||||
assertEquals(trackPoint.getSpeed().toMPS(), importedTrackPoint.getSpeed().toMPS(), 0.001);
|
assertEquals("" + i, trackPoint.getSpeed().toMPS(), importedTrackPoint.getSpeed().toMPS(), 0.001);
|
||||||
}
|
}
|
||||||
assertEquals(trackPoint.hasAltitude(), importedTrackPoint.hasAltitude());
|
assertEquals("" + i, trackPoint.hasAltitude(), importedTrackPoint.hasAltitude());
|
||||||
if (trackPoint.hasAltitude()) {
|
if (trackPoint.hasAltitude()) {
|
||||||
assertEquals(trackPoint.getAltitude().toM(), importedTrackPoint.getAltitude().toM(), 0.001);
|
assertEquals("" + i, trackPoint.getAltitude().toM(), importedTrackPoint.getAltitude().toM(), 0.001);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type.equals(TrackPoint.Type.SEGMENT_START_MANUAL) || type.equals(TrackPoint.Type.SEGMENT_END_MANUAL)) {
|
if (type.equals(TrackPoint.Type.SEGMENT_START_MANUAL) || type.equals(TrackPoint.Type.SEGMENT_END_MANUAL)) {
|
||||||
@@ -426,13 +426,13 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (verifyHeartrate) {
|
if (verifyHeartrate) {
|
||||||
assertEquals("" + trackPoint, trackPoint.getHeartRate_bpm(), importedTrackPoint.getHeartRate_bpm(), 0.01);
|
assertEquals("" + i, trackPoint.getHeartRate_bpm(), importedTrackPoint.getHeartRate_bpm(), 0.01);
|
||||||
}
|
}
|
||||||
if (verifyCadence) {
|
if (verifyCadence) {
|
||||||
assertEquals("" + trackPoint, trackPoint.getCyclingCadence_rpm(), importedTrackPoint.getCyclingCadence_rpm(), 0.01);
|
assertEquals("" + i, trackPoint.getCyclingCadence_rpm(), importedTrackPoint.getCyclingCadence_rpm(), 0.01);
|
||||||
}
|
}
|
||||||
if (verifyPower) {
|
if (verifyPower) {
|
||||||
assertEquals("" + trackPoint, trackPoint.getPower(), importedTrackPoint.getPower(), 0.01);
|
assertEquals("" + i, trackPoint.getPower(), importedTrackPoint.getPower(), 0.01);
|
||||||
}
|
}
|
||||||
if (verifyAltitudeGain) {
|
if (verifyAltitudeGain) {
|
||||||
assertEquals(trackPoint.getAltitudeGain(), importedTrackPoint.getAltitudeGain(), 0.01);
|
assertEquals(trackPoint.getAltitudeGain(), importedTrackPoint.getAltitudeGain(), 0.01);
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public final class Marker {
|
|||||||
private String description = "";
|
private String description = "";
|
||||||
private String category = "";
|
private String category = "";
|
||||||
private String icon = "";
|
private String icon = "";
|
||||||
private final Track.Id trackId;
|
private Track.Id trackId;
|
||||||
|
|
||||||
private final Instant time;
|
private final Instant time;
|
||||||
private Double latitude;
|
private Double latitude;
|
||||||
@@ -74,16 +74,13 @@ public final class Marker {
|
|||||||
if (!trackPoint.hasLocation())
|
if (!trackPoint.hasLocation())
|
||||||
throw new RuntimeException("Marker requires a trackpoint with a location.");
|
throw new RuntimeException("Marker requires a trackpoint with a location.");
|
||||||
|
|
||||||
this.latitude = trackPoint.getLatitude();
|
setTrackPoint(trackPoint);
|
||||||
this.longitude = trackPoint.getLongitude();
|
|
||||||
if (trackPoint.hasAccuracy()) this.accuracy = trackPoint.getAccuracy();
|
|
||||||
if (trackPoint.hasAltitude()) this.altitude = trackPoint.getAltitude();
|
|
||||||
if (trackPoint.hasBearing()) this.bearing = trackPoint.getBearing();
|
|
||||||
|
|
||||||
this.length = Distance.of(0); //TODO Not cool!
|
this.length = Distance.of(0); //TODO Not cool!
|
||||||
this.duration = Duration.ofMillis(0); //TODO Not cool!
|
this.duration = Duration.ofMillis(0); //TODO Not cool!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public Marker(String name, String description, String category, String icon, @NonNull Track.Id trackId, @NonNull TrackStatistics statistics, @NonNull TrackPoint trackPoint, String photoUrl) {
|
public Marker(String name, String description, String category, String icon, @NonNull Track.Id trackId, @NonNull TrackStatistics statistics, @NonNull TrackPoint trackPoint, String photoUrl) {
|
||||||
this(trackId, trackPoint);
|
this(trackId, trackPoint);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@@ -95,6 +92,15 @@ public final class Marker {
|
|||||||
this.photoUrl = photoUrl;
|
this.photoUrl = photoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO Is somehow part of the initialization process. Can we at least limit visibility?
|
||||||
|
public void setTrackPoint(TrackPoint trackPoint) {
|
||||||
|
this.latitude = trackPoint.getLatitude();
|
||||||
|
this.longitude = trackPoint.getLongitude();
|
||||||
|
if (trackPoint.hasAccuracy()) this.accuracy = trackPoint.getAccuracy();
|
||||||
|
if (trackPoint.hasAltitude()) this.altitude = trackPoint.getAltitude();
|
||||||
|
if (trackPoint.hasBearing()) this.bearing = trackPoint.getBearing();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* May be null if the it was not loaded from the database.
|
* May be null if the it was not loaded from the database.
|
||||||
*/
|
*/
|
||||||
@@ -143,16 +149,19 @@ public final class Marker {
|
|||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public Track.Id getTrackId() {
|
public Track.Id getTrackId() {
|
||||||
return trackId;
|
return trackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void setTrackId(@NonNull Track.Id trackId) {
|
||||||
|
this.trackId = trackId;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasLocation() {
|
public boolean hasLocation() {
|
||||||
return latitude != null || longitude != null;
|
return latitude != null || longitude != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Location getLocation() {
|
public Location getLocation() {
|
||||||
Location location = new Location("");
|
Location location = new Location("");
|
||||||
location.setTime(time.toEpochMilli());
|
location.setTime(time.toEpochMilli());
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
|
|||||||
* @author Leif Hendrik Wilden
|
* @author Leif Hendrik Wilden
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
|
//TODO Do not default initialize attributes; might be confusing for debugging
|
||||||
public class Track {
|
public class Track {
|
||||||
|
|
||||||
private Track.Id id;
|
private Track.Id id;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public class TrackPoint {
|
|||||||
private Altitude altitude;
|
private Altitude altitude;
|
||||||
private Speed speed;
|
private Speed speed;
|
||||||
private Float bearing;
|
private Float bearing;
|
||||||
private Distance sensorDistance_m;
|
private Distance sensorDistance;
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
SEGMENT_START_MANUAL(-2), //Start of a segment due to user interaction (start, resume)
|
SEGMENT_START_MANUAL(-2), //Start of a segment due to user interaction (start, resume)
|
||||||
@@ -99,7 +99,11 @@ public class TrackPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TrackPoint(@NonNull Location location) {
|
public TrackPoint(@NonNull Location location) {
|
||||||
this(Type.TRACKPOINT);
|
this(Type.TRACKPOINT, location, Instant.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrackPoint(@NonNull Type type, @NonNull Location location, @NonNull Instant time) {
|
||||||
|
this(type);
|
||||||
|
|
||||||
this.latitude = location.getLatitude();
|
this.latitude = location.getLatitude();
|
||||||
this.longitude = location.getLongitude();
|
this.longitude = location.getLongitude();
|
||||||
@@ -107,7 +111,9 @@ public class TrackPoint {
|
|||||||
this.speed = Speed.of(location.getSpeed());
|
this.speed = Speed.of(location.getSpeed());
|
||||||
this.accuracy = location.getAccuracy();
|
this.accuracy = location.getAccuracy();
|
||||||
|
|
||||||
setTime(Instant.now());
|
//TODO Should we copy the bearing?
|
||||||
|
|
||||||
|
setTime(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackPoint(@NonNull Type type, Instant time) {
|
public TrackPoint(@NonNull Type type, Instant time) {
|
||||||
@@ -314,10 +320,14 @@ public class TrackPoint {
|
|||||||
this.accuracy = horizontalAccuracy;
|
this.accuracy = horizontalAccuracy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public Distance distanceToPrevious(@NonNull TrackPoint previous) {
|
public Distance distanceToPrevious(@NonNull TrackPoint previous) {
|
||||||
if (hasSensorDistance()) {
|
if (hasSensorDistance()) {
|
||||||
return getSensorDistance();
|
return getSensorDistance();
|
||||||
}
|
}
|
||||||
|
if (!(hasLocation() && previous.hasLocation())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return Distance.of(getLocation().distanceTo(previous.getLocation()));
|
return Distance.of(getLocation().distanceTo(previous.getLocation()));
|
||||||
}
|
}
|
||||||
@@ -336,15 +346,15 @@ public class TrackPoint {
|
|||||||
|
|
||||||
// Sensor data
|
// Sensor data
|
||||||
public boolean hasSensorDistance() {
|
public boolean hasSensorDistance() {
|
||||||
return sensorDistance_m != null;
|
return sensorDistance != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Distance getSensorDistance() {
|
public Distance getSensorDistance() {
|
||||||
return sensorDistance_m;
|
return sensorDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSensorDistance(Distance distance_m) {
|
public void setSensorDistance(Distance distance_m) {
|
||||||
this.sensorDistance_m = distance_m;
|
this.sensorDistance = distance_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSensorData() {
|
public boolean hasSensorData() {
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ public class ContentProviderUtils {
|
|||||||
* @param track the track
|
* @param track the track
|
||||||
* @return the content provider URI of the inserted track.
|
* @return the content provider URI of the inserted track.
|
||||||
*/
|
*/
|
||||||
|
///TODO Return Track.Id
|
||||||
public Uri insertTrack(Track track) {
|
public Uri insertTrack(Track track) {
|
||||||
return contentResolver.insert(TracksColumns.CONTENT_URI, createContentValues(track));
|
return contentResolver.insert(TracksColumns.CONTENT_URI, createContentValues(track));
|
||||||
}
|
}
|
||||||
@@ -569,6 +570,7 @@ public class ContentProviderUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//TODO Only used for file import; might be better to replace it.
|
//TODO Only used for file import; might be better to replace it.
|
||||||
|
//TODO Rename to bulkInsert
|
||||||
public int bulkInsertTrackPoint(List<TrackPoint> trackPoints, Track.Id trackId) {
|
public int bulkInsertTrackPoint(List<TrackPoint> trackPoints, Track.Id trackId) {
|
||||||
ContentValues[] values = new ContentValues[trackPoints.size()];
|
ContentValues[] values = new ContentValues[trackPoints.size()];
|
||||||
for (int i = 0; i < trackPoints.size(); i++) {
|
for (int i = 0; i < trackPoints.size(); i++) {
|
||||||
@@ -577,6 +579,15 @@ public class ContentProviderUtils {
|
|||||||
return contentResolver.bulkInsert(TrackPointsColumns.CONTENT_URI_BY_ID, values);
|
return contentResolver.bulkInsert(TrackPointsColumns.CONTENT_URI_BY_ID, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO Set trackId in this method.
|
||||||
|
public int bulkInsertMarkers(List<Marker> markers, Track.Id trackId) {
|
||||||
|
ContentValues[] values = new ContentValues[markers.size()];
|
||||||
|
for (int i = 0; i < markers.size(); i++) {
|
||||||
|
values[i] = createContentValues(markers.get(i));
|
||||||
|
}
|
||||||
|
return contentResolver.bulkInsert(MarkerColumns.CONTENT_URI, values);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the last location id for a track.
|
* Gets the last location id for a track.
|
||||||
* Returns -1L if it doesn't exist.
|
* Returns -1L if it doesn't exist.
|
||||||
|
|||||||
+210
-84
@@ -17,28 +17,38 @@
|
|||||||
package de.dennisguse.opentracks.io.file.importer;
|
package de.dennisguse.opentracks.io.file.importer;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.location.Location;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
|
|
||||||
import org.xml.sax.Attributes;
|
import org.xml.sax.Attributes;
|
||||||
|
import org.xml.sax.Locator;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
import org.xml.sax.helpers.DefaultHandler;
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.data.Distance;
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
import de.dennisguse.opentracks.content.data.Speed;
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.KMLTrackExporter;
|
import de.dennisguse.opentracks.io.file.exporter.KMLTrackExporter;
|
||||||
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports a KML file.
|
* Imports a KML file.
|
||||||
*
|
*
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
public class KmlFileTrackImporter extends DefaultHandler implements XMLImporter.TrackParser {
|
||||||
|
|
||||||
private static final String TAG = KmlFileTrackImporter.class.getSimpleName();
|
private static final String TAG = KmlFileTrackImporter.class.getSimpleName();
|
||||||
|
|
||||||
@@ -64,29 +74,50 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
|
|
||||||
private static final String ATTRIBUTE_NAME = "name";
|
private static final String ATTRIBUTE_NAME = "name";
|
||||||
|
|
||||||
private boolean trackStarted = false;
|
private Locator locator;
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
// Belongs to the current track
|
||||||
|
private final ArrayList<Instant> whenList = new ArrayList<>();
|
||||||
|
private final ArrayList<Location> locationList = new ArrayList<>();
|
||||||
|
|
||||||
private String extendedDataType;
|
private String extendedDataType;
|
||||||
private final ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
private final ArrayList<Float> sensorSpeedList = new ArrayList<>();
|
||||||
private final ArrayList<Float> speedList = new ArrayList<>();
|
private final ArrayList<Float> sensorDistanceList = new ArrayList<>();
|
||||||
private final ArrayList<Float> distanceList = new ArrayList<>();
|
private final ArrayList<Float> sensorCadenceList = new ArrayList<>();
|
||||||
private final ArrayList<Float> cadenceList = new ArrayList<>();
|
private final ArrayList<Float> sensorHeartRateList = new ArrayList<>();
|
||||||
private final ArrayList<Float> heartRateList = new ArrayList<>();
|
private final ArrayList<Float> sensorPowerList = new ArrayList<>();
|
||||||
private final ArrayList<Float> powerList = new ArrayList<>();
|
|
||||||
private final ArrayList<Float> altitudeGainList = new ArrayList<>();
|
private final ArrayList<Float> altitudeGainList = new ArrayList<>();
|
||||||
private final ArrayList<Float> altitudeLossList = new ArrayList<>();
|
private final ArrayList<Float> altitudeLossList = new ArrayList<>();
|
||||||
|
|
||||||
|
private final ArrayList<Marker> markers = new ArrayList<>();
|
||||||
|
|
||||||
|
// The current element content
|
||||||
|
private String content = "";
|
||||||
|
|
||||||
|
private String icon;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String category;
|
||||||
|
private String latitude;
|
||||||
|
private String longitude;
|
||||||
|
private String altitude;
|
||||||
|
private String markerType;
|
||||||
|
private String photoUrl;
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
private final TrackImporter trackImporter;
|
||||||
|
|
||||||
public KmlFileTrackImporter(Context context) {
|
public KmlFileTrackImporter(Context context) {
|
||||||
this(context, new ContentProviderUtils(context));
|
this.context = context;
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
//TODO move this to instantiation of this class
|
||||||
KmlFileTrackImporter(Context context, ContentProviderUtils contentProviderUtils) {
|
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
super(context, contentProviderUtils);
|
Distance maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(sharedPreferences, context);
|
||||||
}
|
Distance recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context);
|
||||||
|
boolean preventReimport = PreferencesUtils.getPreventReimportTracks(sharedPreferences, context);
|
||||||
@Override
|
this.trackImporter = new TrackImporter(context, new ContentProviderUtils(context), recordingDistanceInterval, maxRecordingDistance, preventReimport);
|
||||||
public DefaultHandler getHandler() {
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -98,11 +129,10 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
onMarkerStart();
|
onMarkerStart();
|
||||||
break;
|
break;
|
||||||
case TAG_GX_MULTI_TRACK:
|
case TAG_GX_MULTI_TRACK:
|
||||||
trackStarted = true;
|
trackImporter.newTrack();
|
||||||
onTrackStart();
|
|
||||||
break;
|
break;
|
||||||
case TAG_GX_TRACK:
|
case TAG_GX_TRACK:
|
||||||
if (!trackStarted) {
|
if (trackImporter == null) {
|
||||||
throw new SAXException("No " + TAG_GX_MULTI_TRACK);
|
throw new SAXException("No " + TAG_GX_MULTI_TRACK);
|
||||||
}
|
}
|
||||||
onTrackSegmentStart();
|
onTrackSegmentStart();
|
||||||
@@ -113,11 +143,16 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void characters(char[] ch, int start, int length) {
|
||||||
|
content += new String(ch, start, length);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endElement(String uri, String localName, String tag) throws SAXException {
|
public void endElement(String uri, String localName, String tag) throws SAXException {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case TAG_KML:
|
case TAG_KML:
|
||||||
onFileEnd();
|
onFileEnded();
|
||||||
break;
|
break;
|
||||||
case TAG_PLACEMARK:
|
case TAG_PLACEMARK:
|
||||||
case TAG_PHOTO_OVERLAY:
|
case TAG_PHOTO_OVERLAY:
|
||||||
@@ -134,7 +169,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
onTrackSegmentEnd();
|
onTrackSegmentEnd();
|
||||||
break;
|
break;
|
||||||
case TAG_GX_COORD:
|
case TAG_GX_COORD:
|
||||||
onTrackPointEnd();
|
onCoordEnded();
|
||||||
break;
|
break;
|
||||||
case TAG_GX_VALUE:
|
case TAG_GX_VALUE:
|
||||||
onExtendedDataValueEnd();
|
onExtendedDataValueEnd();
|
||||||
@@ -166,7 +201,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
break;
|
break;
|
||||||
case TAG_WHEN:
|
case TAG_WHEN:
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
time = content.trim();
|
whenList.add(StringUtils.parseTime(content.trim()));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -183,7 +218,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset element content
|
// Reset element content
|
||||||
content = null;
|
content = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMarkerStart() {
|
private void onMarkerStart() {
|
||||||
@@ -196,7 +231,6 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
latitude = null;
|
latitude = null;
|
||||||
longitude = null;
|
longitude = null;
|
||||||
altitude = null;
|
altitude = null;
|
||||||
time = null;
|
|
||||||
markerType = null;
|
markerType = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +238,30 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
if (!MARKER_STYLE.equals(markerType)) {
|
if (!MARKER_STYLE.equals(markerType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addMarker();
|
|
||||||
|
if (whenList.size() != 1) {
|
||||||
|
Log.w(TAG, "Marker without time ignored.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Location location = createLocation(longitude, latitude, altitude);
|
||||||
|
if (location == null) {
|
||||||
|
Log.w(TAG, "Marker with invalid coordinates ignored: " + location);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Marker marker = new Marker(null, new TrackPoint(TrackPoint.Type.TRACKPOINT, location, whenList.get(0))); //TODO Creating marker without need
|
||||||
|
marker.setName(name != null ? name : "");
|
||||||
|
marker.setDescription(description != null ? description : "");
|
||||||
|
marker.setCategory(category != null ? category : "");
|
||||||
|
marker.setPhotoUrl(photoUrl);
|
||||||
|
markers.add(marker);
|
||||||
|
|
||||||
|
name = null;
|
||||||
|
description = null;
|
||||||
|
category = null;
|
||||||
|
photoUrl = null;
|
||||||
|
whenList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMarkerLocationEnd() {
|
private void onMarkerLocationEnd() {
|
||||||
@@ -219,39 +276,56 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void onTrackSegmentStart() {
|
||||||
protected void onTrackSegmentStart() {
|
locationList.clear();
|
||||||
super.onTrackSegmentStart();
|
whenList.clear();
|
||||||
trackPoints.clear();
|
|
||||||
speedList.clear();
|
sensorSpeedList.clear();
|
||||||
distanceList.clear();
|
sensorDistanceList.clear();
|
||||||
heartRateList.clear();
|
sensorHeartRateList.clear();
|
||||||
cadenceList.clear();
|
sensorCadenceList.clear();
|
||||||
powerList.clear();
|
sensorPowerList.clear();
|
||||||
altitudeGainList.clear();
|
altitudeGainList.clear();
|
||||||
altitudeLossList.clear();
|
altitudeLossList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onTrackSegmentEnd() {
|
private void onTrackSegmentEnd() {
|
||||||
super.onTrackSegmentEnd();
|
if (locationList.size() != whenList.size()) {
|
||||||
// Close a track segment by inserting the segment locations
|
throw new ImportParserException("<coords> and <when> should have the same count.");
|
||||||
for (int i = 0; i < trackPoints.size(); i++) {
|
}
|
||||||
TrackPoint trackPoint = trackPoints.get(i);
|
|
||||||
|
|
||||||
if (i < speedList.size() && speedList.get(i) != null) {
|
// Close a track segment by inserting the segment locations
|
||||||
trackPoint.setSpeed(Speed.of(speedList.get(i)));
|
for (int i = 0; i < locationList.size(); i++) {
|
||||||
|
Instant time = whenList.get(i);
|
||||||
|
Location location = locationList.get(i);
|
||||||
|
|
||||||
|
TrackPoint trackPoint;
|
||||||
|
if (i == 0) {
|
||||||
|
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) {
|
||||||
|
trackPoint = TrackPoint.createSegmentEndWithTime(time);
|
||||||
|
} else {
|
||||||
|
trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, location, time);
|
||||||
}
|
}
|
||||||
if (i < distanceList.size() && distanceList.get(i) != null) {
|
|
||||||
trackPoint.setSensorDistance(Distance.of(distanceList.get(i)));
|
if (i < sensorSpeedList.size() && sensorSpeedList.get(i) != null) {
|
||||||
|
trackPoint.setSpeed(Speed.of(sensorSpeedList.get(i)));
|
||||||
}
|
}
|
||||||
if (i < heartRateList.size()) {
|
if (i < sensorDistanceList.size() && sensorDistanceList.get(i) != null) {
|
||||||
trackPoint.setHeartRate_bpm(heartRateList.get(i));
|
trackPoint.setSensorDistance(Distance.of(sensorDistanceList.get(i)));
|
||||||
}
|
}
|
||||||
if (i < cadenceList.size()) {
|
if (i < sensorHeartRateList.size()) {
|
||||||
trackPoint.setCyclingCadence_rpm(cadenceList.get(i));
|
trackPoint.setHeartRate_bpm(sensorHeartRateList.get(i));
|
||||||
}
|
}
|
||||||
if (i < powerList.size()) {
|
if (i < sensorCadenceList.size()) {
|
||||||
trackPoint.setPower(powerList.get(i));
|
trackPoint.setCyclingCadence_rpm(sensorCadenceList.get(i));
|
||||||
|
}
|
||||||
|
if (i < sensorPowerList.size()) {
|
||||||
|
trackPoint.setPower(sensorPowerList.get(i));
|
||||||
}
|
}
|
||||||
if (i < altitudeGainList.size()) {
|
if (i < altitudeGainList.size()) {
|
||||||
trackPoint.setAltitudeGain(altitudeGainList.get(i));
|
trackPoint.setAltitudeGain(altitudeGainList.get(i));
|
||||||
@@ -260,18 +334,11 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
trackPoint.setAltitudeLoss(altitudeLossList.get(i));
|
trackPoint.setAltitudeLoss(altitudeLossList.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
insertTrackPoint(trackPoint);
|
trackImporter.addTrackPoint(trackPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void onCoordEnded() {
|
||||||
* On track point end. gx:coord end tag.
|
|
||||||
*/
|
|
||||||
private void onTrackPointEnd() {
|
|
||||||
// Add trackPoint to trackPoints
|
|
||||||
if (content == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String[] parts = content.trim().split(" ");
|
String[] parts = content.trim().split(" ");
|
||||||
if (parts.length == 2 || parts.length == 3) {
|
if (parts.length == 2 || parts.length == 3) {
|
||||||
longitude = parts[0];
|
longitude = parts[0];
|
||||||
@@ -279,33 +346,41 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
altitude = parts.length == 3 ? parts[2] : null;
|
altitude = parts.length == 3 ? parts[2] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Similar to GPX
|
locationList.add(createLocation(longitude, latitude, altitude));
|
||||||
boolean isFirstTrackPointInSegment = isFirstTrackPointInSegment();
|
|
||||||
TrackPoint trackPoint = getTrackPoint();
|
|
||||||
if (isFirstTrackPointInSegment) {
|
|
||||||
TrackPoint.Type type = !trackPoint.hasLocation() ? TrackPoint.Type.SEGMENT_START_MANUAL : TrackPoint.Type.SEGMENT_START_AUTOMATIC;
|
|
||||||
|
|
||||||
trackPoint.setType(type);
|
|
||||||
}
|
|
||||||
trackPoints.add(trackPoint);
|
|
||||||
|
|
||||||
// Reset variables for next trackpoint (which might not have such data).
|
|
||||||
time = null;
|
|
||||||
longitude = null;
|
longitude = null;
|
||||||
latitude = null;
|
latitude = null;
|
||||||
altitude = null;
|
altitude = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private Location createLocation(String longitude, String latitude, String altitude) {
|
||||||
* On extended data start. gx:SimpleArrayData start tag.
|
Location location = null;
|
||||||
*/
|
if (longitude != null || latitude != null) {
|
||||||
|
location = new Location("import");
|
||||||
|
|
||||||
|
try {
|
||||||
|
location.setLatitude(Double.parseDouble(latitude));
|
||||||
|
location.setLongitude(Double.parseDouble(longitude));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new AbstractFileTrackImporter.ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse latitude longitude: %s %s", latitude, longitude)), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (altitude != null) {
|
||||||
|
try {
|
||||||
|
location.setAltitude(Double.parseDouble(altitude));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new AbstractFileTrackImporter.ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse altitude: %s", altitude)), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void onExtendedDataStart(Attributes attributes) {
|
private void onExtendedDataStart(Attributes attributes) {
|
||||||
extendedDataType = attributes.getValue(ATTRIBUTE_NAME);
|
extendedDataType = attributes.getValue(ATTRIBUTE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* On extended data value end. gx:value end tag.
|
|
||||||
*/
|
|
||||||
private void onExtendedDataValueEnd() throws SAXException {
|
private void onExtendedDataValueEnd() throws SAXException {
|
||||||
Float value = null;
|
Float value = null;
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
@@ -320,19 +395,19 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
}
|
}
|
||||||
switch (extendedDataType) {
|
switch (extendedDataType) {
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED:
|
||||||
speedList.add(value);
|
sensorSpeedList.add(value);
|
||||||
break;
|
break;
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_DISTANCE:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_DISTANCE:
|
||||||
distanceList.add(value);
|
sensorDistanceList.add(value);
|
||||||
break;
|
break;
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER:
|
||||||
powerList.add(value);
|
sensorPowerList.add(value);
|
||||||
break;
|
break;
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_HEART_RATE:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_HEART_RATE:
|
||||||
heartRateList.add(value);
|
sensorHeartRateList.add(value);
|
||||||
break;
|
break;
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE:
|
||||||
cadenceList.add(value);
|
sensorCadenceList.add(value);
|
||||||
break;
|
break;
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_GAIN:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_GAIN:
|
||||||
altitudeGainList.add(value);
|
altitudeGainList.add(value);
|
||||||
@@ -344,4 +419,55 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
Log.w(TAG, "Data from extended data " + extendedDataType + " is not (yet) supported.");
|
Log.w(TAG, "Data from extended data " + extendedDataType + " is not (yet) supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onTrackEnd() {
|
||||||
|
Track track = new Track();
|
||||||
|
track.setName(name != null ? name : "");
|
||||||
|
|
||||||
|
try {
|
||||||
|
track.setUuid(UUID.fromString(uuid));
|
||||||
|
} catch (IllegalArgumentException | NullPointerException e) {
|
||||||
|
Log.w(TAG, "could not parse Track UUID, generating a new one.");
|
||||||
|
track.setUuid(UUID.randomUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
track.setDescription(description != null ? description : "");
|
||||||
|
|
||||||
|
if (category != null) {
|
||||||
|
track.setCategory(category);
|
||||||
|
|
||||||
|
if (icon == null) {
|
||||||
|
//TODO exporting/importing icon is not implemented.
|
||||||
|
icon = TrackIconUtils.getIconValue(context, category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
track.setIcon(icon != null ? icon : "");
|
||||||
|
|
||||||
|
trackImporter.setTrack(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createErrorMessage(String message) {
|
||||||
|
return String.format(Locale.US, "Parsing error at line: %d column: %d. %s", locator.getLineNumber(), locator.getColumnNumber(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFileEnded() {
|
||||||
|
trackImporter.addMarkers(markers);
|
||||||
|
trackImporter.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DefaultHandler getHandler() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Track.Id> getImportTrackIds() {
|
||||||
|
return trackImporter.getTrackIds();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanImport() {
|
||||||
|
trackImporter.cleanImport();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,267 @@
|
|||||||
|
package de.dennisguse.opentracks.io.file.importer;
|
||||||
|
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||||
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
|
import de.dennisguse.opentracks.util.LocationUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles logic to import:
|
||||||
|
* 1. addTrackPoints()
|
||||||
|
* 2. addMarkers();
|
||||||
|
* 3. setTrack();
|
||||||
|
* 4. newTrack(); //stores current track to databse
|
||||||
|
* 5. if needed go to 1.
|
||||||
|
* 6. finish()
|
||||||
|
* <p>
|
||||||
|
* NOTE: This class modifies the parameter.
|
||||||
|
* Do not re-use these objects anywhere else.
|
||||||
|
*/
|
||||||
|
public class TrackImporter {
|
||||||
|
|
||||||
|
private static final String TAG = TrackImporter.class.getSimpleName();
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
|
private final Distance recordingDistanceInterval;
|
||||||
|
private final Distance maxRecordingDistance;
|
||||||
|
private final boolean preventReimport;
|
||||||
|
|
||||||
|
private final List<Track.Id> trackIds = new ArrayList<>();
|
||||||
|
|
||||||
|
// Current track
|
||||||
|
private Track track;
|
||||||
|
private final List<TrackPoint> trackPoints = new LinkedList<>();
|
||||||
|
private final List<Marker> markers = new LinkedList<>();
|
||||||
|
|
||||||
|
public TrackImporter(Context context, ContentProviderUtils contentProviderUtils, Distance recordingDistanceInterval, Distance maxRecordingDistance, boolean preventReimport) {
|
||||||
|
this.context = context;
|
||||||
|
this.contentProviderUtils = contentProviderUtils;
|
||||||
|
this.recordingDistanceInterval = recordingDistanceInterval;
|
||||||
|
this.maxRecordingDistance = maxRecordingDistance;
|
||||||
|
this.preventReimport = preventReimport;
|
||||||
|
}
|
||||||
|
|
||||||
|
void newTrack() {
|
||||||
|
if (track != null) {
|
||||||
|
finishTrack();
|
||||||
|
}
|
||||||
|
|
||||||
|
track = null;
|
||||||
|
trackPoints.clear();
|
||||||
|
markers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addTrackPoint(TrackPoint trackPoint) {
|
||||||
|
this.trackPoints.add(trackPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addTrackPoints(List<TrackPoint> trackPoints) {
|
||||||
|
this.trackPoints.addAll(trackPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addMarker(Marker marker) {
|
||||||
|
this.markers.add(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addMarkers(List<Marker> markers) {
|
||||||
|
this.markers.addAll(markers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTrack(Track track) {
|
||||||
|
this.track = track;
|
||||||
|
}
|
||||||
|
|
||||||
|
void finish() {
|
||||||
|
if (track != null) {
|
||||||
|
finishTrack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finishTrack() {
|
||||||
|
if (trackPoints.isEmpty()) {
|
||||||
|
throw new ImportParserException("Cannot import track without any locations.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store Track
|
||||||
|
if (contentProviderUtils.getTrack(track.getUuid()) != null) {
|
||||||
|
if (preventReimport) {
|
||||||
|
throw new ImportAlreadyExistsException(context.getString(R.string.import_prevent_reimport));
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO This is a workaround until we have proper UI.
|
||||||
|
track.setUuid(UUID.randomUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackStatisticsUpdater updater = new TrackStatisticsUpdater();
|
||||||
|
updater.addTrackPoints(trackPoints, recordingDistanceInterval);
|
||||||
|
track.setTrackStatistics(updater.getTrackStatistics());
|
||||||
|
|
||||||
|
Uri trackUri = contentProviderUtils.insertTrack(track);
|
||||||
|
Track.Id trackId = new Track.Id(ContentUris.parseId(trackUri));
|
||||||
|
|
||||||
|
// Store TrackPoints
|
||||||
|
adjustTrackPoints();
|
||||||
|
contentProviderUtils.bulkInsertTrackPoint(trackPoints, trackId);
|
||||||
|
|
||||||
|
// Store Markers
|
||||||
|
matchMarkers2TrackPoints(trackId);
|
||||||
|
for (Marker marker : markers)
|
||||||
|
marker.setTrackId(trackId); //TODO Should happen in bulkInsertMarkers
|
||||||
|
|
||||||
|
contentProviderUtils.bulkInsertMarkers(markers, trackId);
|
||||||
|
|
||||||
|
//Clear up.
|
||||||
|
trackPoints.clear();
|
||||||
|
markers.clear();
|
||||||
|
|
||||||
|
trackIds.add(trackId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If not present: calculate data from the previous trackPoint (if present)
|
||||||
|
* NOTE: Modifies content of trackPoints.
|
||||||
|
*/
|
||||||
|
private void adjustTrackPoints() {
|
||||||
|
for (int i = 0; i < trackPoints.size(); i++) {
|
||||||
|
TrackPoint current = trackPoints.get(i);
|
||||||
|
|
||||||
|
if (current.hasLocation()) {
|
||||||
|
Instant time = current.getTime();
|
||||||
|
if (current.getLatitude() == 100) {
|
||||||
|
//TODO Remove by 31st December 2021.
|
||||||
|
current = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||||
|
current.setTime(time);
|
||||||
|
} else if (current.getLatitude() == 200) {
|
||||||
|
//TODO Remove by 31st December 2021.
|
||||||
|
current = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL);
|
||||||
|
current.setTime(time);
|
||||||
|
} else if (!LocationUtils.isValidLocation(current.getLocation())) {
|
||||||
|
throw new ImportParserException("Invalid location detected: " + current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < trackPoints.size(); i++) {
|
||||||
|
TrackPoint previous = trackPoints.get(i - 1);
|
||||||
|
TrackPoint current = trackPoints.get(i);
|
||||||
|
|
||||||
|
Distance distanceToPrevious = current.distanceToPrevious(previous);
|
||||||
|
if (distanceToPrevious != null) {
|
||||||
|
if (!current.hasSpeed()) {
|
||||||
|
Duration timeDifference = Duration.between(previous.getTime(), current.getTime());
|
||||||
|
current.setSpeed(Speed.of(distanceToPrevious, timeDifference));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current.hasBearing()) {
|
||||||
|
current.setBearing(previous.bearingTo(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.getType().equals(TrackPoint.Type.TRACKPOINT) && distanceToPrevious.greaterThan(maxRecordingDistance)) {
|
||||||
|
current.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: Modifies content of markers (incl. removal).
|
||||||
|
*/
|
||||||
|
private void matchMarkers2TrackPoints(Track.Id trackId) {
|
||||||
|
List<TrackPoint> trackPointsWithLocation = trackPoints.stream()
|
||||||
|
.filter(TrackPoint::hasLocation)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<Marker> todoMarkers = new LinkedList<>(markers);
|
||||||
|
List<Marker> doneMarkers = new LinkedList<>();
|
||||||
|
|
||||||
|
for (final TrackPoint trackPoint : trackPointsWithLocation) {
|
||||||
|
if (todoMarkers.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackStatisticsUpdater updater = new TrackStatisticsUpdater();
|
||||||
|
updater.addTrackPoint(trackPoint, recordingDistanceInterval);
|
||||||
|
|
||||||
|
List<Marker> matchedMarkers = todoMarkers.stream()
|
||||||
|
.filter(it -> trackPoint.getLatitude() == it.getLatitude()
|
||||||
|
&& trackPoint.getLongitude() == it.getLongitude()
|
||||||
|
&& trackPoint.getTime().equals(it.getTime())
|
||||||
|
)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
TrackStatistics statistics = updater.getTrackStatistics();
|
||||||
|
for (Marker marker : matchedMarkers) {
|
||||||
|
if (marker.hasPhoto()) {
|
||||||
|
marker.setPhotoUrl(getInternalPhotoUrl(trackId, marker.getPhotoUrl()));
|
||||||
|
}
|
||||||
|
|
||||||
|
marker.setIcon(context.getString(R.string.marker_icon_url)); //TODO Why?
|
||||||
|
|
||||||
|
marker.setLength(statistics.getTotalDistance());
|
||||||
|
marker.setDuration(statistics.getTotalTime());
|
||||||
|
|
||||||
|
marker.setTrackPoint(trackPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
todoMarkers.removeAll(matchedMarkers);
|
||||||
|
doneMarkers.addAll(matchedMarkers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (todoMarkers.isEmpty()) {
|
||||||
|
Log.w(TAG, "Some markers could not be attached to TrackPoints; those are not imported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
markers.clear();
|
||||||
|
markers.addAll(doneMarkers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the photo url for a file.
|
||||||
|
*
|
||||||
|
* @param externalPhotoUrl the file name
|
||||||
|
*/
|
||||||
|
private String getInternalPhotoUrl(@NonNull Track.Id trackId, @NonNull String externalPhotoUrl) {
|
||||||
|
String importFileName = KmzTrackImporter.importNameForFilename(externalPhotoUrl);
|
||||||
|
File file = FileUtils.buildInternalPhotoFile(context, trackId, Uri.parse(importFileName));
|
||||||
|
if (file != null) {
|
||||||
|
Uri photoUri = FileUtils.getUriForFile(context, file);
|
||||||
|
return "" + photoUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Track.Id> getTrackIds() {
|
||||||
|
return Collections.unmodifiableList(trackIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanImport() {
|
||||||
|
contentProviderUtils.deleteTracks(context, trackIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,6 +58,7 @@ public class XMLImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface TrackParser {
|
interface TrackParser {
|
||||||
|
@Deprecated
|
||||||
DefaultHandler getHandler();
|
DefaultHandler getHandler();
|
||||||
|
|
||||||
List<Track.Id> getImportTrackIds();
|
List<Track.Id> getImportTrackIds();
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.data.Distance;
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.content.data.Speed;
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
@@ -98,10 +99,13 @@ public class TrackStatisticsUpdater {
|
|||||||
return trackInitialized;
|
return trackInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addTrackPoints(List<TrackPoint> trackPoints, Distance minGPSDistance) {
|
||||||
|
for (TrackPoint tp : trackPoints) {
|
||||||
|
addTrackPoint(tp, minGPSDistance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a trackPoint.
|
|
||||||
*
|
|
||||||
* @param trackPoint the trackPoint
|
|
||||||
* @param minGPSDistance the min recording distance
|
* @param minGPSDistance the min recording distance
|
||||||
*/
|
*/
|
||||||
public void addTrackPoint(TrackPoint trackPoint, Distance minGPSDistance) {
|
public void addTrackPoint(TrackPoint trackPoint, Distance minGPSDistance) {
|
||||||
|
|||||||
Reference in New Issue
Block a user