Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/gpx/GpxParser.java

258 lines
9.3 KiB
Java

/* 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;
case "extensions":
final int hr = parseExtensions();
if (hr >= 0) {
trackPointBuilder.withHeartRate(hr);
}
continue;
}
}
eventType = parser.next();
}
return trackPointBuilder.build();
}
private int parseExtensions() throws Exception {
while (eventType != XmlPullParser.END_TAG || !parser.getName().equals("extensions")) {
if (parser.getEventType() == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case "gpxtpx:TrackPointExtension":
return parseTrackPointExtension();
}
}
eventType = parser.next();
}
return -1;
}
private int parseTrackPointExtension() throws Exception {
while (eventType != XmlPullParser.END_TAG || !parser.getName().equals("gpxtpx:TrackPointExtension")) {
if (parser.getEventType() == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case "gpxtpx:hr":
return Integer.parseInt(parseStringContent("gpxtpx:hr"));
}
}
eventType = parser.next();
}
return -1;
}
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();
}
}
}