forked from upstream-mirrors/OpenTracks
GPX: only export TrackPoints with lat/lng. (#591)
GPX: only export TrackPoints with lat/lng. Thus, segmentation (manual/automatic) cannot be restored on import. This also affects the TrackStatistics.
This commit is contained in:
+2
-2
@@ -17,7 +17,7 @@ import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class KmlTrackWriterTest {
|
public class KmlTrackExporterTest {
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ public class KmlTrackWriterTest {
|
|||||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
|
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
|
||||||
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
KmlTrackWriter kmlTrackWriter = (KmlTrackWriter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.newTrackWriter(context);
|
KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||||
kmlTrackWriter.prepare(outputStream);
|
kmlTrackWriter.prepare(outputStream);
|
||||||
|
|
||||||
kmlTrackWriter.writeTrackPoint(trackPoint);
|
kmlTrackWriter.writeTrackPoint(trackPoint);
|
||||||
+35
-27
@@ -27,6 +27,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
@@ -67,8 +68,6 @@ public class ExportImportTest {
|
|||||||
if (Looper.myLooper() == null) Looper.prepare();
|
if (Looper.myLooper() == null) Looper.prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TrackRecordingServiceInterface service;
|
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||||
@@ -85,8 +84,8 @@ public class ExportImportTest {
|
|||||||
private Track.Id importTrackId;
|
private Track.Id importTrackId;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws TimeoutException, InterruptedException {
|
public void setUp() throws TimeoutException {
|
||||||
service = ((TrackRecordingServiceInterface) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)));
|
||||||
|
|
||||||
trackId = service.startNewTrack();
|
trackId = service.startNewTrack();
|
||||||
|
|
||||||
@@ -138,7 +137,7 @@ public class ExportImportTest {
|
|||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL.newTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL.createTrackExporter(context);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -162,10 +161,10 @@ public class ExportImportTest {
|
|||||||
assertEquals(track.getUuid(), importedTrack.getUuid());
|
assertEquals(track.getUuid(), importedTrack.getUuid());
|
||||||
|
|
||||||
// 2. trackpoints
|
// 2. trackpoints
|
||||||
assertTrackpoints(false, false, false, false, false);
|
assertTrackpoints(trackPoints, false, false, false, false, false);
|
||||||
|
|
||||||
// 3. trackstatistics
|
// 3. trackstatistics
|
||||||
assertTrackStatistics(false);
|
assertTrackStatistics(false, false);
|
||||||
|
|
||||||
// 4. markers
|
// 4. markers
|
||||||
assertMarkers();
|
assertMarkers();
|
||||||
@@ -177,7 +176,7 @@ public class ExportImportTest {
|
|||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.newTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -200,10 +199,10 @@ public class ExportImportTest {
|
|||||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||||
|
|
||||||
// 2. trackpoints
|
// 2. trackpoints
|
||||||
assertTrackpoints(true, true, true, true, true);
|
assertTrackpoints(trackPoints, true, true, true, true, true);
|
||||||
|
|
||||||
// 2. trackstatistics
|
// 2. trackstatistics
|
||||||
assertTrackStatistics(true);
|
assertTrackStatistics(false, true);
|
||||||
|
|
||||||
// 4. markers
|
// 4. markers
|
||||||
assertMarkers();
|
assertMarkers();
|
||||||
@@ -216,7 +215,7 @@ public class ExportImportTest {
|
|||||||
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.newTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -271,7 +270,7 @@ public class ExportImportTest {
|
|||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.GPX.newTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -296,10 +295,16 @@ public class ExportImportTest {
|
|||||||
//assertEquals(track.getIcon(), trackImported.getIcon());
|
//assertEquals(track.getIcon(), trackImported.getIcon());
|
||||||
|
|
||||||
// 2. trackpoints
|
// 2. trackpoints
|
||||||
assertTrackpoints(false, true, true, true, true);
|
// The GPX exporter does not support exporting TrackPoints without lat/lng.
|
||||||
|
// Therefore, the track segmentation is changes.
|
||||||
|
List<TrackPoint> 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(3).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
|
||||||
|
assertTrackpoints(trackPointsWithCoordinates, false, true, true, true, true);
|
||||||
|
|
||||||
// 3. trackstatistics
|
// 3. trackstatistics
|
||||||
assertTrackStatistics(true);
|
assertTrackStatistics(true, true);
|
||||||
|
|
||||||
// 4. markers
|
// 4. markers
|
||||||
assertMarkers();
|
assertMarkers();
|
||||||
@@ -312,7 +317,7 @@ public class ExportImportTest {
|
|||||||
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.GPX.newTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -349,8 +354,9 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain, boolean verifyElevationLoss) {
|
private void assertTrackpoints(List<TrackPoint> trackPoints, boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain, boolean verifyElevationLoss) {
|
||||||
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
||||||
|
|
||||||
assertEquals(trackPoints.size(), importedTrackPoints.size());
|
assertEquals(trackPoints.size(), importedTrackPoints.size());
|
||||||
|
|
||||||
for (int i = 0; i < trackPoints.size(); i++) {
|
for (int i = 0; i < trackPoints.size(); i++) {
|
||||||
@@ -402,7 +408,7 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertTrackStatistics(boolean verifyElevationGainAndLoss) {
|
private void assertTrackStatistics(boolean isGpx, boolean verifyElevationGainAndLoss) {
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
|
|
||||||
assertNotNull(importedTrack.getTrackStatistics());
|
assertNotNull(importedTrack.getTrackStatistics());
|
||||||
@@ -412,19 +418,21 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
// Time
|
// Time
|
||||||
assertTrue(trackStatistics.getStartTime().isBefore(trackStatistics.getStopTime())); //Just to be sure.
|
assertTrue(trackStatistics.getStartTime().isBefore(trackStatistics.getStopTime())); //Just to be sure.
|
||||||
assertEquals(trackStatistics.getStartTime(), importedTrackStatistics.getStartTime());
|
if (!isGpx) {
|
||||||
assertEquals(trackStatistics.getStopTime(), importedTrackStatistics.getStopTime());
|
assertEquals(trackStatistics.getStartTime(), importedTrackStatistics.getStartTime());
|
||||||
|
assertEquals(trackStatistics.getStopTime(), importedTrackStatistics.getStopTime());
|
||||||
|
|
||||||
assertEquals(trackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
|
assertEquals(trackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
|
||||||
assertEquals(trackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
assertEquals(trackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
||||||
|
|
||||||
// Distance
|
// Distance
|
||||||
assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance(), 0.01);
|
assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance(), 0.01);
|
||||||
|
|
||||||
// Speed
|
// Speed
|
||||||
assertEquals(trackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed(), 0.01);
|
assertEquals(trackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed(), 0.01);
|
||||||
assertEquals(trackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed(), 0.01);
|
assertEquals(trackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed(), 0.01);
|
||||||
assertEquals(trackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed(), 0.01);
|
assertEquals(trackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed(), 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
// Elevation
|
// Elevation
|
||||||
assertEquals(trackStatistics.getMinElevation(), importedTrackStatistics.getMinElevation(), 0.01);
|
assertEquals(trackStatistics.getMinElevation(), importedTrackStatistics.getMinElevation(), 0.01);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import java.util.Objects;
|
|||||||
* Drawbacks:
|
* Drawbacks:
|
||||||
* * GPS-provided timestamp might be more precise (but also have GPS week rollover)
|
* * GPS-provided timestamp might be more precise (but also have GPS week rollover)
|
||||||
* * System clock might be changed (and thus non-monotonic)
|
* * System clock might be changed (and thus non-monotonic)
|
||||||
* TODO: if these might be problems, we need to store both timestamps.
|
* TODO: if these might be a problem, we need to store both timestamps.
|
||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
@@ -177,7 +177,7 @@ public class TrackPoint {
|
|||||||
return latitude != null || longitude != null;
|
return latitude != null || longitude != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@NonNull
|
||||||
public Location getLocation() {
|
public Location getLocation() {
|
||||||
Location location = new Location("");
|
Location location = new Location("");
|
||||||
location.setTime(time.toEpochMilli());
|
location.setTime(time.toEpochMilli());
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ public class ShareContentProvider extends CustomContentProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final TrackExporter trackExporter = getTrackFileFormat(uri).newTrackExporter(getContext());
|
final TrackExporter trackExporter = getTrackFileFormat(uri).createTrackExporter(getContext());
|
||||||
|
|
||||||
PipeDataWriter<String> pipeDataWriter = (output, uri1, mimeType, opts, args) -> {
|
PipeDataWriter<String> pipeDataWriter = (output, uri1, mimeType, opts, args) -> {
|
||||||
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
|
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
|
||||||
|
|||||||
@@ -6,12 +6,10 @@ import java.util.Locale;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.FileTrackExporter;
|
import de.dennisguse.opentracks.io.file.exporter.GPXTrackExporter;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.GpxTrackWriter;
|
import de.dennisguse.opentracks.io.file.exporter.KMLTrackExporter;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.KmlTrackWriter;
|
|
||||||
import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter;
|
import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.TrackWriter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of all possible track formats.
|
* Definition of all possible track formats.
|
||||||
@@ -20,8 +18,8 @@ public enum TrackFileFormat {
|
|||||||
|
|
||||||
KML_ONLY_TRACK {
|
KML_ONLY_TRACK {
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context) {
|
public TrackExporter createTrackExporter(Context context) {
|
||||||
return new KmlTrackWriter(context, false, false, false);
|
return new KMLTrackExporter(context, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -35,8 +33,8 @@ public enum TrackFileFormat {
|
|||||||
},
|
},
|
||||||
KML_WITH_TRACKDETAIL {
|
KML_WITH_TRACKDETAIL {
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context) {
|
public TrackExporter createTrackExporter(Context context) {
|
||||||
return new KmlTrackWriter(context, true, false, false);
|
return new KMLTrackExporter(context, true, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -50,8 +48,8 @@ public enum TrackFileFormat {
|
|||||||
},
|
},
|
||||||
KML_WITH_TRACKDETAIL_AND_SENSORDATA {
|
KML_WITH_TRACKDETAIL_AND_SENSORDATA {
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context) {
|
public TrackExporter createTrackExporter(Context context) {
|
||||||
return new KmlTrackWriter(context, true, true, false);
|
return new KMLTrackExporter(context, true, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -64,16 +62,12 @@ public enum TrackFileFormat {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
KMZ_ONLY_TRACK {
|
KMZ_ONLY_TRACK {
|
||||||
|
|
||||||
private static final boolean exportPhotos = false;
|
private static final boolean exportPhotos = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context) {
|
public TrackExporter createTrackExporter(Context context) {
|
||||||
return new KmlTrackWriter(context, false, false, exportPhotos);
|
KMLTrackExporter exporter = new KMLTrackExporter(context, false, false, exportPhotos);
|
||||||
}
|
return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos);
|
||||||
|
|
||||||
public TrackExporter newTrackExporter(Context context) {
|
|
||||||
return newKmzTrackExporter(context, this.newTrackWriter(context), exportPhotos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -95,12 +89,9 @@ public enum TrackFileFormat {
|
|||||||
private static final boolean exportPhotos = false;
|
private static final boolean exportPhotos = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context) {
|
public TrackExporter createTrackExporter(Context context) {
|
||||||
return new KmlTrackWriter(context, true, false, exportPhotos);
|
KMLTrackExporter exporter = new KMLTrackExporter(context, true, false, exportPhotos);
|
||||||
}
|
return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos);
|
||||||
|
|
||||||
public TrackExporter newTrackExporter(Context context) {
|
|
||||||
return newKmzTrackExporter(context, this.newTrackWriter(context), exportPhotos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -123,8 +114,9 @@ public enum TrackFileFormat {
|
|||||||
private static final boolean exportPhotos = false;
|
private static final boolean exportPhotos = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context) {
|
public TrackExporter createTrackExporter(Context context) {
|
||||||
return new KmlTrackWriter(context, true, true, exportPhotos);
|
KMLTrackExporter exporter = new KMLTrackExporter(context, true, true, exportPhotos);
|
||||||
|
return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -132,10 +124,6 @@ public enum TrackFileFormat {
|
|||||||
return MIME_KMZ;
|
return MIME_KMZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackExporter newTrackExporter(Context context) {
|
|
||||||
return newKmzTrackExporter(context, this.newTrackWriter(context), exportPhotos);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getExtension() {
|
public String getExtension() {
|
||||||
return "kmz";
|
return "kmz";
|
||||||
}
|
}
|
||||||
@@ -152,8 +140,9 @@ public enum TrackFileFormat {
|
|||||||
private static final boolean exportPhotos = true;
|
private static final boolean exportPhotos = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context) {
|
public TrackExporter createTrackExporter(Context context) {
|
||||||
return new KmlTrackWriter(context, true, true, exportPhotos);
|
KMLTrackExporter exporter = new KMLTrackExporter(context, true, true, exportPhotos);
|
||||||
|
return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -161,10 +150,6 @@ public enum TrackFileFormat {
|
|||||||
return MIME_KMZ;
|
return MIME_KMZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackExporter newTrackExporter(Context context) {
|
|
||||||
return newKmzTrackExporter(context, newTrackWriter(context), exportPhotos);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getExtension() {
|
public String getExtension() {
|
||||||
return "kmz";
|
return "kmz";
|
||||||
}
|
}
|
||||||
@@ -177,8 +162,8 @@ public enum TrackFileFormat {
|
|||||||
},
|
},
|
||||||
GPX {
|
GPX {
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context) {
|
public TrackExporter createTrackExporter(Context context) {
|
||||||
return new GpxTrackWriter(context.getString(R.string.app_name));
|
return new GPXTrackExporter(new ContentProviderUtils(context), context.getString(R.string.app_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -195,31 +180,17 @@ public enum TrackFileFormat {
|
|||||||
|
|
||||||
private static final String MIME_KML = "application/vnd.google-earth.kml+xml";
|
private static final String MIME_KML = "application/vnd.google-earth.kml+xml";
|
||||||
|
|
||||||
private static TrackExporter newKmzTrackExporter(Context context, TrackWriter trackWriter, boolean exportPhotos) {
|
|
||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
|
||||||
|
|
||||||
FileTrackExporter fileTrackExporter = new FileTrackExporter(contentProviderUtils, trackWriter);
|
|
||||||
|
|
||||||
return new KmzTrackExporter(context, contentProviderUtils, fileTrackExporter, exportPhotos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the mime type for each format.
|
* Returns the mime type for each format.
|
||||||
*/
|
*/
|
||||||
public abstract String getMimeType();
|
public abstract String getMimeType();
|
||||||
|
|
||||||
public TrackExporter newTrackExporter(Context context) {
|
|
||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
|
||||||
TrackWriter trackWriter = newTrackWriter(context);
|
|
||||||
return new FileTrackExporter(contentProviderUtils, trackWriter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new track writer for the format.
|
* Creates a new track writer for the format.
|
||||||
*
|
*
|
||||||
* @param context the context
|
* @param context the context
|
||||||
*/
|
*/
|
||||||
public abstract TrackWriter newTrackWriter(Context context);
|
public abstract TrackExporter createTrackExporter(Context context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the file extension for each format.
|
* Returns the file extension for each format.
|
||||||
@@ -234,7 +205,7 @@ public enum TrackFileFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of for each format.
|
* Returns the name for each format.
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return this.name().toLowerCase(Locale.US);
|
return this.name().toLowerCase(Locale.US);
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.dennisguse.opentracks.io.file.exporter;
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
|
||||||
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.content.provider.TrackPointIterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Track Writer for writing tracks to an {@link OutputStream}.
|
|
||||||
*
|
|
||||||
* @author Sandor Dornbush
|
|
||||||
* @author Rodrigo Damazio
|
|
||||||
*/
|
|
||||||
public class FileTrackExporter implements TrackExporter {
|
|
||||||
|
|
||||||
private static final String TAG = FileTrackExporter.class.getSimpleName();
|
|
||||||
|
|
||||||
private final ContentProviderUtils contentProviderUtils;
|
|
||||||
private final TrackWriter trackWriter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param contentProviderUtils the content provider utils
|
|
||||||
* @param trackWriter the track writer
|
|
||||||
*/
|
|
||||||
public FileTrackExporter(ContentProviderUtils contentProviderUtils, TrackWriter trackWriter) {
|
|
||||||
this.contentProviderUtils = contentProviderUtils;
|
|
||||||
this.trackWriter = trackWriter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
|
||||||
return writeTrack(new Track[]{track}, outputStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
|
||||||
try {
|
|
||||||
trackWriter.prepare(outputStream);
|
|
||||||
trackWriter.writeHeader(tracks);
|
|
||||||
for (Track track : tracks) {
|
|
||||||
writeMarkers(track);
|
|
||||||
}
|
|
||||||
boolean hasMultipleTracks = tracks.length > 1;
|
|
||||||
if (hasMultipleTracks) {
|
|
||||||
trackWriter.writeMultiTrackBegin();
|
|
||||||
}
|
|
||||||
//TODO Why use startTime of first track for the others?
|
|
||||||
Instant startTime = tracks[0].getTrackStatistics().getStartTime();
|
|
||||||
for (Track track : tracks) {
|
|
||||||
Duration offset = Duration.between(track.getTrackStatistics().getStartTime(), startTime);
|
|
||||||
writeLocations(track, offset);
|
|
||||||
}
|
|
||||||
if (hasMultipleTracks) {
|
|
||||||
trackWriter.writeMultiTrackEnd();
|
|
||||||
}
|
|
||||||
trackWriter.writeFooter();
|
|
||||||
trackWriter.close();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.e(TAG, "Thread interrupted", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeMarkers(Track track) throws InterruptedException {
|
|
||||||
/*
|
|
||||||
* TODO: Stream through the markers in chunks.
|
|
||||||
* I am leaving the number of markers very high which should not be a problem, because we don't try to load them into objects all at the same time.
|
|
||||||
*/
|
|
||||||
boolean hasMarkers = false;
|
|
||||||
try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId(), null, -1)) {
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
for (int i = 0; i < cursor.getCount(); i++) {
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new InterruptedException();
|
|
||||||
}
|
|
||||||
if (!hasMarkers) {
|
|
||||||
trackWriter.writeBeginMarkers(track);
|
|
||||||
hasMarkers = true;
|
|
||||||
}
|
|
||||||
Marker marker = contentProviderUtils.createMarker(cursor);
|
|
||||||
trackWriter.writeMarker(marker);
|
|
||||||
|
|
||||||
cursor.moveToNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasMarkers) {
|
|
||||||
trackWriter.writeEndMarkers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the locations.
|
|
||||||
*/
|
|
||||||
private void writeLocations(Track track, Duration offset) throws InterruptedException {
|
|
||||||
boolean wroteTrack = false;
|
|
||||||
boolean wroteSegment = false;
|
|
||||||
|
|
||||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) {
|
|
||||||
while (trackPointIterator.hasNext()) {
|
|
||||||
if (Thread.interrupted()) throw new InterruptedException();
|
|
||||||
|
|
||||||
TrackPoint trackPoint = trackPointIterator.next();
|
|
||||||
setLocationTime(trackPoint, offset);
|
|
||||||
|
|
||||||
if (!wroteTrack) {
|
|
||||||
trackWriter.writeBeginTrack(track, trackPoint);
|
|
||||||
wroteTrack = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean newSegment = TrackPoint.Type.SEGMENT_START_AUTOMATIC.equals(trackPoint.getType()) || TrackPoint.Type.SEGMENT_START_MANUAL.equals(trackPoint.getType());
|
|
||||||
if (newSegment) {
|
|
||||||
if (wroteSegment) trackWriter.writeCloseSegment();
|
|
||||||
trackWriter.writeOpenSegment();
|
|
||||||
trackWriter.writeTrackPoint(trackPoint);
|
|
||||||
wroteSegment = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (TrackPoint.Type.SEGMENT_END_MANUAL.equals(trackPoint.getType())) {
|
|
||||||
if (!wroteSegment) trackWriter.writeOpenSegment();
|
|
||||||
trackWriter.writeTrackPoint(trackPoint);
|
|
||||||
trackWriter.writeCloseSegment();
|
|
||||||
wroteSegment = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
trackWriter.writeTrackPoint(trackPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wroteSegment) {
|
|
||||||
// Should not be necessary as tracks should end with SEGMENT_END_MANUAL.
|
|
||||||
//Close the last segment
|
|
||||||
trackWriter.writeCloseSegment();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wroteTrack) {
|
|
||||||
TrackPoint lastValidTrackPoint = contentProviderUtils.getLastValidTrackPoint(track.getId());
|
|
||||||
setLocationTime(lastValidTrackPoint, offset);
|
|
||||||
trackWriter.writeEndTrack(track, lastValidTrackPoint);
|
|
||||||
} else {
|
|
||||||
// Write an empty track
|
|
||||||
trackWriter.writeBeginTrack(track, null);
|
|
||||||
trackWriter.writeEndTrack(track, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a trackPoint time.
|
|
||||||
*
|
|
||||||
* @param trackPoint the trackPoint
|
|
||||||
* @param offset the time offset
|
|
||||||
*/
|
|
||||||
//TODO Why?
|
|
||||||
private void setLocationTime(TrackPoint trackPoint, Duration offset) {
|
|
||||||
if (trackPoint != null) {
|
|
||||||
trackPoint.setTime(trackPoint.getTime().minus(offset));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+123
-37
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2008 Google Inc.
|
* Copyright 2011 Google Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -16,6 +16,11 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.io.file.exporter;
|
package de.dennisguse.opentracks.io.file.exporter;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
@@ -24,15 +29,21 @@ import java.util.Locale;
|
|||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
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.TrackPointIterator;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write track as GPX to a file.
|
* Convert {@link Track} incl. {@link Marker} and {@link TrackPoint} to KML.
|
||||||
|
* NOTE:
|
||||||
|
* * does not export {@link TrackPoint} without a latitude/longitude (not supported by GPX 1.1).
|
||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
//TODO Export markers
|
public class GPXTrackExporter implements TrackExporter {
|
||||||
public class GpxTrackWriter implements TrackWriter {
|
|
||||||
|
private static final String TAG = GPXTrackExporter.class.getSimpleName();
|
||||||
|
|
||||||
private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US);
|
private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||||
private static final NumberFormat COORDINATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
private static final NumberFormat COORDINATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||||
@@ -62,19 +73,106 @@ public class GpxTrackWriter implements TrackWriter {
|
|||||||
CADENCE_FORMAT.setGroupingUsed(false);
|
CADENCE_FORMAT.setGroupingUsed(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
private final String creator;
|
private final String creator;
|
||||||
private PrintWriter printWriter;
|
private PrintWriter printWriter;
|
||||||
|
|
||||||
public GpxTrackWriter(String creator) {
|
public GPXTrackExporter(ContentProviderUtils contentProviderUtils, String creator) {
|
||||||
|
this.contentProviderUtils = contentProviderUtils;
|
||||||
this.creator = creator;
|
this.creator = creator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
||||||
|
try {
|
||||||
|
prepare(outputStream);
|
||||||
|
writeHeader(track);
|
||||||
|
|
||||||
|
writeMarkers(track);
|
||||||
|
|
||||||
|
writeLocations(track);
|
||||||
|
|
||||||
|
writeFooter();
|
||||||
|
close();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.e(TAG, "Thread interrupted", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeLocations(Track track) throws InterruptedException {
|
||||||
|
boolean wroteTrack = false;
|
||||||
|
boolean wroteSegment = false;
|
||||||
|
|
||||||
|
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) {
|
||||||
|
while (trackPointIterator.hasNext()) {
|
||||||
|
if (Thread.interrupted()) throw new InterruptedException();
|
||||||
|
|
||||||
|
TrackPoint trackPoint = trackPointIterator.next();
|
||||||
|
|
||||||
|
if (!wroteTrack) {
|
||||||
|
writeBeginTrack(track, trackPoint);
|
||||||
|
wroteTrack = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (trackPoint.getType()) {
|
||||||
|
case SEGMENT_START_MANUAL:
|
||||||
|
if (wroteSegment) writeCloseSegment();
|
||||||
|
writeOpenSegment();
|
||||||
|
wroteSegment = true;
|
||||||
|
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_START_MANUAL.name() + " is not supported.");
|
||||||
|
break;
|
||||||
|
case SEGMENT_END_MANUAL:
|
||||||
|
writeCloseSegment();
|
||||||
|
wroteSegment = false;
|
||||||
|
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_END_MANUAL.name() + " is not supported.");
|
||||||
|
break;
|
||||||
|
case SEGMENT_START_AUTOMATIC:
|
||||||
|
if (wroteSegment) writeCloseSegment();
|
||||||
|
writeOpenSegment();
|
||||||
|
wroteSegment = true;
|
||||||
|
writeTrackPoint(trackPoint);
|
||||||
|
break;
|
||||||
|
case TRACKPOINT:
|
||||||
|
if (!wroteSegment) {
|
||||||
|
// Might happen for older data (pre v3.15.0)
|
||||||
|
writeOpenSegment();
|
||||||
|
wroteSegment = true;
|
||||||
|
}
|
||||||
|
writeTrackPoint(trackPoint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wroteSegment) {
|
||||||
|
// Should not be necessary as tracks should end with SEGMENT_END_MANUAL.
|
||||||
|
// Anyhow, make sure that the last segment is closed.
|
||||||
|
writeCloseSegment();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wroteTrack) {
|
||||||
|
TrackPoint lastValidTrackPoint = contentProviderUtils.getLastValidTrackPoint(track.getId());
|
||||||
|
writeEndTrack(track, lastValidTrackPoint);
|
||||||
|
} else {
|
||||||
|
// Write an empty track
|
||||||
|
writeBeginTrack(track, null);
|
||||||
|
writeEndTrack(track, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void prepare(OutputStream outputStream) {
|
public void prepare(OutputStream outputStream) {
|
||||||
this.printWriter = new PrintWriter(outputStream);
|
this.printWriter = new PrintWriter(outputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
public void close() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.flush();
|
printWriter.flush();
|
||||||
@@ -82,8 +180,8 @@ public class GpxTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeHeader(Track[] tracks) {
|
public void writeHeader(Track track) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||||
printWriter.println("<gpx");
|
printWriter.println("<gpx");
|
||||||
@@ -103,31 +201,34 @@ public class GpxTrackWriter implements TrackWriter {
|
|||||||
|
|
||||||
printWriter.println("<metadata>");
|
printWriter.println("<metadata>");
|
||||||
|
|
||||||
Track track = tracks[0];
|
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
||||||
printWriter.println("</metadata>");
|
printWriter.println("</metadata>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeFooter() {
|
public void writeFooter() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("</gpx>");
|
printWriter.println("</gpx>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void writeMarkers(Track track) throws InterruptedException {
|
||||||
public void writeBeginMarkers(Track track) {
|
try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId(), null, -1)) {
|
||||||
// Do nothing
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
for (int i = 0; i < cursor.getCount(); i++) {
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
Marker marker = contentProviderUtils.createMarker(cursor);
|
||||||
|
writeMarker(marker);
|
||||||
|
|
||||||
|
cursor.moveToNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeEndMarkers() {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMarker(Marker marker) {
|
public void writeMarker(Marker marker) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<wpt " + formatLocation(marker.getLatitude(), marker.getLongitude()) + ">");
|
printWriter.println("<wpt " + formatLocation(marker.getLatitude(), marker.getLongitude()) + ">");
|
||||||
@@ -142,17 +243,6 @@ public class GpxTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMultiTrackBegin() {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMultiTrackEnd() {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeBeginTrack(Track track, TrackPoint startTrackPoint) {
|
public void writeBeginTrack(Track track, TrackPoint startTrackPoint) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<trk>");
|
printWriter.println("<trk>");
|
||||||
@@ -167,28 +257,24 @@ public class GpxTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeEndTrack(Track track, TrackPoint endTrackPoint) {
|
public void writeEndTrack(Track track, TrackPoint endTrackPoint) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("</trk>");
|
printWriter.println("</trk>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeOpenSegment() {
|
public void writeOpenSegment() {
|
||||||
printWriter.println("<trkseg>");
|
printWriter.println("<trkseg>");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeCloseSegment() {
|
public void writeCloseSegment() {
|
||||||
printWriter.println("</trkseg>");
|
printWriter.println("</trkseg>");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTrackPoint(TrackPoint trackPoint) {
|
public void writeTrackPoint(TrackPoint trackPoint) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
String coordinates = trackPoint.hasLocation() ? " " + formatLocation(trackPoint.getLatitude(), trackPoint.getLongitude()) : "";
|
|
||||||
printWriter.println("<trkpt " + coordinates + ">");
|
printWriter.println("<trkpt " + formatLocation(trackPoint.getLatitude(), trackPoint.getLongitude()) + ">");
|
||||||
|
|
||||||
if (trackPoint.hasAltitude()) {
|
if (trackPoint.hasAltitude()) {
|
||||||
printWriter.println("<ele>" + ELEVATION_FORMAT.format(trackPoint.getAltitude()) + "</ele>");
|
printWriter.println("<ele>" + ELEVATION_FORMAT.format(trackPoint.getAltitude()) + "</ele>");
|
||||||
@@ -208,7 +294,7 @@ public class GpxTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (trackPoint.hasCyclingCadence()) {
|
if (trackPoint.hasCyclingCadence()) {
|
||||||
printWriter.println("<gpxtpx:cad>" + HEARTRATE_FORMAT.format(trackPoint.getCyclingCadence_rpm()) + "</gpxtpx:cad>");
|
printWriter.println("<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.getCyclingCadence_rpm()) + "</gpxtpx:cad>");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackPoint.hasElevationGain()) {
|
if (trackPoint.hasElevationGain()) {
|
||||||
+174
-44
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2008 Google Inc.
|
* Copyright 2011 Google Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -13,13 +13,20 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.dennisguse.opentracks.io.file.exporter;
|
package de.dennisguse.opentracks.io.file.exporter;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -31,15 +38,20 @@ import de.dennisguse.opentracks.content.data.Marker;
|
|||||||
import de.dennisguse.opentracks.content.data.Track;
|
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.content.provider.TrackPointIterator;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write track as KML to a file.
|
* Convert {@link Track} incl. {@link Marker} and {@link TrackPoint} to KML.
|
||||||
*
|
*
|
||||||
|
* @author Sandor Dornbush
|
||||||
|
* @author Rodrigo Damazio
|
||||||
* @author Leif Hendrik Wilden
|
* @author Leif Hendrik Wilden
|
||||||
*/
|
*/
|
||||||
public class KmlTrackWriter implements TrackWriter {
|
public class KMLTrackExporter implements TrackExporter {
|
||||||
|
|
||||||
|
private static final String TAG = KMLTrackExporter.class.getSimpleName();
|
||||||
|
|
||||||
public static final String MARKER_STYLE = "waypoint";
|
public static final String MARKER_STYLE = "waypoint";
|
||||||
private static final String START_STYLE = "start";
|
private static final String START_STYLE = "start";
|
||||||
@@ -76,13 +88,7 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
|
|
||||||
private TrackPoint startTrackPoint;
|
private TrackPoint startTrackPoint;
|
||||||
|
|
||||||
/**
|
public KMLTrackExporter(Context context, boolean exportTrackDetail, boolean exportSensorData, boolean exportPhotos) {
|
||||||
* @param context the context
|
|
||||||
* @param exportTrackDetail should detailed information about the track be exported (e.g., title, description, markers, timing)?
|
|
||||||
* @param exportSensorData should {@link TrackPoint}'s sensor data be exported?
|
|
||||||
* @param exportPhotos should pictures be exported (if true: exports to KMZ)?
|
|
||||||
*/
|
|
||||||
public KmlTrackWriter(Context context, boolean exportTrackDetail, boolean exportSensorData, boolean exportPhotos) {
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.exportTrackDetail = exportTrackDetail;
|
this.exportTrackDetail = exportTrackDetail;
|
||||||
this.exportSensorData = exportSensorData;
|
this.exportSensorData = exportSensorData;
|
||||||
@@ -91,21 +97,137 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
this.contentProviderUtils = new ContentProviderUtils(context);
|
this.contentProviderUtils = new ContentProviderUtils(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
||||||
public void prepare(OutputStream outputStream) {
|
return writeTrack(new Track[]{track}, outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
||||||
|
try {
|
||||||
|
prepare(outputStream);
|
||||||
|
writeHeader(tracks);
|
||||||
|
for (Track track : tracks) {
|
||||||
|
writeMarkers(track);
|
||||||
|
}
|
||||||
|
boolean hasMultipleTracks = tracks.length > 1;
|
||||||
|
if (hasMultipleTracks) {
|
||||||
|
writeMultiTrackBegin();
|
||||||
|
}
|
||||||
|
//TODO Why use startTime of first track for the others?
|
||||||
|
Instant startTime = tracks[0].getTrackStatistics().getStartTime();
|
||||||
|
for (Track track : tracks) {
|
||||||
|
Duration offset = Duration.between(track.getTrackStatistics().getStartTime(), startTime);
|
||||||
|
writeLocations(track, offset);
|
||||||
|
}
|
||||||
|
if (hasMultipleTracks) {
|
||||||
|
writeMultiTrackEnd();
|
||||||
|
}
|
||||||
|
writeFooter();
|
||||||
|
close();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.e(TAG, "Thread interrupted", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeMarkers(Track track) throws InterruptedException {
|
||||||
|
boolean hasMarkers = false;
|
||||||
|
try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId(), null, -1)) {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
for (int i = 0; i < cursor.getCount(); i++) {
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
if (!hasMarkers) {
|
||||||
|
writeBeginMarkers(track);
|
||||||
|
hasMarkers = true;
|
||||||
|
}
|
||||||
|
Marker marker = contentProviderUtils.createMarker(cursor);
|
||||||
|
writeMarker(marker);
|
||||||
|
|
||||||
|
cursor.moveToNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasMarkers) {
|
||||||
|
writeEndMarkers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeLocations(Track track, Duration offset) throws InterruptedException {
|
||||||
|
boolean wroteTrack = false;
|
||||||
|
boolean wroteSegment = false;
|
||||||
|
|
||||||
|
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) {
|
||||||
|
while (trackPointIterator.hasNext()) {
|
||||||
|
if (Thread.interrupted()) throw new InterruptedException();
|
||||||
|
|
||||||
|
TrackPoint trackPoint = trackPointIterator.next();
|
||||||
|
setLocationTime(trackPoint, offset);
|
||||||
|
|
||||||
|
if (!wroteTrack) {
|
||||||
|
writeBeginTrack(track, trackPoint);
|
||||||
|
wroteTrack = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (trackPoint.getType()) {
|
||||||
|
case SEGMENT_START_MANUAL:
|
||||||
|
case SEGMENT_START_AUTOMATIC:
|
||||||
|
if (wroteSegment) writeCloseSegment();
|
||||||
|
writeOpenSegment();
|
||||||
|
writeTrackPoint(trackPoint);
|
||||||
|
wroteSegment = true;
|
||||||
|
break;
|
||||||
|
case SEGMENT_END_MANUAL:
|
||||||
|
if (!wroteSegment) writeOpenSegment();
|
||||||
|
writeTrackPoint(trackPoint);
|
||||||
|
writeCloseSegment();
|
||||||
|
wroteSegment = false;
|
||||||
|
break;
|
||||||
|
case TRACKPOINT:
|
||||||
|
if (!wroteSegment) {
|
||||||
|
// Might happen for older data (pre v3.15.0)
|
||||||
|
writeOpenSegment();
|
||||||
|
wroteSegment = true;
|
||||||
|
}
|
||||||
|
writeTrackPoint(trackPoint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wroteSegment) {
|
||||||
|
// Should not be necessary as tracks should end with SEGMENT_END_MANUAL.
|
||||||
|
// Anyhow, make sure that the last segment is closed.
|
||||||
|
writeCloseSegment();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wroteTrack) {
|
||||||
|
TrackPoint lastValidTrackPoint = contentProviderUtils.getLastValidTrackPoint(track.getId());
|
||||||
|
setLocationTime(lastValidTrackPoint, offset);
|
||||||
|
writeEndTrack(track, lastValidTrackPoint);
|
||||||
|
} else {
|
||||||
|
// Write an empty track
|
||||||
|
writeBeginTrack(track, null);
|
||||||
|
writeEndTrack(track, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void prepare(OutputStream outputStream) {
|
||||||
this.printWriter = new PrintWriter(outputStream);
|
this.printWriter = new PrintWriter(outputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@VisibleForTesting
|
||||||
public void close() {
|
void close() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.flush();
|
printWriter.flush();
|
||||||
printWriter = null;
|
printWriter = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void writeHeader(Track[] tracks) {
|
||||||
public void writeHeader(Track[] tracks) {
|
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||||
printWriter.println("<kml xmlns=\"http://www.opengis.net/kml/2.2\"");
|
printWriter.println("<kml xmlns=\"http://www.opengis.net/kml/2.2\"");
|
||||||
@@ -141,16 +263,14 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void writeFooter() {
|
||||||
public void writeFooter() {
|
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("</Document>");
|
printWriter.println("</Document>");
|
||||||
printWriter.println("</kml>");
|
printWriter.println("</kml>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void writeBeginMarkers(Track track) {
|
||||||
public void writeBeginMarkers(Track track) {
|
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<Folder>");
|
printWriter.println("<Folder>");
|
||||||
if (exportTrackDetail) {
|
if (exportTrackDetail) {
|
||||||
@@ -160,15 +280,7 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void writeMarker(Marker marker) {
|
||||||
public void writeEndMarkers() {
|
|
||||||
if (printWriter != null) {
|
|
||||||
printWriter.println("</Folder>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMarker(Marker marker) {
|
|
||||||
if (printWriter != null && exportTrackDetail) {
|
if (printWriter != null && exportTrackDetail) {
|
||||||
boolean existsPhoto = FileUtils.buildInternalPhotoFile(context, marker.getTrackId(), marker.getPhotoURI()) != null;
|
boolean existsPhoto = FileUtils.buildInternalPhotoFile(context, marker.getTrackId(), marker.getPhotoURI()) != null;
|
||||||
if (marker.hasPhoto() && exportPhotos && existsPhoto) {
|
if (marker.hasPhoto() && exportPhotos && existsPhoto) {
|
||||||
@@ -180,7 +292,13 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeMultiTrackBegin() {
|
private void writeEndMarkers() {
|
||||||
|
if (printWriter != null) {
|
||||||
|
printWriter.println("</Folder>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeMultiTrackBegin() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<Folder id=tour>");
|
printWriter.println("<Folder id=tour>");
|
||||||
printWriter.println("<name>" + context.getString(R.string.generic_tracks) + "</name>");
|
printWriter.println("<name>" + context.getString(R.string.generic_tracks) + "</name>");
|
||||||
@@ -188,14 +306,13 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeMultiTrackEnd() {
|
private void writeMultiTrackEnd() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("</Folder>");
|
printWriter.println("</Folder>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void writeBeginTrack(Track track, TrackPoint startTrackPoint) {
|
||||||
public void writeBeginTrack(Track track, TrackPoint startTrackPoint) {
|
|
||||||
this.startTrackPoint = startTrackPoint;
|
this.startTrackPoint = startTrackPoint;
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
String name = context.getString(R.string.marker_label_start, track.getName());
|
String name = context.getString(R.string.marker_label_start, track.getName());
|
||||||
@@ -218,8 +335,8 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeEndTrack(Track track, TrackPoint endTrackPoint) {
|
private void writeEndTrack(Track track, TrackPoint endTrackPoint) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("</gx:MultiTrack>");
|
printWriter.println("</gx:MultiTrack>");
|
||||||
printWriter.println("</Placemark>");
|
printWriter.println("</Placemark>");
|
||||||
@@ -233,8 +350,8 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@VisibleForTesting
|
||||||
public void writeOpenSegment() {
|
void writeOpenSegment() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<gx:Track>");
|
printWriter.println("<gx:Track>");
|
||||||
speedList.clear();
|
speedList.clear();
|
||||||
@@ -246,8 +363,8 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@VisibleForTesting
|
||||||
public void writeCloseSegment() {
|
void writeCloseSegment() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<ExtendedData>");
|
printWriter.println("<ExtendedData>");
|
||||||
printWriter.println("<SchemaData schemaUrl=\"#" + SCHEMA_ID + "\">");
|
printWriter.println("<SchemaData schemaUrl=\"#" + SCHEMA_ID + "\">");
|
||||||
@@ -277,8 +394,8 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@VisibleForTesting
|
||||||
public void writeTrackPoint(TrackPoint trackPoint) {
|
void writeTrackPoint(TrackPoint trackPoint) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
if (exportTrackDetail) {
|
if (exportTrackDetail) {
|
||||||
printWriter.println("<when>" + getTime(trackPoint.getLocation()) + "</when>");
|
printWriter.println("<when>" + getTime(trackPoint.getLocation()) + "</when>");
|
||||||
@@ -351,14 +468,14 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>");
|
||||||
printWriter.println("<description>" + StringUtils.formatCData(marker.getDescription()) + "</description>");
|
printWriter.println("<description>" + StringUtils.formatCData(marker.getDescription()) + "</description>");
|
||||||
printWriter.print("<Camera>");
|
printWriter.print("<Camera>");
|
||||||
printWriter.print("<longitude>" + marker.getLocation().getLongitude() + "</longitude>");
|
printWriter.print("<longitude>" + marker.getLongitude() + "</longitude>");
|
||||||
printWriter.print("<latitude>" + marker.getLocation().getLatitude() + "</latitude>");
|
printWriter.print("<latitude>" + marker.getLatitude() + "</latitude>");
|
||||||
printWriter.print("<altitude>20</altitude>");
|
printWriter.print("<altitude>20</altitude>");
|
||||||
printWriter.print("<heading>" + heading + "</heading>");
|
printWriter.print("<heading>" + heading + "</heading>");
|
||||||
printWriter.print("<tilt>90</tilt>");
|
printWriter.print("<tilt>90</tilt>");
|
||||||
printWriter.println("</Camera>");
|
printWriter.println("</Camera>");
|
||||||
printWriter.println("<TimeStamp><when>" + getTime(marker.getLocation()) + "</when></TimeStamp>");
|
printWriter.println("<TimeStamp><when>" + getTime(marker.getLocation()) + "</when></TimeStamp>");
|
||||||
printWriter.println("<styleUrl>#" + KmlTrackWriter.MARKER_STYLE + "</styleUrl>");
|
printWriter.println("<styleUrl>#" + MARKER_STYLE + "</styleUrl>");
|
||||||
writeCategory(marker.getCategory());
|
writeCategory(marker.getCategory());
|
||||||
|
|
||||||
if (exportPhotos) {
|
if (exportPhotos) {
|
||||||
@@ -474,4 +591,17 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
printWriter.println("<displayName>" + StringUtils.formatCData(extendedDataType) + "</displayName>");
|
printWriter.println("<displayName>" + StringUtils.formatCData(extendedDataType) + "</displayName>");
|
||||||
printWriter.println("</gx:SimpleArrayField>");
|
printWriter.println("</gx:SimpleArrayField>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a trackPoint time.
|
||||||
|
*
|
||||||
|
* @param trackPoint the trackPoint
|
||||||
|
* @param offset the time offset
|
||||||
|
*/
|
||||||
|
//TODO Why?
|
||||||
|
private void setLocationTime(TrackPoint trackPoint, Duration offset) {
|
||||||
|
if (trackPoint != null) {
|
||||||
|
trackPoint.setTime(trackPoint.getTime().minus(offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -50,19 +50,12 @@ public class KmzTrackExporter implements TrackExporter {
|
|||||||
private static final int BUFFER_SIZE = 4096;
|
private static final int BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
private final FileTrackExporter fileTrackExporter;
|
private final KMLTrackExporter fileTrackExporter;
|
||||||
|
|
||||||
private final boolean exportPhotos;
|
private final boolean exportPhotos;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
/**
|
public KmzTrackExporter(Context context, ContentProviderUtils contentProviderUtils, KMLTrackExporter trackExporter, boolean exportPhotos) {
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param context the context
|
|
||||||
* @param contentProviderUtils the content provider utils
|
|
||||||
* @param trackExporter the file track exporter
|
|
||||||
*/
|
|
||||||
public KmzTrackExporter(Context context, ContentProviderUtils contentProviderUtils, FileTrackExporter trackExporter, boolean exportPhotos) {
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.contentProviderUtils = contentProviderUtils;
|
this.contentProviderUtils = contentProviderUtils;
|
||||||
this.fileTrackExporter = trackExporter;
|
this.fileTrackExporter = trackExporter;
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
package de.dennisguse.opentracks.io.file.exporter;
|
|
||||||
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for writing tracks to a file. The expected sequence of calls is:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* {@link #prepare(OutputStream)}
|
|
||||||
* {@link #writeHeader(Track[])}
|
|
||||||
* For each track:
|
|
||||||
* {@link #writeBeginMarkers(Track)}
|
|
||||||
* For each marker:
|
|
||||||
* {@link #writeMarker(Marker)}
|
|
||||||
* {@link #writeEndMarkers()}
|
|
||||||
* {@link #writeMultiTrackBegin()}
|
|
||||||
* For each track:
|
|
||||||
* {@link #writeBeginTrack(Track, TrackPoint)}
|
|
||||||
* For each segment:
|
|
||||||
* {@link #writeOpenSegment()}
|
|
||||||
* For each trackPoint in the segment:
|
|
||||||
* {@link #writeTrackPoint(TrackPoint)}
|
|
||||||
* {@link #writeCloseSegment()}
|
|
||||||
* {@link #writeEndTrack(Track, TrackPoint)}
|
|
||||||
* {@link #writeMultiTrackEnd()}
|
|
||||||
* {@link #writeFooter()}
|
|
||||||
* {@link #close()}
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @author Rodrigo Damazio
|
|
||||||
*/
|
|
||||||
public interface TrackWriter {
|
|
||||||
|
|
||||||
void prepare(OutputStream outputStream);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the output stream.
|
|
||||||
*/
|
|
||||||
void close();
|
|
||||||
|
|
||||||
void writeHeader(Track[] tracks);
|
|
||||||
|
|
||||||
void writeFooter();
|
|
||||||
|
|
||||||
void writeBeginMarkers(Track track);
|
|
||||||
|
|
||||||
void writeEndMarkers();
|
|
||||||
|
|
||||||
void writeMarker(Marker marker);
|
|
||||||
|
|
||||||
void writeMultiTrackBegin();
|
|
||||||
|
|
||||||
void writeMultiTrackEnd();
|
|
||||||
|
|
||||||
void writeBeginTrack(Track track, TrackPoint startTrackPoint);
|
|
||||||
|
|
||||||
void writeEndTrack(Track track, TrackPoint endTrackPoint);
|
|
||||||
|
|
||||||
void writeOpenSegment();
|
|
||||||
|
|
||||||
void writeCloseSegment();
|
|
||||||
|
|
||||||
void writeTrackPoint(TrackPoint trackPoint);
|
|
||||||
}
|
|
||||||
+2
-7
@@ -36,7 +36,6 @@ import java.util.Locale;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import javax.xml.parsers.SAXParser;
|
|
||||||
import javax.xml.parsers.SAXParserFactory;
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
@@ -121,11 +120,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
@Override
|
@Override
|
||||||
public Track.Id importFile(InputStream inputStream) {
|
public Track.Id importFile(InputStream inputStream) {
|
||||||
try {
|
try {
|
||||||
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
|
SAXParserFactory.newInstance().newSAXParser().parse(inputStream, this);
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
saxParser.parse(inputStream, this);
|
|
||||||
Log.d(TAG, "Total import time: " + (System.currentTimeMillis() - start) + "ms");
|
|
||||||
return trackIds.get(0);
|
return trackIds.get(0);
|
||||||
} catch (IOException | SAXException | ParserConfigurationException e) {
|
} catch (IOException | SAXException | ParserConfigurationException e) {
|
||||||
Log.e(TAG, "Unable to import file", e);
|
Log.e(TAG, "Unable to import file", e);
|
||||||
@@ -375,7 +370,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
*/
|
*/
|
||||||
if (trackPoint.hasLocation() && trackData.lastLocationInCurrentSegment.hasLocation()) {
|
if (trackPoint.hasLocation() && trackData.lastLocationInCurrentSegment.hasLocation()) {
|
||||||
float speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / timeDifference.toMillis();
|
float speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / timeDifference.toMillis();
|
||||||
trackPoint.setSpeed((float) speed);
|
trackPoint.setSpeed(speed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
|||||||
/**
|
/**
|
||||||
* Imports a GPX file.
|
* Imports a GPX file.
|
||||||
* Uses https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd
|
* Uses https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd
|
||||||
|
* <p>
|
||||||
|
* {@link de.dennisguse.opentracks.io.file.exporter.GPXTrackExporter} does not export information if a segment was started automatic or manually.
|
||||||
|
* Therefore, all segments starts are marked as SEGMENT_START_AUTOMATIC.
|
||||||
|
* Thus, the {@link de.dennisguse.opentracks.stats.TrackStatistics} cannot be restored correctly.
|
||||||
*
|
*
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
@@ -200,9 +204,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
boolean isFirstTrackPointInSegment = isFirstTrackPointInSegment();
|
boolean isFirstTrackPointInSegment = isFirstTrackPointInSegment();
|
||||||
TrackPoint trackPoint = getTrackPoint();
|
TrackPoint trackPoint = getTrackPoint();
|
||||||
if (isFirstTrackPointInSegment) {
|
if (isFirstTrackPointInSegment) {
|
||||||
TrackPoint.Type type = !trackPoint.hasLocation() ? TrackPoint.Type.SEGMENT_START_MANUAL : TrackPoint.Type.SEGMENT_START_AUTOMATIC;
|
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
|
||||||
trackPoint.setType(type);
|
|
||||||
}
|
}
|
||||||
insertTrackPoint(trackPoint);
|
insertTrackPoint(trackPoint);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import java.util.ArrayList;
|
|||||||
|
|
||||||
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.KmlTrackWriter;
|
import de.dennisguse.opentracks.io.file.exporter.KMLTrackExporter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports a KML file.
|
* Imports a KML file.
|
||||||
@@ -39,7 +39,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
|
|
||||||
private static final String TAG = KmlFileTrackImporter.class.getSimpleName();
|
private static final String TAG = KmlFileTrackImporter.class.getSimpleName();
|
||||||
|
|
||||||
private static final String MARKER_STYLE = "#" + KmlTrackWriter.MARKER_STYLE;
|
private static final String MARKER_STYLE = "#" + KMLTrackExporter.MARKER_STYLE;
|
||||||
|
|
||||||
private static final String TAG_COORDINATES = "coordinates";
|
private static final String TAG_COORDINATES = "coordinates";
|
||||||
private static final String TAG_DESCRIPTION = "description";
|
private static final String TAG_DESCRIPTION = "description";
|
||||||
@@ -306,22 +306,22 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (extendedDataType) {
|
switch (extendedDataType) {
|
||||||
case KmlTrackWriter.EXTENDED_DATA_TYPE_SPEED:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED:
|
||||||
speedList.add(value);
|
speedList.add(value);
|
||||||
break;
|
break;
|
||||||
case KmlTrackWriter.EXTENDED_DATA_TYPE_POWER:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER:
|
||||||
powerList.add(value);
|
powerList.add(value);
|
||||||
break;
|
break;
|
||||||
case KmlTrackWriter.EXTENDED_DATA_TYPE_HEART_RATE:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_HEART_RATE:
|
||||||
heartRateList.add(value);
|
heartRateList.add(value);
|
||||||
break;
|
break;
|
||||||
case KmlTrackWriter.EXTENDED_DATA_TYPE_CADENCE:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE:
|
||||||
cadenceList.add(value);
|
cadenceList.add(value);
|
||||||
break;
|
break;
|
||||||
case KmlTrackWriter.EXTENDED_DATA_TYPE_ELEVATION_GAIN:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ELEVATION_GAIN:
|
||||||
elevationGainList.add(value);
|
elevationGainList.add(value);
|
||||||
break;
|
break;
|
||||||
case KmlTrackWriter.EXTENDED_DATA_TYPE_ELEVATION_LOSS:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ELEVATION_LOSS:
|
||||||
elevationLossList.add(value);
|
elevationLossList.add(value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class ExportUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean exportTrack(Context context, TrackFileFormat trackFileFormat, DocumentFile directory, Track track) {
|
public static boolean exportTrack(Context context, TrackFileFormat trackFileFormat, DocumentFile directory, Track track) {
|
||||||
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context);
|
TrackExporter trackExporter = trackFileFormat.createTrackExporter(context);
|
||||||
|
|
||||||
Uri exportDocumentFileUri = getExportDocumentFileUri(context, track.getId(), trackFileFormat, directory);
|
Uri exportDocumentFileUri = getExportDocumentFileUri(context, track.getId(), trackFileFormat, directory);
|
||||||
if (exportDocumentFileUri == null) {
|
if (exportDocumentFileUri == null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user