Fix read / write gpx route extensions

This commit is contained in:
max-klaus 2020-08-16 18:26:22 +03:00
parent dedbbb4c26
commit 66f0b4cd77
5 changed files with 225 additions and 179 deletions

View file

@ -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<WptPt> points = new ArrayList<>();
public List<Route> routes = new ArrayList<>();
public List<RouteSegment> routeSegments = new ArrayList<>();
public List<RouteType> 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<WptPt> 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<StringBundle> segmentsBundle = new ArrayList<>();
for (RouteSegment segment : gpxFile.routeSegments) {
segmentsBundle.add(segment.getStringBundle());
}
bundle.putBundleList("route", "segment", segmentsBundle);
List<StringBundle> 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<GPXExtensions> parserState = new Stack<>();
boolean extensionReadMode = false;
boolean routePointExtension = false;
List<RouteSegment> routeSegments = gpxFile.routeSegments;
List<RouteType> 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<String, String> values = readTextMap(parser, tag);
if (values.size() > 0) {
for (Entry<String, String> 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<String, String> values = readTextMap(parser, tag);
if (values.size() > 0) {
for (Entry<String, String> 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 {

View file

@ -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<String, Item> map = new LinkedHashMap<>();
private Map<String, Item<?>> map = new LinkedHashMap<>();
public enum ItemType {
STRING,
@ -32,7 +32,7 @@ public class StringBundle {
public StringBundle() {
}
protected StringBundle(Map<String, Item> map) {
protected StringBundle(Map<String, Item<?>> map) {
this.map = map;
}
@ -156,16 +156,16 @@ public class StringBundle {
}
}
public static class StringListItem extends Item<List<Item>> {
public static class StringListItem extends Item<List<Item<?>>> {
private StringListItem(String name, List<Item> list) {
private StringListItem(String name, List<Item<?>> list) {
super(name, ItemType.LIST, list);
}
}
public static class StringMapItem extends Item<Map<String, Item>> {
public static class StringMapItem extends Item<Map<String, Item<?>>> {
private StringMapItem(String name, Map<String, Item> map) {
private StringMapItem(String name, Map<String, Item<?>> map) {
super(name, ItemType.MAP, map);
}
}
@ -177,11 +177,11 @@ public class StringBundle {
}
}
public Map<String, Item> getMap() {
public Map<String, Item<?>> 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<? extends StringExternalizable> list) {
if (list != null) {
List<Item> 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<StringBundle> list) {
if (list != null) {
List<Item> itemList = new ArrayList<>();
List<Item<?>> 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;
}

View file

@ -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<String, Item> entry : bundle.getMap().entrySet()) {
for (Entry<String, Item<?>> entry : bundle.getMap().entrySet()) {
writeItem(entry.getKey(), entry.getValue());
}
}

View file

@ -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<Item> list = listItem.getValue();
for (Item i : list) {
List<Item<?>> 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<String, Item> entry : mapItem.getValue().entrySet()) {
Item i = entry.getValue();
for (Entry<String, Item<?>> entry : mapItem.getValue().entrySet()) {
Item<?> i = entry.getValue();
if (i.getType() == StringBundle.ItemType.STRING) {
writeItemImpl(entry.getKey(), i);
}
}
for (Entry<String, Item> entry : mapItem.getValue().entrySet()) {
Item i = entry.getValue();
for (Entry<String, Item<?>> entry : mapItem.getValue().entrySet()) {
Item<?> i = entry.getValue();
if (i.getType() != StringBundle.ItemType.STRING) {
writeItemImpl(entry.getKey(), i);
}

View file

@ -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<RouteSegmentResult> 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<RouteSegmentResult> importRoute() {
final List<RouteSegmentResult> 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<Location> 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<Location> 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);
}
}
}