From 66f0b4cd77232800ceaad31508b327c0d68d523b Mon Sep 17 00:00:00 2001 From: max-klaus Date: Sun, 16 Aug 2020 18:26:22 +0300 Subject: [PATCH] Fix read / write gpx route extensions --- .../main/java/net/osmand/GPXUtilities.java | 190 +++++++++++++----- .../java/net/osmand/binary/StringBundle.java | 54 ++--- .../net/osmand/binary/StringBundleWriter.java | 4 +- .../osmand/binary/StringBundleXmlWriter.java | 18 +- .../java/net/osmand/router/RouteImporter.java | 138 +++++-------- 5 files changed, 225 insertions(+), 179 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java b/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java index 3b003d1735..48b8a9fbee 100644 --- a/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java +++ b/OsmAnd-java/src/main/java/net/osmand/GPXUtilities.java @@ -2,6 +2,9 @@ package net.osmand; +import net.osmand.binary.StringBundle; +import net.osmand.binary.StringBundleWriter; +import net.osmand.binary.StringBundleXmlWriter; import net.osmand.data.QuadRect; import net.osmand.util.Algorithms; @@ -13,7 +16,6 @@ import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -437,6 +439,44 @@ public class GPXUtilities { public double maxlon; } + public static class RouteSegment { + public String id; + public String length; + public String segmentTime; + public String speed; + public String turnType; + public String turnAngle; + public String types; + public String pointTypes; + public String names; + + public StringBundle getStringBundle() { + StringBundle bundle = new StringBundle(); + bundle.putString("id", id); + bundle.putString("length", length); + bundle.putString("segmentTime", segmentTime); + bundle.putString("speed", speed); + bundle.putString("turnType", turnType); + bundle.putString("turnAngle", turnAngle); + bundle.putString("types", types); + bundle.putString("pointTypes", pointTypes); + bundle.putString("names", names); + return bundle; + } + } + + public static class RouteType { + public String tag; + public String value; + + public StringBundle getStringBundle() { + StringBundle bundle = new StringBundle(); + bundle.putString("t", tag); + bundle.putString("v", value); + return bundle; + } + } + public static class GPXTrackAnalysis { public float totalDistance = 0; public float totalDistanceWithoutGaps = 0; @@ -1006,6 +1046,9 @@ public class GPXUtilities { private List points = new ArrayList<>(); public List routes = new ArrayList<>(); + public List routeSegments = new ArrayList<>(); + public List routeTypes = new ArrayList<>(); + public Exception error = null; public String path = ""; public boolean showCurrentTrack; @@ -1032,6 +1075,10 @@ public class GPXUtilities { } } + public boolean hasRoute() { + return !routeSegments.isEmpty() && !routeTypes.isEmpty(); + } + public List getPoints() { return Collections.unmodifiableList(points); } @@ -1744,6 +1791,7 @@ public class GPXUtilities { serializer.endTag(null, "wpt"); //$NON-NLS-1$ } + assignRouteExtensionWriter(file); writeExtensions(serializer, file); serializer.endTag(null, "gpx"); //$NON-NLS-1$ @@ -1756,6 +1804,29 @@ public class GPXUtilities { return null; } + private static void assignRouteExtensionWriter(final GPXFile gpxFile) { + if (gpxFile.hasRoute() && gpxFile.getExtensionsWriter() == null) { + gpxFile.setExtensionsWriter(new GPXExtensionsWriter() { + @Override + public void writeExtensions(XmlSerializer serializer) { + StringBundle bundle = new StringBundle(); + List segmentsBundle = new ArrayList<>(); + for (RouteSegment segment : gpxFile.routeSegments) { + segmentsBundle.add(segment.getStringBundle()); + } + bundle.putBundleList("route", "segment", segmentsBundle); + List typesBundle = new ArrayList<>(); + for (RouteType routeType : gpxFile.routeTypes) { + typesBundle.add(routeType.getStringBundle()); + } + bundle.putBundleList("types", "type", typesBundle); + StringBundleWriter bundleWriter = new StringBundleXmlWriter(bundle, serializer); + bundleWriter.writeBundle(); + } + }); + } + } + private static String getFilename(String path) { if(path != null) { int i = path.lastIndexOf('/'); @@ -1973,23 +2044,7 @@ public class GPXUtilities { } public static GPXFile loadGPXFile(InputStream f) { - return loadGPXFile(f, null, null); - } - - public static GPXFile loadGPXFile(InputStream f, GPXFile gpxFile, GPXExtensionsReader extensionsReader) { - boolean readExtensionsOnly = false; - if (gpxFile == null) { - gpxFile = new GPXFile(null); - } else { - if (f == null) { - try { - f = new FileInputStream(new File(gpxFile.path)); - } catch (FileNotFoundException e) { - return gpxFile; - } - } - readExtensionsOnly = extensionsReader != null; - } + GPXFile gpxFile = new GPXFile(null); SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT, Locale.US); format.setTimeZone(TimeZone.getTimeZone("UTC")); SimpleDateFormat formatMillis = new SimpleDateFormat(GPX_TIME_FORMAT_MILLIS, Locale.US); @@ -2003,6 +2058,10 @@ public class GPXUtilities { Stack parserState = new Stack<>(); boolean extensionReadMode = false; boolean routePointExtension = false; + List routeSegments = gpxFile.routeSegments; + List routeTypes = gpxFile.routeTypes; + boolean routeExtension = false; + boolean typesExtension = false; parserState.push(gpxFile); int tok; while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -2011,37 +2070,50 @@ public class GPXUtilities { String tag = parser.getName(); if (extensionReadMode && parse != null && !routePointExtension) { String tagName = tag.toLowerCase(); - boolean extensionsRead = false; - if (extensionsReader != null) { - extensionsRead = extensionsReader.readExtensions(gpxFile, parser); + if (routeExtension) { + if (tagName.equals("segment")) { + RouteSegment segment = parseRouteSegmentAttributes(parser); + routeSegments.add(segment); + } + } else if (typesExtension) { + if (tagName.equals("type")) { + RouteType type = parseRouteTypeAttributes(parser); + routeTypes.add(type); + } } - if (!readExtensionsOnly && !extensionsRead) { - switch (tagName) { - case "routepointextension": - routePointExtension = true; - if (parse instanceof WptPt) { - parse.getExtensionsToWrite().put("offset", routeTrackSegment.points.size() + ""); - } - break; + switch (tagName) { + case "routepointextension": + routePointExtension = true; + if (parse instanceof WptPt) { + parse.getExtensionsToWrite().put("offset", routeTrackSegment.points.size() + ""); + } + break; - default: - Map values = readTextMap(parser, tag); - if (values.size() > 0) { - for (Entry entry : values.entrySet()) { - String t = entry.getKey().toLowerCase(); - String value = entry.getValue(); - parse.getExtensionsToWrite().put(t, value); - if (tag.equals("speed") && parse instanceof WptPt) { - try { - ((WptPt) parse).speed = Float.parseFloat(value); - } catch (NumberFormatException e) { - log.debug(e.getMessage(), e); - } + case "route": + routeExtension = true; + break; + + case "types": + typesExtension = true; + break; + + default: + Map values = readTextMap(parser, tag); + if (values.size() > 0) { + for (Entry entry : values.entrySet()) { + String t = entry.getKey().toLowerCase(); + String value = entry.getValue(); + parse.getExtensionsToWrite().put(t, value); + if (tag.equals("speed") && parse instanceof WptPt) { + try { + ((WptPt) parse).speed = Float.parseFloat(value); + } catch (NumberFormatException e) { + log.debug(e.getMessage(), e); } } } - break; - } + } + break; } } else if (parse != null && tag.equals("extensions")) { extensionReadMode = true; @@ -2051,7 +2123,7 @@ public class GPXUtilities { routeTrackSegment.points.add(wptPt); parserState.push(wptPt); } - } else if (!readExtensionsOnly) { + } else { if (parse instanceof GPXFile) { if (tag.equals("gpx")) { ((GPXFile) parse).author = parser.getAttributeValue("", "creator"); @@ -2247,7 +2319,12 @@ public class GPXUtilities { if (parse != null && tag.equals("extensions")) { extensionReadMode = false; } - if (readExtensionsOnly) { + if (extensionReadMode && tag.equals("route")) { + routeExtension = false; + continue; + } + if (extensionReadMode && tag.equals("types")) { + typesExtension = false; continue; } @@ -2327,6 +2404,27 @@ public class GPXUtilities { return wpt; } + private static RouteSegment parseRouteSegmentAttributes(XmlPullParser parser) { + RouteSegment segment = new RouteSegment(); + segment.id = parser.getAttributeValue("", "id"); + segment.length = parser.getAttributeValue("", "length"); + segment.segmentTime = parser.getAttributeValue("", "segmentTime"); + segment.speed = parser.getAttributeValue("", "speed"); + segment.turnType = parser.getAttributeValue("", "turnType"); + segment.turnAngle = parser.getAttributeValue("", "turnAngle"); + segment.types = parser.getAttributeValue("", "types"); + segment.pointTypes = parser.getAttributeValue("", "pointTypes"); + segment.names = parser.getAttributeValue("", "names"); + return segment; + } + + private static RouteType parseRouteTypeAttributes(XmlPullParser parser) { + RouteType type = new RouteType(); + type.tag = parser.getAttributeValue("", "t"); + type.value = parser.getAttributeValue("", "v"); + return type; + } + private static Bounds parseBoundsAttributes(XmlPullParser parser) { Bounds bounds = new Bounds(); try { diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/StringBundle.java b/OsmAnd-java/src/main/java/net/osmand/binary/StringBundle.java index dd66cfc969..ad54015c07 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/StringBundle.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/StringBundle.java @@ -21,7 +21,7 @@ public class StringBundle { private static final DecimalFormat FIVE_DIGITS_FORMATTER = new DecimalFormat("#.#####"); private static final DecimalFormat SIX_DIGITS_FORMATTER = new DecimalFormat("#.######"); - private Map map = new LinkedHashMap<>(); + private Map> map = new LinkedHashMap<>(); public enum ItemType { STRING, @@ -32,7 +32,7 @@ public class StringBundle { public StringBundle() { } - protected StringBundle(Map map) { + protected StringBundle(Map> map) { this.map = map; } @@ -156,16 +156,16 @@ public class StringBundle { } } - public static class StringListItem extends Item> { + public static class StringListItem extends Item>> { - private StringListItem(String name, List list) { + private StringListItem(String name, List> list) { super(name, ItemType.LIST, list); } } - public static class StringMapItem extends Item> { + public static class StringMapItem extends Item>> { - private StringMapItem(String name, Map map) { + private StringMapItem(String name, Map> map) { super(name, ItemType.MAP, map); } } @@ -177,11 +177,11 @@ public class StringBundle { } } - public Map getMap() { + public Map> getMap() { return Collections.unmodifiableMap(map); } - public Item getItem(String key) { + public Item getItem(String key) { return map.get(key); } @@ -190,7 +190,7 @@ public class StringBundle { } public int getInt(String key, int defaultValue) { - Item item = map.get(key); + Item item = map.get(key); return item instanceof StringItem ? ((StringItem) item).asInt(defaultValue) : defaultValue; } @@ -199,7 +199,7 @@ public class StringBundle { } public long getLong(String key, long defaultValue) { - Item item = map.get(key); + Item item = map.get(key); return item instanceof StringItem ? ((StringItem) item).asLong(defaultValue) : defaultValue; } @@ -212,7 +212,7 @@ public class StringBundle { } public float getFloat(String key, float defaultValue) { - Item item = map.get(key); + Item item = map.get(key); return item instanceof StringItem ? ((StringItem) item).asFloat(defaultValue) : defaultValue; } @@ -221,7 +221,7 @@ public class StringBundle { } public boolean getBoolean(String key, boolean defaultValue) { - Item item = map.get(key); + Item item = map.get(key); return item instanceof StringItem ? ((StringItem) item).asBoolean(defaultValue) : defaultValue; } @@ -232,35 +232,13 @@ public class StringBundle { } public String getString(String key, String defaultValue) { - Item item = map.get(key); + Item item = map.get(key); return item instanceof StringItem ? ((StringItem) item).getValue() : defaultValue; } - public void putObject(String key, StringExternalizable object) { - if (object != null) { - StringBundle bundle = newInstance(); - object.writeToBundle(bundle); - map.put(key, new StringBundleItem(key, bundle)); - } - } - - public void putList(String key, String itemName, List list) { - if (list != null) { - List itemList = new ArrayList<>(); - for (StringExternalizable ex : list) { - if (ex != null) { - StringBundle bundle = newInstance(); - ex.writeToBundle(bundle); - itemList.add(new StringBundleItem(itemName, bundle)); - } - } - map.put(key, new StringListItem(key, itemList)); - } - } - public void putBundleList(String key, String itemName, List list) { if (list != null) { - List itemList = new ArrayList<>(); + List> itemList = new ArrayList<>(); for (StringBundle bundle : list) { itemList.add(new StringBundleItem(itemName, bundle)); } @@ -279,7 +257,7 @@ public class StringBundle { } public int[] getIntArray(String key, int[] defaultValue) { - Item item = map.get(key); + Item item = map.get(key); return item instanceof StringItem ? ((StringItem) item).asIntArray(defaultValue) : defaultValue; } @@ -290,7 +268,7 @@ public class StringBundle { } public int[][] getIntIntArray(String key, int[][] defaultValue) { - Item item = map.get(key); + Item item = map.get(key); return item instanceof StringItem ? ((StringItem) item).asIntIntArray(defaultValue) : defaultValue; } diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleWriter.java b/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleWriter.java index 36edaf268c..2f607aef3a 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleWriter.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleWriter.java @@ -16,10 +16,10 @@ public abstract class StringBundleWriter { return bundle; } - protected abstract void writeItem(String name, Item item); + protected abstract void writeItem(String name, Item item); public void writeBundle() { - for (Entry entry : bundle.getMap().entrySet()) { + for (Entry> entry : bundle.getMap().entrySet()) { writeItem(entry.getKey(), entry.getValue()); } } diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleXmlWriter.java b/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleXmlWriter.java index a676f20c88..f1c8dc1054 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleXmlWriter.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/StringBundleXmlWriter.java @@ -25,7 +25,7 @@ public class StringBundleXmlWriter extends StringBundleWriter { } @Override - protected void writeItem(String name, Item item) { + protected void writeItem(String name, Item item) { if (serializer != null) { try { writeItemImpl(name, item); @@ -47,7 +47,7 @@ public class StringBundleXmlWriter extends StringBundleWriter { } } - private void writeItemImpl(String name, Item item) throws IOException { + private void writeItemImpl(String name, Item item) throws IOException { if (serializer != null && item != null) { switch (item.getType()) { case STRING: { @@ -58,13 +58,13 @@ public class StringBundleXmlWriter extends StringBundleWriter { case LIST: { StringListItem listItem = (StringListItem) item; serializer.startTag(null, name); - List list = listItem.getValue(); - for (Item i : list) { + List> list = listItem.getValue(); + for (Item i : list) { if (i.getType() == StringBundle.ItemType.STRING) { writeItemImpl(i.getName(), i); } } - for (Item i : list) { + for (Item i : list) { if (i.getType() != StringBundle.ItemType.STRING) { writeItemImpl(i.getName(), i); } @@ -75,14 +75,14 @@ public class StringBundleXmlWriter extends StringBundleWriter { case MAP: { StringMapItem mapItem = (StringMapItem) item; serializer.startTag(null, name); - for (Entry entry : mapItem.getValue().entrySet()) { - Item i = entry.getValue(); + for (Entry> entry : mapItem.getValue().entrySet()) { + Item i = entry.getValue(); if (i.getType() == StringBundle.ItemType.STRING) { writeItemImpl(entry.getKey(), i); } } - for (Entry entry : mapItem.getValue().entrySet()) { - Item i = entry.getValue(); + for (Entry> entry : mapItem.getValue().entrySet()) { + Item i = entry.getValue(); if (i.getType() != StringBundle.ItemType.STRING) { writeItemImpl(entry.getKey(), i); } diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteImporter.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteImporter.java index db6e14a31f..9c790229a1 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RouteImporter.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteImporter.java @@ -1,8 +1,9 @@ package net.osmand.router; import net.osmand.GPXUtilities; -import net.osmand.GPXUtilities.GPXExtensionsReader; import net.osmand.GPXUtilities.GPXFile; +import net.osmand.GPXUtilities.RouteSegment; +import net.osmand.GPXUtilities.RouteType; import net.osmand.GPXUtilities.WptPt; import net.osmand.Location; import net.osmand.PlatformUtil; @@ -10,11 +11,8 @@ import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion; import net.osmand.binary.RouteDataBundle; import net.osmand.binary.RouteDataObject; import net.osmand.binary.StringBundle; -import net.osmand.binary.StringBundleReader; -import net.osmand.binary.StringBundleXmlReader; import org.apache.commons.logging.Log; -import org.xmlpull.v1.XmlPullParser; import java.io.File; import java.io.FileInputStream; @@ -31,6 +29,10 @@ public class RouteImporter { private File file; private GPXFile gpxFile; + private List route = new ArrayList<>(); + private RouteRegion region = new RouteRegion(); + private RouteDataResources resources = new RouteDataResources(); + public RouteImporter(File file) { this.file = file; } @@ -40,90 +42,14 @@ public class RouteImporter { } public List importRoute() { - - final List route = new ArrayList<>(); - final RouteRegion region = new RouteRegion(); - final RouteDataResources resources = new RouteDataResources(); - - GPXExtensionsReader extensionsReader = new GPXExtensionsReader() { - @Override - public boolean readExtensions(GPXFile res, XmlPullParser parser) throws Exception { - if (!resources.hasLocations()) { - List locations = resources.getLocations(); - double lastElevation = HEIGHT_UNDEFINED; - if (res.tracks.size() > 0 && res.tracks.get(0).segments.size() > 0 && res.tracks.get(0).segments.get(0).points.size() > 0) { - for (WptPt point : res.tracks.get(0).segments.get(0).points) { - Location loc = new Location("", point.getLatitude(), point.getLongitude()); - if (!Double.isNaN(point.ele)) { - loc.setAltitude(point.ele); - lastElevation = point.ele; - } else if (lastElevation != HEIGHT_UNDEFINED) { - loc.setAltitude(lastElevation); - } - locations.add(loc); - } - } - } - String tag = parser.getName(); - if ("route".equals(tag)) { - int tok; - while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (tok == XmlPullParser.START_TAG) { - tag = parser.getName(); - if ("segment".equals(tag)) { - StringBundleReader bundleReader = new StringBundleXmlReader(parser); - RouteDataObject object = new RouteDataObject(region); - RouteSegmentResult segment = new RouteSegmentResult(object); - bundleReader.readBundle(); - segment.readFromBundle(new RouteDataBundle(resources, bundleReader.getBundle())); - route.add(segment); - } - } else if (tok == XmlPullParser.END_TAG) { - tag = parser.getName(); - if ("route".equals(tag)) { - return true; - } - } - } - } else if ("types".equals(tag)) { - int tok; - int i = 0; - while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (tok == XmlPullParser.START_TAG) { - tag = parser.getName(); - if ("type".equals(tag)) { - StringBundleReader bundleReader = new StringBundleXmlReader(parser); - bundleReader.readBundle(); - StringBundle bundle = bundleReader.getBundle(); - String t = bundle.getString("t", null); - String v = bundle.getString("v", null); - region.initRouteEncodingRule(i++, t, v); - } - } else if (tok == XmlPullParser.END_TAG) { - tag = parser.getName(); - if ("types".equals(tag)) { - return true; - } - } - } - } - return false; - } - }; - if (gpxFile != null) { - GPXUtilities.loadGPXFile(null, gpxFile, extensionsReader); - for (RouteSegmentResult segment : route) { - segment.fillNames(resources); - } + parseRoute(); } else if (file != null) { FileInputStream fis = null; try { fis = new FileInputStream(file); - GPXFile gpxFile = GPXUtilities.loadGPXFile(fis, null, extensionsReader); - for (RouteSegmentResult segment : route) { - segment.fillNames(resources); - } + gpxFile = GPXUtilities.loadGPXFile(fis); + parseRoute(); gpxFile.path = file.getAbsolutePath(); gpxFile.modifiedTime = file.lastModified(); } catch (IOException e) { @@ -139,7 +65,51 @@ public class RouteImporter { } } } - return route; } + + private void parseRoute() { + collectLocations(); + collectSegments(); + collectTypes(); + for (RouteSegmentResult segment : route) { + segment.fillNames(resources); + } + } + + private void collectLocations() { + List locations = resources.getLocations(); + double lastElevation = HEIGHT_UNDEFINED; + if (gpxFile.tracks.size() > 0 && gpxFile.tracks.get(0).segments.size() > 0 && gpxFile.tracks.get(0).segments.get(0).points.size() > 0) { + for (WptPt point : gpxFile.tracks.get(0).segments.get(0).points) { + Location loc = new Location("", point.getLatitude(), point.getLongitude()); + if (!Double.isNaN(point.ele)) { + loc.setAltitude(point.ele); + lastElevation = point.ele; + } else if (lastElevation != HEIGHT_UNDEFINED) { + loc.setAltitude(lastElevation); + } + locations.add(loc); + } + } + } + + private void collectSegments() { + for (RouteSegment segment : gpxFile.routeSegments) { + RouteDataObject object = new RouteDataObject(region); + RouteSegmentResult segmentResult = new RouteSegmentResult(object); + segmentResult.readFromBundle(new RouteDataBundle(resources, segment.getStringBundle())); + route.add(segmentResult); + } + } + + private void collectTypes() { + int i = 0; + for (RouteType routeType : gpxFile.routeTypes) { + StringBundle bundle = routeType.getStringBundle(); + String t = bundle.getString("t", null); + String v = bundle.getString("v", null); + region.initRouteEncodingRule(i++, t, v); + } + } }