1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-27 12:26:48 +01:00

Refactor and extend GpxParser

This commit is contained in:
José Rebelo 2023-05-14 14:19:48 +01:00
parent 87c91fb9b0
commit 01ec74602a
14 changed files with 787 additions and 152 deletions

View File

@ -34,13 +34,16 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
import nodomain.freeyourgadget.gadgetbridge.util.GpxParser;
import nodomain.freeyourgadget.gadgetbridge.util.gpx.GpxParseException;
import nodomain.freeyourgadget.gadgetbridge.util.gpx.GpxParser;
import nodomain.freeyourgadget.gadgetbridge.util.gpx.model.GpxFile;
import static android.graphics.Bitmap.createBitmap;
@ -74,37 +77,35 @@ public class ActivitySummariesGpsFragment extends AbstractGBFragment {
new Thread(new Runnable() {
@Override
public void run() {
GpxParser gpxParser = null;
FileInputStream inputStream = null;
final GpxFile gpxFile;
try {
inputStream = new FileInputStream(inputFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
try (FileInputStream inputStream = new FileInputStream(inputFile)) {
final GpxParser gpxParser = new GpxParser(inputStream);
gpxFile = gpxParser.getGpxFile();
} catch (final IOException e) {
LOG.error("Failed to open {}", inputFile, e);
return;
} catch (final GpxParseException e) {
LOG.error("Failed to parse gpx file", e);
return;
}
if (inputStream != null) {
gpxParser = new GpxParser(inputStream);
}
if (gpxParser != null) {
if (gpxParser.getPoints().toArray().length > 0) {
drawTrack(canvas, gpxParser.getPoints());
}
if (!gpxFile.getPoints().isEmpty()) {
drawTrack(canvas, gpxFile.getPoints());
}
}
}).start();
}
private void drawTrack(Canvas canvas, List<GPSCoordinate> trackPoints) {
private void drawTrack(Canvas canvas, List<? extends GPSCoordinate> trackPoints) {
double maxLat = (Collections.max(trackPoints, new GPSCoordinate.compareLatitude())).getLatitude();
double minLat = (Collections.min(trackPoints, new GPSCoordinate.compareLatitude())).getLatitude();
double maxLon = (Collections.max(trackPoints, new GPSCoordinate.compareLongitude())).getLongitude();
double minLon = (Collections.min(trackPoints, new GPSCoordinate.compareLongitude())).getLongitude();
double maxAlt = (Collections.max(trackPoints, new GPSCoordinate.compareElevation())).getAltitude();
double minAlt = (Collections.min(trackPoints, new GPSCoordinate.compareElevation())).getAltitude();
float scale_factor_w = (float) ((maxLat - minLat) / (maxLon - minLon));
float scale_factor_h = (float) ((maxLon - minLon) / (maxLat - minLat));
float scale_factor_w = (float) ((maxLon - minLon) / (maxLat - minLat));
float scale_factor_h = (float) ((maxLat - minLat) / (maxLon - minLon));
if (scale_factor_h > scale_factor_w) { //scaling to draw proportionally
scale_factor_h = 1;
@ -122,7 +123,7 @@ public class ActivitySummariesGpsFragment extends AbstractGBFragment {
float lon = (float) ((p.getLongitude() - minLon) / (maxLon - minLon));
float alt = (float) ((p.getAltitude() - minAlt) / (maxAlt - minAlt));
paint.setStrokeWidth(1 + alt); //make thicker with higher altitude, we could do more here
canvas.drawPoint(CANVAS_SIZE * lat * scale_factor_w, CANVAS_SIZE * lon * scale_factor_h, paint);
canvas.drawPoint(CANVAS_SIZE * lon * scale_factor_w, CANVAS_SIZE * lat * scale_factor_h, paint);
}
}

View File

@ -20,7 +20,7 @@ import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Comparator;
public final class GPSCoordinate {
public class GPSCoordinate {
private final double latitude;
private final double longitude;
private final double altitude;

View File

@ -1,99 +0,0 @@
/* Copyright (C) 2020-2021 Petr Vaněk
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
public class GpxParser {
private static final Logger LOG = LoggerFactory.getLogger(GpxParser.class);
private XmlPullParser parser;
private List<GPSCoordinate> points = new ArrayList<>();
private int eventType;
public GpxParser(InputStream stream) {
points = new ArrayList<>();
try {
parser = createXmlParser(stream);
parseGpx();
} catch (Exception e) {
e.printStackTrace();
}
}
private static XmlPullParser createXmlParser(InputStream stream) throws XmlPullParserException {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = factory.newPullParser();
parser.setInput(stream, null);
return parser;
}
private void parseGpx() throws XmlPullParserException, IOException {
eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && parser.getName().equals("trkpt")) {
points.add(parsePoint(parser));
} else {
eventType = parser.next();
}
}
}
private double parseElevation(XmlPullParser parser) throws XmlPullParserException, IOException {
String eleString = "";
while (eventType != XmlPullParser.END_TAG) {
if (eventType == XmlPullParser.TEXT) {
eleString = parser.getText();
}
eventType = parser.next();
}
return Double.parseDouble(eleString);
}
private GPSCoordinate parsePoint(XmlPullParser parser) throws XmlPullParserException, IOException {
double lat;
double lon;
double ele = 0;
String latString = parser.getAttributeValue(null, "lat");
String lonString = parser.getAttributeValue(null, "lon");
lat = latString != null ? Double.parseDouble(latString) : 0;
lon = lonString != null ? Double.parseDouble(lonString) : 0;
while (eventType != XmlPullParser.END_TAG) {
if (parser.getEventType() == XmlPullParser.START_TAG && parser.getName().equals("ele")) {
ele = parseElevation(parser);
}
eventType = parser.next();
}
return new GPSCoordinate(lat, lon, ele);
}
public List<GPSCoordinate> getPoints() {
return points;
}
}

View File

@ -0,0 +1,27 @@
/* Copyright (C) 2023 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util.gpx;
public class GpxParseException extends Exception {
public GpxParseException(final String message) {
super(message);
}
public GpxParseException(final String message, final Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,222 @@
/* Copyright (C) 2020-2023 Petr Vaněk, José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util.gpx;
import com.google.gson.internal.bind.util.ISO8601Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParsePosition;
import java.util.Date;
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
import nodomain.freeyourgadget.gadgetbridge.util.gpx.model.GpxFile;
import nodomain.freeyourgadget.gadgetbridge.util.gpx.model.GpxTrack;
import nodomain.freeyourgadget.gadgetbridge.util.gpx.model.GpxTrackPoint;
import nodomain.freeyourgadget.gadgetbridge.util.gpx.model.GpxTrackSegment;
import nodomain.freeyourgadget.gadgetbridge.util.gpx.model.GpxWaypoint;
public class GpxParser {
private static final Logger LOG = LoggerFactory.getLogger(GpxParser.class);
private final XmlPullParser parser;
private int eventType;
private final GpxFile.Builder fileBuilder;
public GpxParser(final InputStream stream) throws GpxParseException {
this.fileBuilder = new GpxFile.Builder();
try {
parser = createXmlParser(stream);
parseGpx();
} catch (final Exception e) {
throw new GpxParseException("Failed to parse gpx", e);
}
}
public GpxFile getGpxFile() {
return fileBuilder.build();
}
private static XmlPullParser createXmlParser(InputStream stream) throws XmlPullParserException {
final XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = factory.newPullParser();
parser.setInput(stream, null);
return parser;
}
private void parseGpx() throws Exception {
eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case "trk":
final GpxTrack track = parseTrack();
if (!track.isEmpty()) {
fileBuilder.withTrack(track);
}
continue;
case "wpt":
fileBuilder.withWaypoints(parseWaypoint());
continue;
case "metadata":
parseMetadata();
continue;
}
}
eventType = parser.next();
}
}
private GpxTrack parseTrack() throws Exception {
final GpxTrack.Builder trackBuilder = new GpxTrack.Builder();
while (eventType != XmlPullParser.END_TAG || !parser.getName().equals("trk")) {
if (eventType == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case "trkseg":
final GpxTrackSegment segment = parseTrackSegment();
if (!segment.getTrackPoints().isEmpty()) {
trackBuilder.withTrackSegment(segment);
}
continue;
}
}
eventType = parser.next();
}
return trackBuilder.build();
}
private GpxTrackSegment parseTrackSegment() throws Exception {
final GpxTrackSegment.Builder segmentBuilder = new GpxTrackSegment.Builder();
while (eventType != XmlPullParser.END_TAG || !parser.getName().equals("trkseg")) {
if (eventType == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case "trkpt":
final GpxTrackPoint trackPoint = parseTrackPoint();
segmentBuilder.withTrackPoint(trackPoint);
continue;
}
}
eventType = parser.next();
}
return segmentBuilder.build();
}
private String parseStringContent(final String tag) throws Exception {
String retString = "";
while (eventType != XmlPullParser.END_TAG || !parser.getName().equals(tag)) {
if (eventType == XmlPullParser.TEXT) {
retString = parser.getText();
}
eventType = parser.next();
}
return retString;
}
private double parseElevation() throws Exception {
return Double.parseDouble(parseStringContent("ele"));
}
private Date parseTime() throws Exception {
return ISO8601Utils.parse(parseStringContent("time"), new ParsePosition(0));
}
private GpxTrackPoint parseTrackPoint() throws Exception {
final GpxTrackPoint.Builder trackPointBuilder = new GpxTrackPoint.Builder();
final String latString = parser.getAttributeValue(null, "lat");
final String lonString = parser.getAttributeValue(null, "lon");
trackPointBuilder.withLatitude(latString != null ? Double.parseDouble(latString) : 0);
trackPointBuilder.withLongitude(lonString != null ? Double.parseDouble(lonString) : 0);
while (eventType != XmlPullParser.END_TAG || !parser.getName().equals("trkpt")) {
if (parser.getEventType() == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case "ele":
trackPointBuilder.withAltitude(parseElevation());
continue;
case "time":
trackPointBuilder.withTime(parseTime());
continue;
}
}
eventType = parser.next();
}
return trackPointBuilder.build();
}
private GpxWaypoint parseWaypoint() throws Exception {
final GpxWaypoint.Builder waypointBuilder = new GpxWaypoint.Builder();
final String latString = parser.getAttributeValue(null, "lat");
final String lonString = parser.getAttributeValue(null, "lon");
waypointBuilder.withLatitude(latString != null ? Double.parseDouble(latString) : 0);
waypointBuilder.withLongitude(lonString != null ? Double.parseDouble(lonString) : 0);
while (eventType != XmlPullParser.END_TAG || !parser.getName().equals("wpt")) {
if (parser.getEventType() == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case "ele":
waypointBuilder.withAltitude(parseElevation());
continue;
case "name":
waypointBuilder.withName(parseStringContent("name"));
continue;
}
}
eventType = parser.next();
}
return waypointBuilder.build();
}
private void parseMetadata() throws Exception {
while (eventType != XmlPullParser.END_TAG || !parser.getName().equals("metadata")) {
if (parser.getEventType() == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case "name":
fileBuilder.withName(parseStringContent("name"));
continue;
case "author":
// TODO parse author
break;
}
}
eventType = parser.next();
}
}
}

View File

@ -0,0 +1,95 @@
/* Copyright (C) 2023 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util.gpx.model;
import java.util.ArrayList;
import java.util.List;
public class GpxFile {
private final String name;
private final String author;
private final List<GpxTrack> tracks;
private final List<GpxWaypoint> waypoints;
public GpxFile(final String name, final String author, final List<GpxTrack> tracks, final List<GpxWaypoint> waypoints) {
this.name = name;
this.author = author;
this.tracks = tracks;
this.waypoints = waypoints;
}
public String getName() {
return name;
}
public String getAuthor() {
return author;
}
public List<GpxTrack> getTracks() {
return tracks;
}
public List<GpxWaypoint> getWaypoints() {
return waypoints;
}
public List<GpxTrackPoint> getPoints() {
final List<GpxTrackPoint> allPoints = new ArrayList<>();
for (final GpxTrack track : tracks) {
for (final GpxTrackSegment trackSegment : track.getTrackSegments()) {
allPoints.addAll(trackSegment.getTrackPoints());
}
}
return allPoints;
}
public static class Builder {
private String name;
private String author;
private List<GpxTrack> tracks = new ArrayList<>();
private List<GpxWaypoint> waypoints = new ArrayList<>();
public Builder withName(final String name) {
this.name = name;
return this;
}
public Builder withAuthor(final String author) {
this.author = author;
return this;
}
public Builder withTrack(final GpxTrack track) {
this.tracks.add(track);
return this;
}
public Builder withWaypoints(final GpxWaypoint waypoint) {
this.waypoints.add(waypoint);
return this;
}
public GpxFile build() {
return new GpxFile(name, author, tracks, waypoints);
}
}
}

View File

@ -0,0 +1,55 @@
/* Copyright (C) 2023 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util.gpx.model;
import java.util.ArrayList;
import java.util.List;
public class GpxTrack {
private final List<GpxTrackSegment> trackSegments;
public GpxTrack(final List<GpxTrackSegment> trackSegments) {
this.trackSegments = trackSegments;
}
public List<GpxTrackSegment> getTrackSegments() {
return trackSegments;
}
public boolean isEmpty() {
for (final GpxTrackSegment trackSegment : trackSegments) {
if (!trackSegment.getTrackPoints().isEmpty()) {
return false;
}
}
return true;
}
public static class Builder {
private final List<GpxTrackSegment> trackSegments = new ArrayList<>();
public Builder withTrackSegment(final GpxTrackSegment trackSegment) {
trackSegments.add(trackSegment);
return this;
}
public GpxTrack build() {
return new GpxTrack(trackSegments);
}
}
}

View File

@ -0,0 +1,80 @@
/* Copyright (C) 2023 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util.gpx.model;
import java.util.Date;
import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
public class GpxTrackPoint extends GPSCoordinate {
private final Date time;
public GpxTrackPoint(final double longitude, final double latitude, final double altitude, final Date time) {
super(longitude, latitude, altitude);
this.time = time;
}
public Date getTime() {
return time;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof GpxTrackPoint)) return false;
if (!super.equals(o)) return false;
final GpxTrackPoint that = (GpxTrackPoint) o;
return Objects.equals(time, that.time);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), time);
}
public static class Builder {
private double longitude;
private double latitude;
private double altitude;
private Date time;
public Builder withLongitude(final double longitude) {
this.longitude = longitude;
return this;
}
public Builder withLatitude(final double latitude) {
this.latitude = latitude;
return this;
}
public Builder withAltitude(final double altitude) {
this.altitude = altitude;
return this;
}
public Builder withTime(final Date time) {
this.time = time;
return this;
}
public GpxTrackPoint build() {
return new GpxTrackPoint(longitude, latitude, altitude, time);
}
}
}

View File

@ -0,0 +1,49 @@
/* Copyright (C) 2023 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util.gpx.model;
import java.util.ArrayList;
import java.util.List;
public class GpxTrackSegment {
private final List<GpxTrackPoint> trackPoints;
public GpxTrackSegment(final List<GpxTrackPoint> trackPoints) {
this.trackPoints = trackPoints;
}
public List<GpxTrackPoint> getTrackPoints() {
return trackPoints;
}
public static class Builder {
private final List<GpxTrackPoint> trackPoints = new ArrayList<>();
public Builder withTrackPoint(final GpxTrackPoint trackPoint) {
trackPoints.add(trackPoint);
return this;
}
public boolean isEmpty() {
return trackPoints.isEmpty();
}
public GpxTrackSegment build() {
return new GpxTrackSegment(trackPoints);
}
}
}

View File

@ -0,0 +1,67 @@
/* Copyright (C) 2023 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util.gpx.model;
import androidx.annotation.Nullable;
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
public class GpxWaypoint extends GPSCoordinate {
@Nullable
private final String name;
public GpxWaypoint(final double longitude, final double latitude, final double altitude, @Nullable final String name) {
super(longitude, latitude, altitude);
this.name = name;
}
@Nullable
public String getName() {
return name;
}
public static class Builder {
private double longitude;
private double latitude;
private double altitude;
private String name;
public Builder withLongitude(final double longitude) {
this.longitude = longitude;
return this;
}
public Builder withLatitude(final double latitude) {
this.latitude = latitude;
return this;
}
public Builder withAltitude(final double altitude) {
this.altitude = altitude;
return this;
}
public Builder withName(final String name) {
this.name = name;
return this;
}
public GpxWaypoint build() {
return new GpxWaypoint(longitude, latitude, altitude, name);
}
}
}

View File

@ -1,33 +0,0 @@
package nodomain.freeyourgadget.gadgetbridge.test;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
import nodomain.freeyourgadget.gadgetbridge.util.GpxParser;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.is;
public class GPXParserTest extends TestBase {
@Test
public void shouldReadGPXCorrectly() throws IOException {
try (final InputStream inputStream = getClass().getResourceAsStream("/gpx-exporter-test-SampleTrack.gpx")) {
GpxParser gpxParser = new GpxParser(inputStream);
List<GPSCoordinate> trackPoints = gpxParser.getPoints();
Assert.assertEquals(trackPoints.size(), 14);
DecimalFormat df = new DecimalFormat("###.##");
for (GPSCoordinate tp : trackPoints) {
Assert.assertEquals(df.format(tp.getLongitude()), "44.15");
Assert.assertEquals(df.format(tp.getLatitude()), "-68.2");
Assert.assertThat(df.format(tp.getAltitude()), anyOf(is("40"), is("46")));
}
}
}
}

View File

@ -0,0 +1,78 @@
package nodomain.freeyourgadget.gadgetbridge.util.gpx;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate;
import nodomain.freeyourgadget.gadgetbridge.test.TestBase;
import nodomain.freeyourgadget.gadgetbridge.util.gpx.model.GpxFile;
import nodomain.freeyourgadget.gadgetbridge.util.gpx.model.GpxTrackPoint;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.is;
public class GPXParserTest extends TestBase {
@Test
public void shouldReadGPXCorrectly() throws IOException, GpxParseException {
try (final InputStream inputStream = getClass().getResourceAsStream("/gpx-exporter-test-SampleTrack.gpx")) {
GpxParser gpxParser = new GpxParser(inputStream);
List<GpxTrackPoint> trackPoints = gpxParser.getGpxFile().getPoints();
Assert.assertEquals(trackPoints.size(), 14);
DecimalFormat df = new DecimalFormat("###.##");
for (GPSCoordinate tp : trackPoints) {
Assert.assertEquals(df.format(tp.getLongitude()), "-68.2");
Assert.assertEquals(df.format(tp.getLatitude()), "44.15");
Assert.assertThat(df.format(tp.getAltitude()), anyOf(is("40"), is("46")));
}
}
}
@Test
public void shouldParseMultipleSegments() throws IOException, GpxParseException, ParseException {
try (final InputStream inputStream = getClass().getResourceAsStream("/gpx-parser-test-multiple-segments.gpx")) {
final GpxParser gpxParser = new GpxParser(inputStream);
final GpxFile gpxFile = gpxParser.getGpxFile();
Assert.assertEquals(1, gpxFile.getTracks().size());
Assert.assertEquals(2, gpxFile.getTracks().get(0).getTrackSegments().size());
final List<GpxTrackPoint> segment1 = new ArrayList<GpxTrackPoint>() {{
add(new GpxTrackPoint(-8.2695876, -70.6666343, 790.0, new Date(1680969788000L)));
add(new GpxTrackPoint(-8.2653274, -70.6670617, 296.0, new Date(1680970639000L)));
}};
final List<GpxTrackPoint> segment2 = new ArrayList<GpxTrackPoint>() {{
add(new GpxTrackPoint(-8.2653274, -70.6670617, 205.0, new Date(1680971684000L)));
add(new GpxTrackPoint(-8.2695876, -70.6666343, 209.0, new Date(1680973017000L)));
}};
Assert.assertEquals(gpxFile.getTracks().get(0).getTrackSegments().get(0).getTrackPoints(), segment1);
Assert.assertEquals(gpxFile.getTracks().get(0).getTrackSegments().get(1).getTrackPoints(), segment2);
}
}
@Test
public void shouldParseOutOfOrder() throws IOException, GpxParseException {
try (final InputStream inputStream = getClass().getResourceAsStream("/gpx-parser-test-order.gpx")) {
final GpxParser gpxParser = new GpxParser(inputStream);
final GpxFile gpxFile = gpxParser.getGpxFile();
Assert.assertEquals(1, gpxFile.getTracks().size());
Assert.assertEquals(1, gpxFile.getTracks().get(0).getTrackSegments().size());
final List<GpxTrackPoint> segment1 = new ArrayList<GpxTrackPoint>() {{
add(new GpxTrackPoint(-8.2695876, -70.6666343, 790.0, new Date(1680969788000L)));
add(new GpxTrackPoint(-8.2653274, -70.6670617, 296.0, new Date(1680970639000L)));
}};
Assert.assertEquals(gpxFile.getTracks().get(0).getTrackSegments().get(0).getTrackPoints(), segment1);
}
}
}

View File

@ -0,0 +1,57 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<gpx version="1.1" creator="GBApplication.app().getNameAndVersion()"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd
http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns="http://www.topografix.com/GPX/1/1"
xmlns:opentracks="http://opentracksapp.com/xmlschemas/v1">
<metadata>
<name>Test Multiple Segments</name>
<author />
<time>2023-04-09T17:17:52+01:00</time>
</metadata>
<trk>
<trkseg>
<trkpt lon="-8.2695876" lat="-70.6666343">
<ele>790.000000</ele>
<time>2023-04-08T16:03:08Z</time>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>123</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lon="-8.2653274" lat="-70.6670617">
<ele>296.000000</ele>
<time>2023-04-08T16:17:19Z</time>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>56</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
</trkseg>
<trkseg/>
<trkseg>
<trkpt lon="-8.2653274" lat="-70.6670617">
<time>2023-04-08T16:34:44Z</time>
<ele>205.000000</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>85</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lon="-8.2695876" lat="-70.6666343">
<time>2023-04-08T16:56:57Z</time>
<ele>209.000000</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
</trkseg>
</trk>
</gpx>

View File

@ -0,0 +1,36 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<gpx version="1.1" creator="GBApplication.app().getNameAndVersion()"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd
http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns="http://www.topografix.com/GPX/1/1"
xmlns:opentracks="http://opentracksapp.com/xmlschemas/v1">
<metadata>
<name>Order Test</name>
<author />
<time>2023-04-07T16:15:20+01:00</time>
</metadata>
<trk>
<trkseg>
<trkpt lon="-8.2695876" lat="-70.6666343">
<ele>790.000000</ele>
<time>2023-04-08T16:03:08Z</time>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>123</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lon="-8.2653274" lat="-70.6670617">
<time>2023-04-08T16:17:19Z</time>
<ele>296.000000</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>56</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
</trkseg>
</trk>
</gpx>