Gpx approx refactor (first stage)

This commit is contained in:
max-klaus 2020-10-24 20:55:22 +03:00
parent 267d1374b5
commit b1d714a62c
27 changed files with 889 additions and 715 deletions

View file

@ -52,6 +52,7 @@ public class GPXUtilities {
private static final String DEFAULT_ICON_NAME = "special_star"; private static final String DEFAULT_ICON_NAME = "special_star";
private static final String BACKGROUND_TYPE_EXTENSION = "background"; private static final String BACKGROUND_TYPE_EXTENSION = "background";
private static final String PROFILE_TYPE_EXTENSION = "profile"; private static final String PROFILE_TYPE_EXTENSION = "profile";
private static final String GAP_PROFILE_TYPE = "gap";
private static final String TRKPT_INDEX_EXTENSION = "trkpt_idx"; private static final String TRKPT_INDEX_EXTENSION = "trkpt_idx";
private final static String GPX_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; //$NON-NLS-1$ private final static String GPX_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; //$NON-NLS-1$
@ -324,6 +325,11 @@ public class GPXUtilities {
getExtensionsToWrite().put(PROFILE_TYPE_EXTENSION, profileType); getExtensionsToWrite().put(PROFILE_TYPE_EXTENSION, profileType);
} }
public boolean hasProfile() {
String profileType = getProfileType();
return profileType != null && !GAP_PROFILE_TYPE.equals(profileType);
}
public void removeProfileType() { public void removeProfileType() {
getExtensionsToWrite().remove(PROFILE_TYPE_EXTENSION); getExtensionsToWrite().remove(PROFILE_TYPE_EXTENSION);
} }
@ -374,11 +380,16 @@ public class GPXUtilities {
public static class TrkSegment extends GPXExtensions { public static class TrkSegment extends GPXExtensions {
public boolean generalSegment = false; public boolean generalSegment = false;
public List<WptPt> points = new ArrayList<>(); public List<WptPt> points = new ArrayList<>();
public Object renderer; public Object renderer;
public List<RouteSegment> routeSegments = new ArrayList<>();
public List<RouteType> routeTypes = new ArrayList<>();
public boolean hasRoute() {
return !routeSegments.isEmpty() && !routeTypes.isEmpty();
}
public List<GPXTrackAnalysis> splitByDistance(double meters, boolean joinSegments) { public List<GPXTrackAnalysis> splitByDistance(double meters, boolean joinSegments) {
return split(getDistanceMetric(), getTimeSplit(), meters, joinSegments); return split(getDistanceMetric(), getTimeSplit(), meters, joinSegments);
@ -393,7 +404,6 @@ public class GPXUtilities {
splitSegment(metric, secondaryMetric, metricLimit, splitSegments, this, joinSegments); splitSegment(metric, secondaryMetric, metricLimit, splitSegments, this, joinSegments);
return convert(splitSegments); return convert(splitSegments);
} }
} }
public static class Track extends GPXExtensions { public static class Track extends GPXExtensions {
@ -1078,9 +1088,6 @@ public class GPXUtilities {
private List<WptPt> points = new ArrayList<>(); private List<WptPt> points = new ArrayList<>();
public List<Route> routes = 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 Exception error = null;
public String path = ""; public String path = "";
public boolean showCurrentTrack; public boolean showCurrentTrack;
@ -1108,7 +1115,7 @@ public class GPXUtilities {
} }
public boolean hasRoute() { public boolean hasRoute() {
return !routeSegments.isEmpty() && !routeTypes.isEmpty(); return getNonEmptyTrkSegments(true).size() > 0;
} }
public List<WptPt> getPoints() { public List<WptPt> getPoints() {
@ -1218,7 +1225,7 @@ public class GPXUtilities {
GPXTrackAnalysis g = new GPXTrackAnalysis(); GPXTrackAnalysis g = new GPXTrackAnalysis();
g.wptPoints = points.size(); g.wptPoints = points.size();
g.wptCategoryNames = getWaypointCategories(true); g.wptCategoryNames = getWaypointCategories(true);
List<SplitSegment> splitSegments = new ArrayList<GPXUtilities.SplitSegment>(); List<SplitSegment> splitSegments = new ArrayList<>();
for (int i = 0; i < tracks.size(); i++) { for (int i = 0; i < tracks.size(); i++) {
Track subtrack = tracks.get(i); Track subtrack = tracks.get(i);
for (TrkSegment segment : subtrack.segments) { for (TrkSegment segment : subtrack.segments) {
@ -1243,6 +1250,15 @@ public class GPXUtilities {
return points; return points;
} }
public List<WptPt> getRoutePoints(int routeIndex) {
List<WptPt> points = new ArrayList<>();
if (routes.size() > routeIndex) {
Route rt = routes.get(routeIndex);
points.addAll(rt.points);
}
return points;
}
public boolean hasRtePt() { public boolean hasRtePt() {
for (Route r : routes) { for (Route r : routes) {
if (r.points.size() > 0) { if (r.points.size() > 0) {
@ -1318,15 +1334,16 @@ public class GPXUtilities {
return pt; return pt;
} }
public TrkSegment getNonEmptyTrkSegment() { public List<TrkSegment> getNonEmptyTrkSegments(boolean routesOnly) {
for (GPXUtilities.Track t : tracks) { List<TrkSegment> segments = new ArrayList<>();
for (Track t : tracks) {
for (TrkSegment s : t.segments) { for (TrkSegment s : t.segments) {
if (s.points.size() > 0) { if (!s.generalSegment && s.points.size() > 0 && (!routesOnly || s.hasRoute())) {
return s; segments.add(s);
} }
} }
} }
return null; return segments;
} }
public void addTrkSegment(List<WptPt> points) { public void addTrkSegment(List<WptPt> points) {
@ -1365,8 +1382,8 @@ public class GPXUtilities {
return false; return false;
} }
public void addRoutePoints(List<WptPt> points) { public void addRoutePoints(List<WptPt> points, boolean addRoute) {
if (routes.size() == 0) { if (routes.size() == 0 || addRoute) {
Route route = new Route(); Route route = new Route();
routes.add(route); routes.add(route);
} }
@ -1608,7 +1625,7 @@ public class GPXUtilities {
bottom = Math.min(bottom, p.getLatitude()); bottom = Math.min(bottom, p.getLatitude());
} }
} }
for (GPXUtilities.Route route : routes) { for (Route route : routes) {
for (WptPt p : route.points) { for (WptPt p : route.points) {
if (left == 0 && right == 0) { if (left == 0 && right == 0) {
left = p.getLongitude(); left = p.getLongitude();
@ -1720,7 +1737,7 @@ public class GPXUtilities {
public static String asString(GPXFile file) { public static String asString(GPXFile file) {
final Writer writer = new StringWriter(); final Writer writer = new StringWriter();
GPXUtilities.writeGpx(writer, file); writeGpx(writer, file);
return writer.toString(); return writer.toString();
} }
@ -1807,6 +1824,8 @@ public class GPXUtilities {
writeWpt(format, serializer, p); writeWpt(format, serializer, p);
serializer.endTag(null, "trkpt"); //$NON-NLS-1$ serializer.endTag(null, "trkpt"); //$NON-NLS-1$
} }
assignRouteExtensionWriter(segment);
writeExtensions(serializer, segment);
serializer.endTag(null, "trkseg"); //$NON-NLS-1$ serializer.endTag(null, "trkseg"); //$NON-NLS-1$
} }
writeExtensions(serializer, track); writeExtensions(serializer, track);
@ -1834,7 +1853,6 @@ public class GPXUtilities {
serializer.endTag(null, "wpt"); //$NON-NLS-1$ serializer.endTag(null, "wpt"); //$NON-NLS-1$
} }
assignRouteExtensionWriter(file);
writeExtensions(serializer, file); writeExtensions(serializer, file);
serializer.endTag(null, "gpx"); //$NON-NLS-1$ serializer.endTag(null, "gpx"); //$NON-NLS-1$
@ -1847,19 +1865,19 @@ public class GPXUtilities {
return null; return null;
} }
private static void assignRouteExtensionWriter(final GPXFile gpxFile) { private static void assignRouteExtensionWriter(final TrkSegment segment) {
if (gpxFile.hasRoute() && gpxFile.getExtensionsWriter() == null) { if (segment.hasRoute() && segment.getExtensionsWriter() == null) {
gpxFile.setExtensionsWriter(new GPXExtensionsWriter() { segment.setExtensionsWriter(new GPXExtensionsWriter() {
@Override @Override
public void writeExtensions(XmlSerializer serializer) { public void writeExtensions(XmlSerializer serializer) {
StringBundle bundle = new StringBundle(); StringBundle bundle = new StringBundle();
List<StringBundle> segmentsBundle = new ArrayList<>(); List<StringBundle> segmentsBundle = new ArrayList<>();
for (RouteSegment segment : gpxFile.routeSegments) { for (RouteSegment segment : segment.routeSegments) {
segmentsBundle.add(segment.toStringBundle()); segmentsBundle.add(segment.toStringBundle());
} }
bundle.putBundleList("route", "segment", segmentsBundle); bundle.putBundleList("route", "segment", segmentsBundle);
List<StringBundle> typesBundle = new ArrayList<>(); List<StringBundle> typesBundle = new ArrayList<>();
for (RouteType routeType : gpxFile.routeTypes) { for (RouteType routeType : segment.routeTypes) {
typesBundle.add(routeType.toStringBundle()); typesBundle.add(routeType.toStringBundle());
} }
bundle.putBundleList("types", "type", typesBundle); bundle.putBundleList("types", "type", typesBundle);
@ -1901,12 +1919,15 @@ public class GPXUtilities {
} }
private static void writeExtensions(XmlSerializer serializer, GPXExtensions p) throws IOException { private static void writeExtensions(XmlSerializer serializer, GPXExtensions p) throws IOException {
Map<String, String> extensionsToRead = p.getExtensionsToRead(); writeExtensions(serializer, p.getExtensionsToRead(), p);
}
private static void writeExtensions(XmlSerializer serializer, Map<String, String> extensions, GPXExtensions p) throws IOException {
GPXExtensionsWriter extensionsWriter = p.getExtensionsWriter(); GPXExtensionsWriter extensionsWriter = p.getExtensionsWriter();
if (!extensionsToRead.isEmpty() || extensionsWriter != null) { if (!extensions.isEmpty() || extensionsWriter != null) {
serializer.startTag(null, "extensions"); serializer.startTag(null, "extensions");
if (!extensionsToRead.isEmpty()) { if (!extensions.isEmpty()) {
for (Entry<String, String> s : extensionsToRead.entrySet()) { for (Entry<String, String> s : extensions.entrySet()) {
writeNotNullText(serializer, s.getKey(), s.getValue()); writeNotNullText(serializer, s.getKey(), s.getValue());
} }
} }
@ -1943,7 +1964,20 @@ public class GPXUtilities {
if (!Float.isNaN(p.heading)) { if (!Float.isNaN(p.heading)) {
p.getExtensionsToWrite().put("heading", String.valueOf(Math.round(p.heading))); p.getExtensionsToWrite().put("heading", String.valueOf(Math.round(p.heading)));
} }
writeExtensions(serializer, p); Map<String, String> extensions = p.getExtensionsToRead();
if (!"rtept".equals(serializer.getName())) {
// Leave "profile" and "trkpt" tags for rtept only
extensions.remove(PROFILE_TYPE_EXTENSION);
extensions.remove(TRKPT_INDEX_EXTENSION);
writeExtensions(serializer, extensions, p);
} else {
// Remove "gap" profile
String profile = extensions.get(PROFILE_TYPE_EXTENSION);
if (GAP_PROFILE_TYPE.equals(profile)) {
extensions.remove(PROFILE_TYPE_EXTENSION);
}
writeExtensions(serializer, p);
}
} }
private static void writeAuthor(XmlSerializer serializer, Author author) throws IOException { private static void writeAuthor(XmlSerializer serializer, Author author) throws IOException {
@ -2099,10 +2133,11 @@ public class GPXUtilities {
TrkSegment routeTrackSegment = new TrkSegment(); TrkSegment routeTrackSegment = new TrkSegment();
routeTrack.segments.add(routeTrackSegment); routeTrack.segments.add(routeTrackSegment);
Stack<GPXExtensions> parserState = new Stack<>(); Stack<GPXExtensions> parserState = new Stack<>();
TrkSegment firstSegment = null;
boolean extensionReadMode = false; boolean extensionReadMode = false;
boolean routePointExtension = false; boolean routePointExtension = false;
List<RouteSegment> routeSegments = gpxFile.routeSegments; List<RouteSegment> routeSegments = new ArrayList<>();
List<RouteType> routeTypes = gpxFile.routeTypes; List<RouteType> routeTypes = new ArrayList<>();
boolean routeExtension = false; boolean routeExtension = false;
boolean typesExtension = false; boolean typesExtension = false;
parserState.push(gpxFile); parserState.push(gpxFile);
@ -2403,6 +2438,16 @@ public class GPXUtilities {
assert pop instanceof Route; assert pop instanceof Route;
} else if (tag.equals("trkseg")) { } else if (tag.equals("trkseg")) {
Object pop = parserState.pop(); Object pop = parserState.pop();
if (pop instanceof TrkSegment) {
TrkSegment segment = (TrkSegment) pop;
segment.routeSegments = routeSegments;
segment.routeTypes = routeTypes;
routeSegments = new ArrayList<>();
routeTypes = new ArrayList<>();
if (firstSegment == null) {
firstSegment = segment;
}
}
assert pop instanceof TrkSegment; assert pop instanceof TrkSegment;
} else if (tag.equals("rpt")) { } else if (tag.equals("rpt")) {
Object pop = parserState.pop(); Object pop = parserState.pop();
@ -2413,6 +2458,10 @@ public class GPXUtilities {
if (!routeTrackSegment.points.isEmpty()) { if (!routeTrackSegment.points.isEmpty()) {
gpxFile.tracks.add(routeTrack); gpxFile.tracks.add(routeTrack);
} }
if (!routeSegments.isEmpty() && !routeTypes.isEmpty() && firstSegment != null) {
firstSegment.routeSegments = routeSegments;
firstSegment.routeTypes = routeTypes;
}
} catch (Exception e) { } catch (Exception e) {
gpxFile.error = e; gpxFile.error = e;
log.error("Error reading gpx", e); //$NON-NLS-1$ log.error("Error reading gpx", e); //$NON-NLS-1$

View file

@ -20,10 +20,10 @@ public class RouteExporter {
public static final String OSMAND_ROUTER_V2 = "OsmAndRouterV2"; public static final String OSMAND_ROUTER_V2 = "OsmAndRouterV2";
private String name; private final String name;
private List<RouteSegmentResult> route; private final List<RouteSegmentResult> route;
private List<Location> locations; private final List<Location> locations;
private List<WptPt> points; private final List<WptPt> points;
public RouteExporter(String name, List<RouteSegmentResult> route, List<Location> locations, List<WptPt> points) { public RouteExporter(String name, List<RouteSegmentResult> route, List<Location> locations, List<WptPt> points) {
this.name = name; this.name = name;
@ -33,6 +33,34 @@ public class RouteExporter {
} }
public GPXFile exportRoute() { public GPXFile exportRoute() {
GPXFile gpx = new GPXFile(OSMAND_ROUTER_V2);
Track track = new Track();
track.name = name;
gpx.tracks.add(track);
track.segments.add(generateRouteSegment());
if (points != null) {
for (WptPt pt : points) {
gpx.addPoint(pt);
}
}
return gpx;
}
public static GPXFile exportRoute(String name, List<TrkSegment> trkSegments, List<WptPt> points) {
GPXFile gpx = new GPXFile(OSMAND_ROUTER_V2);
Track track = new Track();
track.name = name;
gpx.tracks.add(track);
track.segments.addAll(trkSegments);
if (points != null) {
for (WptPt pt : points) {
gpx.addPoint(pt);
}
}
return gpx;
}
public TrkSegment generateRouteSegment() {
RouteDataResources resources = new RouteDataResources(locations); RouteDataResources resources = new RouteDataResources(locations);
List<StringBundle> routeItems = new ArrayList<>(); List<StringBundle> routeItems = new ArrayList<>();
if (!Algorithms.isEmpty(route)) { if (!Algorithms.isEmpty(route)) {
@ -57,15 +85,9 @@ public class RouteExporter {
typeList.add(typeBundle); typeList.add(typeBundle);
} }
GPXFile gpx = new GPXFile(OSMAND_ROUTER_V2);
Track track = new Track();
track.name = name;
gpx.tracks.add(track);
TrkSegment trkSegment = new TrkSegment(); TrkSegment trkSegment = new TrkSegment();
track.segments.add(trkSegment);
if (locations == null || locations.isEmpty()) { if (locations == null || locations.isEmpty()) {
return gpx; return trkSegment;
} }
for (int i = 0; i < locations.size(); i++) { for (int i = 0; i < locations.size(); i++) {
Location loc = locations.get(i); Location loc = locations.get(i);
@ -83,23 +105,17 @@ public class RouteExporter {
} }
trkSegment.points.add(pt); trkSegment.points.add(pt);
} }
if (points != null) {
for (WptPt pt : points) {
gpx.addPoint(pt);
}
}
List<RouteSegment> routeSegments = new ArrayList<>(); List<RouteSegment> routeSegments = new ArrayList<>();
for (StringBundle item : routeItems) { for (StringBundle item : routeItems) {
routeSegments.add(RouteSegment.fromStringBundle(item)); routeSegments.add(RouteSegment.fromStringBundle(item));
} }
gpx.routeSegments = routeSegments; trkSegment.routeSegments = routeSegments;
List<RouteType> routeTypes = new ArrayList<>(); List<RouteType> routeTypes = new ArrayList<>();
for (StringBundle item : typeList) { for (StringBundle item : typeList) {
routeTypes.add(RouteType.fromStringBundle(item)); routeTypes.add(RouteType.fromStringBundle(item));
} }
gpx.routeTypes = routeTypes; trkSegment.routeTypes = routeTypes;
return trkSegment;
return gpx;
} }
} }

View file

@ -4,6 +4,7 @@ import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.RouteSegment; import net.osmand.GPXUtilities.RouteSegment;
import net.osmand.GPXUtilities.RouteType; import net.osmand.GPXUtilities.RouteType;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.Location; import net.osmand.Location;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
@ -28,10 +29,9 @@ public class RouteImporter {
private File file; private File file;
private GPXFile gpxFile; private GPXFile gpxFile;
private TrkSegment segment;
private List<RouteSegmentResult> route = new ArrayList<>(); private final List<RouteSegmentResult> route = new ArrayList<>();
private RouteRegion region = new RouteRegion();
private RouteDataResources resources = new RouteDataResources();
public RouteImporter(File file) { public RouteImporter(File file) {
this.file = file; this.file = file;
@ -41,8 +41,12 @@ public class RouteImporter {
this.gpxFile = gpxFile; this.gpxFile = gpxFile;
} }
public RouteImporter(TrkSegment segment) {
this.segment = segment;
}
public List<RouteSegmentResult> importRoute() { public List<RouteSegmentResult> importRoute() {
if (gpxFile != null) { if (gpxFile != null || segment != null) {
parseRoute(); parseRoute();
} else if (file != null) { } else if (file != null) {
FileInputStream fis = null; FileInputStream fis = null;
@ -69,19 +73,34 @@ public class RouteImporter {
} }
private void parseRoute() { private void parseRoute() {
collectLocations(); if (segment != null) {
collectSegments(); parseRoute(segment);
collectTypes(); } else if (gpxFile != null) {
for (RouteSegmentResult segment : route) { List<TrkSegment> segments = gpxFile.getNonEmptyTrkSegments(true);
segment.fillNames(resources); for (TrkSegment s : segments) {
parseRoute(s);
}
} }
} }
private void collectLocations() { private void parseRoute(TrkSegment segment) {
RouteRegion region = new RouteRegion();
RouteDataResources resources = new RouteDataResources();
collectLocations(resources, segment);
List<RouteSegmentResult> route = collectRouteSegments(region, resources, segment);
collectRouteTypes(region, segment);
for (RouteSegmentResult routeSegment : route) {
routeSegment.fillNames(resources);
}
this.route.addAll(route);
}
private void collectLocations(RouteDataResources resources, TrkSegment segment) {
List<Location> locations = resources.getLocations(); List<Location> locations = resources.getLocations();
double lastElevation = HEIGHT_UNDEFINED; 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) { if (segment.hasRoute()) {
for (WptPt point : gpxFile.tracks.get(0).segments.get(0).points) { for (WptPt point : segment.points) {
Location loc = new Location("", point.getLatitude(), point.getLongitude()); Location loc = new Location("", point.getLatitude(), point.getLongitude());
if (!Double.isNaN(point.ele)) { if (!Double.isNaN(point.ele)) {
loc.setAltitude(point.ele); loc.setAltitude(point.ele);
@ -94,18 +113,20 @@ public class RouteImporter {
} }
} }
private void collectSegments() { private List<RouteSegmentResult> collectRouteSegments(RouteRegion region, RouteDataResources resources, TrkSegment segment) {
for (RouteSegment segment : gpxFile.routeSegments) { List<RouteSegmentResult> route = new ArrayList<>();
for (RouteSegment routeSegment : segment.routeSegments) {
RouteDataObject object = new RouteDataObject(region); RouteDataObject object = new RouteDataObject(region);
RouteSegmentResult segmentResult = new RouteSegmentResult(object); RouteSegmentResult segmentResult = new RouteSegmentResult(object);
segmentResult.readFromBundle(new RouteDataBundle(resources, segment.toStringBundle())); segmentResult.readFromBundle(new RouteDataBundle(resources, routeSegment.toStringBundle()));
route.add(segmentResult); route.add(segmentResult);
} }
return route;
} }
private void collectTypes() { private void collectRouteTypes(RouteRegion region, TrkSegment segment) {
int i = 0; int i = 0;
for (RouteType routeType : gpxFile.routeTypes) { for (RouteType routeType : segment.routeTypes) {
StringBundle bundle = routeType.toStringBundle(); StringBundle bundle = routeType.toStringBundle();
String t = bundle.getString("t", null); String t = bundle.getString("t", null);
String v = bundle.getString("v", null); String v = bundle.getString("v", null);

View file

@ -243,9 +243,6 @@ public class RoutePlannerFrontEnd {
start = gpxPoints.get(0); start = gpxPoints.get(0);
} }
while (start != null && !gctx.ctx.calculationProgress.isCancelled) { while (start != null && !gctx.ctx.calculationProgress.isCancelled) {
if (Thread.currentThread().isInterrupted()) {
return null;
}
double routeDist = gctx.MAXIMUM_STEP_APPROXIMATION; double routeDist = gctx.MAXIMUM_STEP_APPROXIMATION;
GpxPoint next = findNextGpxPointWithin(gctx, gpxPoints, start, routeDist); GpxPoint next = findNextGpxPointWithin(gctx, gpxPoints, start, routeDist);
boolean routeFound = false; boolean routeFound = false;

View file

@ -255,7 +255,8 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
@Override @Override
public void writeToBundle(RouteDataBundle bundle) { public void writeToBundle(RouteDataBundle bundle) {
Map<RouteTypeRule, Integer> rules = bundle.getResources().getRules(); Map<RouteTypeRule, Integer> rules = bundle.getResources().getRules();
bundle.putInt("length", (Math.abs(endPointIndex - startPointIndex) + 1) * (endPointIndex >= startPointIndex ? 1 : -1)); boolean reversed = endPointIndex < startPointIndex;
bundle.putInt("length", Math.abs(endPointIndex - startPointIndex) + 1);
bundle.putFloat("segmentTime", segmentTime, 2); bundle.putFloat("segmentTime", segmentTime, 2);
bundle.putFloat("speed", speed, 2); bundle.putFloat("speed", speed, 2);
if (turnType != null) { if (turnType != null) {
@ -278,17 +279,22 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
int end = Math.max(startPointIndex, endPointIndex) + 1; int end = Math.max(startPointIndex, endPointIndex) + 1;
if (object.pointTypes != null && start < object.pointTypes.length) { if (object.pointTypes != null && start < object.pointTypes.length) {
int[][] types = Arrays.copyOfRange(object.pointTypes, start, Math.min(end, object.pointTypes.length)); int[][] types = Arrays.copyOfRange(object.pointTypes, start, Math.min(end, object.pointTypes.length));
if (reversed) {
Algorithms.reverseArray(types);
}
bundle.putArray("pointTypes", convertTypes(types, rules)); bundle.putArray("pointTypes", convertTypes(types, rules));
} }
if (object.nameIds != null) { if (object.nameIds != null) {
bundle.putArray("names", convertNameIds(object.nameIds, rules)); bundle.putArray("names", convertNameIds(object.nameIds, rules));
} }
if (object.pointNameTypes != null && start < object.pointNameTypes.length) { if (object.pointNameTypes != null && start < object.pointNameTypes.length && object.pointNames != null) {
int[][] types = Arrays.copyOfRange(object.pointNameTypes, start, Math.min(end, object.pointNameTypes.length)); int[][] types = Arrays.copyOfRange(object.pointNameTypes, start, Math.min(end, object.pointNameTypes.length));
if (object.pointNames != null) { String[][] names = Arrays.copyOfRange(object.pointNames, start, Math.min(end, object.pointNames.length));
String[][] names = Arrays.copyOfRange(object.pointNames, start, Math.min(end, object.pointNames.length)); if (reversed) {
bundle.putArray("pointNames", convertPointNames(types, names, rules)); Algorithms.reverseArray(types);
Algorithms.reverseArray(names);
} }
bundle.putArray("pointNames", convertPointNames(types, names, rules));
} }
} }

View file

@ -886,6 +886,14 @@ public class Algorithms {
return map; return map;
} }
public static <T> void reverseArray(T[] array) {
for (int i = 0; i < array.length / 2; i++) {
T temp = array[i];
array[i] = array[array.length - i - 1];
array[array.length - i - 1] = temp;
}
}
public static boolean containsInArrayL(long[] array, long value) { public static boolean containsInArrayL(long[] array, long value) {
return Arrays.binarySearch(array, value) >= 0; return Arrays.binarySearch(array, value) >= 0;
} }

View file

@ -11,6 +11,8 @@
Thx - Hardy Thx - Hardy
--> -->
<string name="app_mode_gap">Gap</string>
<string name="ltr_or_rtl_combine_via_dash">%1$s — %2$s</string>
<string name="file_already_imported">File is already imported in OsmAnd</string> <string name="file_already_imported">File is already imported in OsmAnd</string>
<string name="osm_edit_logout_success">Logout successful</string> <string name="osm_edit_logout_success">Logout successful</string>
<string name="clear_osm_token">Clear OpenStreetMap OAuth token</string> <string name="clear_osm_token">Clear OpenStreetMap OAuth token</string>

View file

@ -21,7 +21,6 @@ import com.google.android.material.bottomnavigation.BottomNavigationView;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.GPXUtilities; import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.IndexConstants; import net.osmand.IndexConstants;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
@ -32,9 +31,7 @@ import net.osmand.plus.GpxSelectionHelper;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup; import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.LockableViewPager; import net.osmand.plus.LockableViewPager;
import net.osmand.plus.settings.backend.OsmAndAppCustomization;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.mapmarkers.CoordinateInputDialogFragment; import net.osmand.plus.mapmarkers.CoordinateInputDialogFragment;
import net.osmand.plus.measurementtool.GpxData; import net.osmand.plus.measurementtool.GpxData;
@ -44,6 +41,8 @@ import net.osmand.plus.myplaces.TrackBitmapDrawer;
import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener; import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener;
import net.osmand.plus.myplaces.TrackPointFragment; import net.osmand.plus.myplaces.TrackPointFragment;
import net.osmand.plus.myplaces.TrackSegmentFragment; import net.osmand.plus.myplaces.TrackSegmentFragment;
import net.osmand.plus.settings.backend.OsmAndAppCustomization;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint; import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint;
import java.io.File; import java.io.File;
@ -136,14 +135,9 @@ public class TrackActivity extends TabActivity {
} }
} }
public void addNewGpxData(GpxData.ActionType actionType) { public void addNewGpxData() {
addNewGpxData(actionType, null);
}
public void addNewGpxData(GpxData.ActionType actionType, TrkSegment segment) {
GPXFile gpxFile = getGpx(); GPXFile gpxFile = getGpx();
QuadRect rect = getRect(); GpxData gpxData = new GpxData(gpxFile);
GpxData gpxData = new GpxData(gpxFile, rect, actionType, segment);
WptPt pointToShow = gpxFile != null ? gpxFile.findPointToShow() : null; WptPt pointToShow = gpxFile != null ? gpxFile.findPointToShow() : null;
if (pointToShow != null) { if (pointToShow != null) {
LatLon location = new LatLon(pointToShow.getLatitude(), pointToShow.getLongitude()); LatLon location = new LatLon(pointToShow.getLatitude(), pointToShow.getLongitude());
@ -154,7 +148,6 @@ public class TrackActivity extends TabActivity {
false, false,
gpxData gpxData
); );
MapActivity.launchMapActivityMoveToTop(this); MapActivity.launchMapActivityMoveToTop(this);
} }
} }

View file

@ -17,6 +17,7 @@ import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.LocationsHolder; import net.osmand.LocationsHolder;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher; import net.osmand.ResultMatcher;
@ -27,11 +28,18 @@ import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.ContextMenuScrollFragment; import net.osmand.plus.base.ContextMenuScrollFragment;
import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.routing.GpxApproximator; import net.osmand.plus.routing.GpxApproximator;
import net.osmand.plus.routing.GpxApproximator.GpxApproximationProgressCallback;
import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation; import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static net.osmand.plus.measurementtool.ProfileCard.ProfileCardListener; import static net.osmand.plus.measurementtool.ProfileCard.ProfileCardListener;
import static net.osmand.plus.measurementtool.SliderCard.SliderCardListener; import static net.osmand.plus.measurementtool.SliderCard.SliderCardListener;
@ -49,8 +57,11 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
private ApplicationMode snapToRoadAppMode = ApplicationMode.CAR; private ApplicationMode snapToRoadAppMode = ApplicationMode.CAR;
private int distanceThreshold = 50; private int distanceThreshold = 50;
private boolean applyApproximation; private boolean applyApproximation;
private GpxApproximationProgressCallback approximationProgress;
private List<LocationsHolder> locationsHolders;
private final Map<LocationsHolder, GpxRouteApproximation> resultMap = new HashMap<>();
private LocationsHolder locationsHolder;
@Nullable @Nullable
private GpxApproximator gpxApproximator; private GpxApproximator gpxApproximator;
private ProgressBar progressBar; private ProgressBar progressBar;
@ -119,44 +130,26 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
distanceThreshold = savedInstanceState.getInt(DISTANCE_THRESHOLD_KEY); distanceThreshold = savedInstanceState.getInt(DISTANCE_THRESHOLD_KEY);
snapToRoadAppMode = ApplicationMode.valueOfStringKey( snapToRoadAppMode = ApplicationMode.valueOfStringKey(
savedInstanceState.getString(SNAP_TO_ROAD_APP_MODE_STRING_KEY), ApplicationMode.CAR); savedInstanceState.getString(SNAP_TO_ROAD_APP_MODE_STRING_KEY), ApplicationMode.CAR);
try {
gpxApproximator = new GpxApproximator(requireMyApplication(), snapToRoadAppMode, distanceThreshold, locationsHolder);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
} else {
try {
gpxApproximator = new GpxApproximator(requireMyApplication(), locationsHolder);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
} }
approximationProgress = new GpxApproximationProgressCallback() {
if (gpxApproximator != null) { @Override
gpxApproximator.setApproximationProgress(new GpxApproximator.GpxApproximationProgressCallback() { public void start(GpxApproximator approximator) {
}
@Override @Override
public void start() { public void updateProgress(GpxApproximator approximator, int progress) {
if (isResumed()) { if (isResumed() && approximator == GpxApproximationFragment.this.gpxApproximator) {
startProgress(); float partSize = 100f / locationsHolders.size();
} float p = resultMap.size() * partSize + (progress / 100f) * partSize;
GpxApproximationFragment.this.updateProgress((int) p);
} }
}
@Override @Override
public void updateProgress(int progress) { public void finish(GpxApproximator approximator) {
if (isResumed()) { }
GpxApproximationFragment.this.updateProgress(progress); };
}
}
@Override
public void finish() {
if (isResumed()) {
finishProgress();
}
}
});
}
applyButton = mainView.findViewById(R.id.right_bottom_button); applyButton = mainView.findViewById(R.id.right_bottom_button);
cancelButton = mainView.findViewById(R.id.dismiss_button); cancelButton = mainView.findViewById(R.id.dismiss_button);
@ -180,7 +173,7 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
} }
runLayoutListener(); runLayoutListener();
calculateGpxApproximation(); calculateGpxApproximation(true);
return mainView; return mainView;
} }
@ -219,6 +212,20 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
} }
} }
private GpxApproximator getNewGpxApproximator(@NonNull LocationsHolder locationsHolder) {
GpxApproximator gpxApproximator = null;
try {
OsmandApplication app = getMyApplication();
if (app != null) {
gpxApproximator = new GpxApproximator(app, snapToRoadAppMode, distanceThreshold, locationsHolder);
gpxApproximator.setApproximationProgress(approximationProgress);
}
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
return gpxApproximator;
}
private void updateCardsLayout() { private void updateCardsLayout() {
View mainView = getMainView(); View mainView = getMainView();
if (mainView != null) { if (mainView != null) {
@ -299,13 +306,17 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
} }
public static void showInstance(@NonNull FragmentManager fm, @Nullable Fragment targetFragment, public static void showInstance(@NonNull FragmentManager fm, @Nullable Fragment targetFragment,
@NonNull LocationsHolder locationsHolder, @Nullable ApplicationMode appMode) { @NonNull List<List<WptPt>> pointsList, @Nullable ApplicationMode appMode) {
try { try {
if (!fm.isStateSaved()) { if (!fm.isStateSaved()) {
GpxApproximationFragment fragment = new GpxApproximationFragment(); GpxApproximationFragment fragment = new GpxApproximationFragment();
fragment.setRetainInstance(true); fragment.setRetainInstance(true);
fragment.setTargetFragment(targetFragment, REQUEST_CODE); fragment.setTargetFragment(targetFragment, REQUEST_CODE);
fragment.setLocationsHolder(locationsHolder); List<LocationsHolder> locationsHolders = new ArrayList<>();
for (List<WptPt> points : pointsList) {
locationsHolders.add(new LocationsHolder(points));
}
fragment.setLocationsHolders(locationsHolders);
fragment.setSnapToRoadAppMode(appMode); fragment.setSnapToRoadAppMode(appMode);
fm.beginTransaction() fm.beginTransaction()
.replace(R.id.fragmentContainer, fragment, TAG) .replace(R.id.fragmentContainer, fragment, TAG)
@ -328,30 +339,48 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
} }
} }
public void calculateGpxApproximation() { public boolean calculateGpxApproximation(boolean newCalculation) {
if (newCalculation) {
if (gpxApproximator != null) {
gpxApproximator.cancelApproximation();
gpxApproximator = null;
}
resultMap.clear();
startProgress();
}
GpxApproximator gpxApproximator = null;
for (LocationsHolder locationsHolder : locationsHolders) {
if (!resultMap.containsKey(locationsHolder)) {
gpxApproximator = getNewGpxApproximator(locationsHolder);
break;
}
}
if (gpxApproximator != null) { if (gpxApproximator != null) {
try { try {
this.gpxApproximator = gpxApproximator;
gpxApproximator.setMode(snapToRoadAppMode); gpxApproximator.setMode(snapToRoadAppMode);
gpxApproximator.setPointApproximation(distanceThreshold); gpxApproximator.setPointApproximation(distanceThreshold);
approximateGpx(); approximateGpx(gpxApproximator);
return true;
} catch (Exception e) { } catch (Exception e) {
LOG.error(e.getMessage(), e); LOG.error(e.getMessage(), e);
} }
} }
return false;
} }
@Override @Override
public void onSliderChange(int sliderValue) { public void onSliderChange(int sliderValue) {
if (distanceThreshold != sliderValue) { if (distanceThreshold != sliderValue) {
distanceThreshold = sliderValue; distanceThreshold = sliderValue;
calculateGpxApproximation(); calculateGpxApproximation(true);
} }
} }
@Override @Override
public void onProfileSelect(ApplicationMode applicationMode) { public void onProfileSelect(ApplicationMode applicationMode) {
if (setSnapToRoadAppMode(applicationMode)) { if (setSnapToRoadAppMode(applicationMode)) {
calculateGpxApproximation(); calculateGpxApproximation(true);
} }
} }
@ -363,12 +392,12 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
return false; return false;
} }
public LocationsHolder getLocationsHolder() { public List<LocationsHolder> getLocationsHolders() {
return locationsHolder; return locationsHolders;
} }
public void setLocationsHolder(LocationsHolder locationsHolder) { public void setLocationsHolders(List<LocationsHolder> locationsHolders) {
this.locationsHolder = locationsHolder; this.locationsHolders = locationsHolders;
} }
public void startProgress() { public void startProgress() {
@ -393,35 +422,58 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
} }
} }
private void approximateGpx() { private void approximateGpx(@NonNull final GpxApproximator gpxApproximator) {
if (gpxApproximator != null) { onApproximationStarted();
setApplyButtonEnabled(false); gpxApproximator.calculateGpxApproximation(new ResultMatcher<GpxRouteApproximation>() {
gpxApproximator.calculateGpxApproximation(new ResultMatcher<GpxRouteApproximation>() { @Override
@Override public boolean publish(final GpxRouteApproximation gpxApproximation) {
public boolean publish(final GpxRouteApproximation gpxApproximation) { OsmandApplication app = getMyApplication();
OsmandApplication app = getMyApplication(); if (app != null) {
if (app != null) { app.runInUIThread(new Runnable() {
app.runInUIThread(new Runnable() { @Override
@Override public void run() {
public void run() { if (!gpxApproximator.isCancelled()) {
Fragment fragment = getTargetFragment(); if (gpxApproximation != null) {
if (fragment instanceof GpxApproximationFragmentListener) { resultMap.put(gpxApproximator.getLocationsHolder(), gpxApproximation);
((GpxApproximationFragmentListener) fragment).onGpxApproximationDone(gpxApproximation, gpxApproximator.getMode()); }
if (!calculateGpxApproximation(false)) {
onApproximationFinished();
} }
setApplyButtonEnabled(gpxApproximation != null);
} }
}); }
return true; });
}
return false;
} }
return true;
}
@Override @Override
public boolean isCancelled() { public boolean isCancelled() {
return false; return false;
} }
}); });
}
private void onApproximationStarted() {
setApplyButtonEnabled(false);
}
private void onApproximationFinished() {
finishProgress();
Fragment fragment = getTargetFragment();
List<GpxRouteApproximation> approximations = new ArrayList<>();
List<List<WptPt>> points = new ArrayList<>();
for (LocationsHolder locationsHolder : locationsHolders) {
GpxRouteApproximation approximation = resultMap.get(locationsHolder);
if (approximation != null) {
approximations.add(approximation);
points.add(locationsHolder.getWptPtList());
}
} }
if (fragment instanceof GpxApproximationFragmentListener) {
((GpxApproximationFragmentListener) fragment).onGpxApproximationDone(
approximations, points, snapToRoadAppMode);
}
setApplyButtonEnabled(!approximations.isEmpty());
} }
@Override @Override
@ -431,7 +483,7 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
public interface GpxApproximationFragmentListener { public interface GpxApproximationFragmentListener {
void onGpxApproximationDone(GpxRouteApproximation gpxApproximation, ApplicationMode mode); void onGpxApproximationDone(List<GpxRouteApproximation> gpxApproximations, List<List<WptPt>> pointsList, ApplicationMode mode);
void onApplyGpxApproximation(); void onApplyGpxApproximation();

View file

@ -1,35 +1,20 @@
package net.osmand.plus.measurementtool; package net.osmand.plus.measurementtool;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.data.QuadRect; import net.osmand.data.QuadRect;
public class GpxData { public class GpxData {
public enum ActionType { private final GPXFile gpxFile;
ADD_SEGMENT, private final QuadRect rect;
ADD_ROUTE_POINTS,
EDIT_SEGMENT,
OVERWRITE_SEGMENT
}
private GPXFile gpxFile; public GpxData(GPXFile gpxFile) {
private TrkSegment trkSegment;
private QuadRect rect;
private ActionType actionType;
public GpxData(GPXFile gpxFile, QuadRect rect, ActionType actionType, TrkSegment trkSegment) {
this.gpxFile = gpxFile; this.gpxFile = gpxFile;
this.rect = rect; if (gpxFile != null) {
this.actionType = actionType; this.rect = gpxFile.getRect();
this.trkSegment = trkSegment; } else {
} this.rect = new QuadRect(0, 0, 0, 0);
}
public GpxData(GPXFile gpxFile, GpxData gpxData) {
this.gpxFile = gpxFile;
this.rect = gpxData.rect;
this.actionType = gpxData.actionType;
this.trkSegment = gpxData.trkSegment;
} }
public GPXFile getGpxFile() { public GPXFile getGpxFile() {
@ -39,12 +24,4 @@ public class GpxData {
public QuadRect getRect() { public QuadRect getRect() {
return rect; return rect;
} }
public ActionType getActionType() {
return actionType;
}
public TrkSegment getTrkSegment() {
return trkSegment;
}
} }

View file

@ -53,9 +53,11 @@ public class MeasurementEditingContext {
private final MeasurementCommandManager commandManager = new MeasurementCommandManager(); private final MeasurementCommandManager commandManager = new MeasurementCommandManager();
private final TrkSegment before = new TrkSegment(); private final TrkSegment before = new TrkSegment();
private TrkSegment beforeCacheForSnap; private List<TrkSegment> beforeSegments = new ArrayList<>();
private List<TrkSegment> beforeSegmentsForSnap;
private final TrkSegment after = new TrkSegment(); private final TrkSegment after = new TrkSegment();
private TrkSegment afterCacheForSnap; private List<TrkSegment> afterSegments = new ArrayList<>();
private List<TrkSegment> afterSegmentsForSnap;
private GpxData gpxData; private GpxData gpxData;
@ -79,11 +81,11 @@ public class MeasurementEditingContext {
} }
public static class RoadSegmentData { public static class RoadSegmentData {
private ApplicationMode appMode; private final ApplicationMode appMode;
private WptPt start; private final WptPt start;
private WptPt end; private final WptPt end;
private List<WptPt> points; private final List<WptPt> points;
private List<RouteSegmentResult> segments; private final List<RouteSegmentResult> segments;
private double distance; private double distance;
public RoadSegmentData(@NonNull ApplicationMode appMode, @NonNull WptPt start, @NonNull WptPt end, public RoadSegmentData(@NonNull ApplicationMode appMode, @NonNull WptPt start, @NonNull WptPt end,
@ -156,8 +158,8 @@ public class MeasurementEditingContext {
return inAddPointMode; return inAddPointMode;
} }
public void updateCacheForSnap() { public void updateSegmentsForSnap() {
updateCacheForSnap(true); updateSegmentsForSnap(true);
} }
public int getSelectedPointPosition() { public int getSelectedPointPosition() {
@ -188,10 +190,10 @@ public class MeasurementEditingContext {
this.inApproximationMode = inApproximationMode; this.inApproximationMode = inApproximationMode;
} }
public List<WptPt> getOriginalTrackPointList() { public List<List<WptPt>> getOriginalSegmentPointsList() {
MeasurementModeCommand command = commandManager.getLastCommand(); MeasurementModeCommand command = commandManager.getLastCommand();
if (command.getType() == APPROXIMATE_POINTS) { if (command.getType() == APPROXIMATE_POINTS) {
return ((ApplyGpxApproximationCommand) command).getPoints(); return ((ApplyGpxApproximationCommand) command).getOriginalSegmentPointsList();
} }
return null; return null;
} }
@ -213,10 +215,6 @@ public class MeasurementEditingContext {
return gpxData != null && gpxData.getGpxFile() != null && gpxData.getGpxFile().hasRtePt(); return gpxData != null && gpxData.getGpxFile() != null && gpxData.getGpxFile().hasRtePt();
} }
public boolean hasSavedRoute() {
return gpxData != null && gpxData.getGpxFile() != null && gpxData.getGpxFile().hasRoute();
}
public CalculationMode getLastCalculationMode() { public CalculationMode getLastCalculationMode() {
return lastCalculationMode; return lastCalculationMode;
} }
@ -252,7 +250,7 @@ public class MeasurementEditingContext {
if (appMode != MeasurementEditingContext.DEFAULT_APP_MODE || !pair.first.lastPoint || !pair.second.firstPoint) { if (appMode != MeasurementEditingContext.DEFAULT_APP_MODE || !pair.first.lastPoint || !pair.second.firstPoint) {
double localDist = MapUtils.getDistance(pair.first.getLatitude(), pair.first.getLongitude(), double localDist = MapUtils.getDistance(pair.first.getLatitude(), pair.first.getLongitude(),
pair.second.getLatitude(), pair.second.getLongitude()); pair.second.getLatitude(), pair.second.getLongitude());
if(!Double.isNaN(pair.first.ele) && !Double.isNaN(pair.second.ele) && if (!Double.isNaN(pair.first.ele) && !Double.isNaN(pair.second.ele) &&
pair.first.ele != 0 && pair.second.ele != 0) { pair.first.ele != 0 && pair.second.ele != 0) {
double h = Math.abs(pair.first.ele - pair.second.ele); double h = Math.abs(pair.first.ele - pair.second.ele);
localDist = Math.sqrt(localDist * localDist + h * h); localDist = Math.sqrt(localDist * localDist + h * h);
@ -279,28 +277,70 @@ public class MeasurementEditingContext {
return !roadSegmentData.isEmpty(); return !roadSegmentData.isEmpty();
} }
public boolean isApproximationNeeded() {
boolean hasDefaultPoints = false;
boolean newData = isNewData();
if (!newData) {
List<WptPt> points = getPoints();
WptPt prevPoint = null;
for (WptPt point : points) {
if (!point.hasProfile() && (prevPoint == null || !prevPoint.hasProfile())) {
hasDefaultPoints = true;
break;
}
prevPoint = point;
}
}
return !newData && hasDefaultPoints && getPoints().size() > 2;
}
public void clearSnappedToRoadPoints() { public void clearSnappedToRoadPoints() {
roadSegmentData.clear(); roadSegmentData.clear();
} }
TrkSegment getBeforeTrkSegmentLine() { List<TrkSegment> getBeforeTrkSegmentLine() {
if (beforeCacheForSnap != null) { if (beforeSegmentsForSnap != null) {
return beforeCacheForSnap; return beforeSegmentsForSnap;
} }
return before; return beforeSegments;
} }
TrkSegment getAfterTrkSegmentLine() { List<TrkSegment> getAfterTrkSegmentLine() {
if (afterCacheForSnap != null) { if (afterSegmentsForSnap != null) {
return afterCacheForSnap; return afterSegmentsForSnap;
} }
return after; return afterSegments;
} }
public List<WptPt> getPoints() { public List<WptPt> getPoints() {
return getBeforePoints(); return getBeforePoints();
} }
public List<List<WptPt>> getPointsSegments(boolean plain, boolean route) {
List<List<WptPt>> res = new ArrayList<>();
List<WptPt> allPoints = getPoints();
List<WptPt> segment = new ArrayList<>();
String prevProfileType = null;
for (WptPt point : allPoints) {
String profileType = point.getProfileType();
boolean isGap = ApplicationMode.GAP.getStringKey().equals(profileType);
boolean plainPoint = Algorithms.isEmpty(profileType) || (isGap && Algorithms.isEmpty(prevProfileType));
boolean routePoint = !plainPoint;
if (plain && plainPoint || route && routePoint) {
segment.add(point);
if (isGap) {
res.add(segment);
segment = new ArrayList<>();
}
}
prevProfileType = profileType;
}
if (!segment.isEmpty()) {
res.add(segment);
}
return res;
}
List<WptPt> getBeforePoints() { List<WptPt> getBeforePoints() {
return before.points; return before.points;
} }
@ -321,22 +361,43 @@ public class MeasurementEditingContext {
after.points.clear(); after.points.clear();
before.points.addAll(points.subList(0, position)); before.points.addAll(points.subList(0, position));
after.points.addAll(points.subList(position, points.size())); after.points.addAll(points.subList(position, points.size()));
updateCacheForSnap(true); updateSegmentsForSnap(true);
} }
public void addPoint(WptPt pt) { public void addPoint(WptPt pt) {
before.points.add(pt); before.points.add(pt);
updateCacheForSnap(false); updateSegmentsForSnap(false);
} }
public void addPoint(int position, WptPt pt) { public void addPoint(int position, WptPt pt) {
before.points.add(position, pt); before.points.add(position, pt);
updateCacheForSnap(false); updateSegmentsForSnap(false);
} }
public void addPoints(List<WptPt> points) { public void addPoints(List<WptPt> points) {
before.points.addAll(points); before.points.addAll(points);
updateCacheForSnap(false); updateSegmentsForSnap(false);
}
public void replacePoints(List<WptPt> originalPoints, List<WptPt> points) {
if (originalPoints.size() > 1) {
int firstPointIndex = before.points.indexOf(originalPoints.get(0));
int lastPointIndex = before.points.indexOf(originalPoints.get(originalPoints.size() - 1));
List<WptPt> newPoints = new ArrayList<>();
if (firstPointIndex != -1 && lastPointIndex != -1) {
newPoints.addAll(before.points.subList(0, firstPointIndex));
newPoints.addAll(points);
if (before.points.size() > lastPointIndex + 1) {
newPoints.addAll(before.points.subList(lastPointIndex + 1, before.points.size()));
}
} else {
newPoints.addAll(points);
}
before.points = newPoints;
} else {
before.points = points;
}
updateSegmentsForSnap(false);
} }
public WptPt removePoint(int position, boolean updateSnapToRoad) { public WptPt removePoint(int position, boolean updateSnapToRoad) {
@ -345,7 +406,7 @@ public class MeasurementEditingContext {
} }
WptPt pt = before.points.remove(position); WptPt pt = before.points.remove(position);
if (updateSnapToRoad) { if (updateSnapToRoad) {
updateCacheForSnap(false); updateSegmentsForSnap(false);
} }
return pt; return pt;
} }
@ -368,15 +429,15 @@ public class MeasurementEditingContext {
public void clearBeforeSegments() { public void clearBeforeSegments() {
before.points.clear(); before.points.clear();
if (beforeCacheForSnap != null) { if (beforeSegmentsForSnap != null) {
beforeCacheForSnap.points.clear(); beforeSegmentsForSnap.clear();
} }
} }
public void clearAfterSegments() { public void clearAfterSegments() {
after.points.clear(); after.points.clear();
if (afterCacheForSnap != null) { if (afterSegmentsForSnap != null) {
afterCacheForSnap.points.clear(); afterSegmentsForSnap.clear();
} }
} }
@ -424,8 +485,10 @@ public class MeasurementEditingContext {
List<Pair<WptPt, WptPt>> res = new ArrayList<>(); List<Pair<WptPt, WptPt>> res = new ArrayList<>();
for (List<WptPt> points : Arrays.asList(before.points, after.points)) { for (List<WptPt> points : Arrays.asList(before.points, after.points)) {
for (int i = 0; i < points.size() - 1; i++) { for (int i = 0; i < points.size() - 1; i++) {
Pair<WptPt, WptPt> pair = new Pair<>(points.get(i), points.get(i + 1)); WptPt startPoint = points.get(i);
if (roadSegmentData.get(pair) == null) { WptPt endPoint = points.get(i + 1);
Pair<WptPt, WptPt> pair = new Pair<>(startPoint, endPoint);
if (roadSegmentData.get(pair) == null && startPoint.hasProfile()) {
res.add(pair); res.add(pair);
} }
} }
@ -433,98 +496,135 @@ public class MeasurementEditingContext {
return res; return res;
} }
private void recreateCacheForSnap(TrkSegment cache, TrkSegment original, boolean calculateIfNeeded) { private void recreateSegments(List<TrkSegment> segments, List<TrkSegment> segmentsForSnap, List<WptPt> points, boolean calculateIfNeeded) {
boolean hasDefaultModeOnly = true; List<Integer> roadSegmentIndexes = new ArrayList<>();
if (original.points.size() > 1) { TrkSegment s = new TrkSegment();
for (int i = 0; i < original.points.size(); i++) { segments.add(s);
String profileType = original.points.get(i).getProfileType(); boolean defaultMode = true;
if (profileType != null && !profileType.equals(DEFAULT_APP_MODE.getStringKey())) { if (points.size() > 1) {
hasDefaultModeOnly = false; for (int i = 0; i < points.size(); i++) {
break; WptPt point = points.get(i);
s.points.add(point);
String profileType = point.getProfileType();
if (profileType != null) {
boolean isDefault = profileType.equals(DEFAULT_APP_MODE.getStringKey());
boolean isGap = profileType.equals(ApplicationMode.GAP.getStringKey());
if (defaultMode && !isDefault && !isGap) {
roadSegmentIndexes.add(segments.size() - 1);
defaultMode = false;
}
if (isGap) {
if (!s.points.isEmpty()) {
s = new TrkSegment();
segments.add(s);
defaultMode = true;
}
}
} }
} }
} }
if (original.points.size() > 1) { if (s.points.isEmpty()) {
for (int i = 0; i < original.points.size() - 1; i++) { segments.remove(s);
Pair<WptPt, WptPt> pair = new Pair<>(original.points.get(i), original.points.get(i + 1)); }
RoadSegmentData data = this.roadSegmentData.get(pair); if (!segments.isEmpty()) {
List<WptPt> pts = data != null ? data.getPoints() : null; for (TrkSegment segment : segments) {
if (pts != null) { TrkSegment segmentForSnap = new TrkSegment();
cache.points.addAll(pts); for (int i = 0; i < segment.points.size() - 1; i++) {
} else { Pair<WptPt, WptPt> pair = new Pair<>(segment.points.get(i), segment.points.get(i + 1));
if (calculateIfNeeded && !hasDefaultModeOnly) { RoadSegmentData data = this.roadSegmentData.get(pair);
scheduleRouteCalculateIfNotEmpty(); List<WptPt> pts = data != null ? data.getPoints() : null;
if (pts != null) {
segmentForSnap.points.addAll(pts);
} else {
if (calculateIfNeeded && roadSegmentIndexes.contains(segmentsForSnap.size())) {
scheduleRouteCalculateIfNotEmpty();
}
segmentForSnap.points.addAll(Arrays.asList(pair.first, pair.second));
} }
cache.points.addAll(Arrays.asList(pair.first, pair.second));
} }
segmentsForSnap.add(segmentForSnap);
} }
} else { } else if (!points.isEmpty()) {
cache.points.addAll(original.points); TrkSegment segmentForSnap = new TrkSegment();
segmentForSnap.points.addAll(points);
segmentsForSnap.add(segmentForSnap);
} }
} }
void addPoints() { void addPoints() {
GpxData gpxData = getGpxData(); GpxData gpxData = getGpxData();
if (gpxData == null || gpxData.getTrkSegment() == null || Algorithms.isEmpty(gpxData.getTrkSegment().points)) { if (gpxData == null || gpxData.getGpxFile() == null) {
return; return;
} }
List<WptPt> points = gpxData.getTrkSegment().points; List<TrkSegment> segments = gpxData.getGpxFile().getNonEmptyTrkSegments(false);
if (isTrackSnappedToRoad()) { if (Algorithms.isEmpty(segments)) {
RouteImporter routeImporter = new RouteImporter(gpxData.getGpxFile()); return;
List<RouteSegmentResult> segments = routeImporter.importRoute(); }
List<WptPt> routePoints = gpxData.getGpxFile().getRoutePoints(); for (int si = 0; si < segments.size(); si++) {
int prevPointIndex = 0; TrkSegment segment = segments.get(si);
if (routePoints.isEmpty() && points.size() > 1) { List<WptPt> points = segment.points;
routePoints.add(points.get(0)); if (segment.hasRoute()) {
routePoints.add(points.get(points.size() - 1)); RouteImporter routeImporter = new RouteImporter(segment);
} List<RouteSegmentResult> routeSegments = routeImporter.importRoute();
for (int i = 0; i < routePoints.size() - 1; i++) { List<WptPt> routePoints = gpxData.getGpxFile().getRoutePoints(si);
Pair<WptPt, WptPt> pair = new Pair<>(routePoints.get(i), routePoints.get(i + 1)); int prevPointIndex = 0;
int startIndex = pair.first.getTrkPtIndex(); if (routePoints.isEmpty() && points.size() > 1) {
if (startIndex < 0 || startIndex < prevPointIndex || startIndex >= points.size()) { routePoints.add(points.get(0));
startIndex = findPointIndex(pair.first, points, prevPointIndex); routePoints.add(points.get(points.size() - 1));
} }
int endIndex = pair.second.getTrkPtIndex(); for (int i = 0; i < routePoints.size() - 1; i++) {
if (endIndex < 0 || endIndex < startIndex || endIndex >= points.size()) { Pair<WptPt, WptPt> pair = new Pair<>(routePoints.get(i), routePoints.get(i + 1));
endIndex = findPointIndex(pair.second, points, startIndex); int startIndex = pair.first.getTrkPtIndex();
} if (startIndex < 0 || startIndex < prevPointIndex || startIndex >= points.size()) {
if (startIndex >= 0 && endIndex >= 0) { startIndex = findPointIndex(pair.first, points, prevPointIndex);
List<WptPt> pairPoints = new ArrayList<>();
for (int j = startIndex; j < endIndex && j < points.size(); j++) {
pairPoints.add(points.get(j));
prevPointIndex = j;
} }
if (points.size() > prevPointIndex + 1) { int endIndex = pair.second.getTrkPtIndex();
pairPoints.add(points.get(prevPointIndex + 1)); if (endIndex < 0 || endIndex < startIndex || endIndex >= points.size()) {
endIndex = findPointIndex(pair.second, points, startIndex);
} }
Iterator<RouteSegmentResult> it = segments.iterator(); if (startIndex >= 0 && endIndex >= 0) {
int k = endIndex - startIndex - 1; List<WptPt> pairPoints = new ArrayList<>();
List<RouteSegmentResult> pairSegments = new ArrayList<>(); for (int j = startIndex; j < endIndex && j < points.size(); j++) {
if (k == 0 && !segments.isEmpty()) { pairPoints.add(points.get(j));
pairSegments.add(segments.remove(0)); prevPointIndex = j;
} else {
while (it.hasNext() && k > 0) {
RouteSegmentResult s = it.next();
pairSegments.add(s);
it.remove();
k -= Math.abs(s.getEndPointIndex() - s.getStartPointIndex());
} }
if (points.size() > prevPointIndex + 1) {
pairPoints.add(points.get(prevPointIndex + 1));
}
Iterator<RouteSegmentResult> it = routeSegments.iterator();
int k = endIndex - startIndex - 1;
List<RouteSegmentResult> pairSegments = new ArrayList<>();
if (k == 0 && !routeSegments.isEmpty()) {
pairSegments.add(routeSegments.remove(0));
} else {
while (it.hasNext() && k > 0) {
RouteSegmentResult s = it.next();
pairSegments.add(s);
it.remove();
k -= Math.abs(s.getEndPointIndex() - s.getStartPointIndex());
}
}
ApplicationMode appMode = ApplicationMode.valueOfStringKey(pair.first.getProfileType(), DEFAULT_APP_MODE);
roadSegmentData.put(pair, new RoadSegmentData(appMode, pair.first, pair.second, pairPoints, pairSegments));
} }
ApplicationMode appMode = ApplicationMode.valueOfStringKey(pair.first.getProfileType(), DEFAULT_APP_MODE); }
roadSegmentData.put(pair, new RoadSegmentData(appMode, pair.first, pair.second, pairPoints, pairSegments)); if (!routePoints.isEmpty() && si < segments.size() - 1) {
routePoints.get(routePoints.size() - 1).setProfileType(ApplicationMode.GAP.getStringKey());
}
addPoints(routePoints);
} else {
addPoints(points);
if (!points.isEmpty() && si < segments.size() - 1) {
points.get(points.size() - 1).setProfileType(ApplicationMode.GAP.getStringKey());
} }
} }
addPoints(routePoints);
} else {
addPoints(points);
} }
} }
public void setPoints(GpxRouteApproximation gpxApproximation, ApplicationMode mode) { public List<WptPt> setPoints(GpxRouteApproximation gpxApproximation, List<WptPt> originalPoints, ApplicationMode mode) {
if (gpxApproximation == null || Algorithms.isEmpty(gpxApproximation.finalPoints) || Algorithms.isEmpty(gpxApproximation.result)) { if (gpxApproximation == null || Algorithms.isEmpty(gpxApproximation.finalPoints) || Algorithms.isEmpty(gpxApproximation.result)) {
return; return null;
} }
roadSegmentData.clear();
List<WptPt> routePoints = new ArrayList<>(); List<WptPt> routePoints = new ArrayList<>();
List<GpxPoint> gpxPoints = gpxApproximation.finalPoints; List<GpxPoint> gpxPoints = gpxApproximation.finalPoints;
for (int i = 0; i < gpxPoints.size(); i++) { for (int i = 0; i < gpxPoints.size(); i++) {
@ -566,7 +666,13 @@ public class MeasurementEditingContext {
break; break;
} }
} }
addPoints(routePoints); WptPt lastOriginalPoint = originalPoints.get(originalPoints.size() - 1);
WptPt lastRoutePoint = routePoints.get(routePoints.size() - 1);
if (ApplicationMode.GAP.getStringKey().equals(lastOriginalPoint.getProfileType())) {
lastRoutePoint.setProfileType(ApplicationMode.GAP.getStringKey());
}
replacePoints(originalPoints, routePoints);
return routePoints;
} }
private boolean isLastGpxPoint(List<GpxPoint> gpxPoints, int index) { private boolean isLastGpxPoint(List<GpxPoint> gpxPoints, int index) {
@ -624,24 +730,21 @@ public class MeasurementEditingContext {
return index; return index;
} }
boolean isTrackSnappedToRoad() { private void updateSegmentsForSnap(boolean both) {
GpxData gpxData = getGpxData(); recreateSegments(beforeSegments = new ArrayList<>(),
return gpxData != null && gpxData.getTrkSegment() != null beforeSegmentsForSnap = new ArrayList<>(), before.points, true);
&& !gpxData.getTrkSegment().points.isEmpty()
&& gpxData.getGpxFile().hasRoute();
}
private void updateCacheForSnap(boolean both) {
recreateCacheForSnap(beforeCacheForSnap = new TrkSegment(), before, true);
if (both) { if (both) {
recreateCacheForSnap(afterCacheForSnap = new TrkSegment(), after, true); recreateSegments(afterSegments = new ArrayList<>(),
afterSegmentsForSnap = new ArrayList<>(), after.points, true);
} }
} }
private void updateCacheForSnap(boolean both, boolean calculateIfNeeded) { private void updateSegmentsForSnap(boolean both, boolean calculateIfNeeded) {
recreateCacheForSnap(beforeCacheForSnap = new TrkSegment(), before, calculateIfNeeded); recreateSegments(beforeSegments = new ArrayList<>(),
beforeSegmentsForSnap = new ArrayList<>(), before.points, calculateIfNeeded);
if (both) { if (both) {
recreateCacheForSnap(afterCacheForSnap = new TrkSegment(), after, calculateIfNeeded); recreateSegments(afterSegments = new ArrayList<>(),
afterSegmentsForSnap = new ArrayList<>(), after.points, calculateIfNeeded);
} }
} }
@ -694,7 +797,7 @@ public class MeasurementEditingContext {
int pairs = pointsToCalculateSize; int pairs = pointsToCalculateSize;
if (pairs != 0) { if (pairs != 0) {
float pairProgress = 100f / pairs; float pairProgress = 100f / pairs;
progress = (int)(calculatedPairs * pairProgress + (float) progress / pairs); progress = (int) (calculatedPairs * pairProgress + (float) progress / pairs);
} }
progressListener.updateProgress(progress); progressListener.updateProgress(progress);
} }
@ -738,7 +841,7 @@ public class MeasurementEditingContext {
application.runInUIThread(new Runnable() { application.runInUIThread(new Runnable() {
@Override @Override
public void run() { public void run() {
updateCacheForSnap(true, false); updateSegmentsForSnap(true, false);
progressListener.refresh(); progressListener.refresh();
RouteCalculationParams params = getParams(false); RouteCalculationParams params = getParams(false);
if (params != null) { if (params != null) {
@ -753,34 +856,41 @@ public class MeasurementEditingContext {
return params; return params;
} }
public List<WptPt> getRoutePoints() { public List<List<WptPt>> getRoutePoints() {
List<WptPt> res = new ArrayList<>(); List<List<WptPt>> res = new ArrayList<>();
List<WptPt> points = new ArrayList<>(before.points); List<WptPt> plainPoints = new ArrayList<>(before.points);
points.addAll(after.points); plainPoints.addAll(after.points);
int size = points.size(); List<WptPt> points = new ArrayList<>();
for (int i = 0; i < size - 1; i++) { for (WptPt point : plainPoints) {
Pair<WptPt, WptPt> pair = new Pair<>(points.get(i), points.get(i + 1)); if (point.getTrkPtIndex() != -1) {
RoadSegmentData data = this.roadSegmentData.get(pair); points.add(point);
if (data != null) { if (ApplicationMode.GAP.getStringKey().equals(point.getProfileType())) {
res.addAll(data.points); res.add(points);
points = new ArrayList<>();
}
} }
} }
if (!points.isEmpty()) {
res.add(points);
}
return res; return res;
} }
@Nullable @Nullable
public GPXFile exportRouteAsGpx(@NonNull String gpxName) { public GPXFile exportGpx(@NonNull String gpxName) {
if (application == null || before.points.isEmpty() || !hasRoute()) { if (application == null || before.points.isEmpty()) {
return null; return null;
} }
return RouteExporter.exportRoute(gpxName, getRouteSegments(), null);
}
private TrkSegment getRouteSegment(int startPointIndex, int endPointIndex) {
List<RouteSegmentResult> route = new ArrayList<>(); List<RouteSegmentResult> route = new ArrayList<>();
List<Location> locations = new ArrayList<>(); List<Location> locations = new ArrayList<>();
before.points.get(0).setTrkPtIndex(0); for (int i = startPointIndex; i < endPointIndex; i++) {
int size = before.points.size();
for (int i = 0; i < size - 1; i++) {
Pair<WptPt, WptPt> pair = new Pair<>(before.points.get(i), before.points.get(i + 1)); Pair<WptPt, WptPt> pair = new Pair<>(before.points.get(i), before.points.get(i + 1));
RoadSegmentData data = this.roadSegmentData.get(pair); RoadSegmentData data = this.roadSegmentData.get(pair);
if (data != null) { if (data != null && data.points != null && data.segments != null) {
for (WptPt pt : data.points) { for (WptPt pt : data.points) {
Location l = new Location(""); Location l = new Location("");
l.setLatitude(pt.getLatitude()); l.setLatitude(pt.getLatitude());
@ -790,11 +900,42 @@ public class MeasurementEditingContext {
} }
locations.add(l); locations.add(l);
} }
pair.second.setTrkPtIndex(i < size - 1 ? locations.size() : locations.size() - 1); pair.second.setTrkPtIndex(i + 1 < before.points.size() - 1 ? locations.size() : locations.size() - 1);
route.addAll(data.segments); route.addAll(data.segments);
} }
} }
return new RouteExporter(gpxName, route, locations, null).exportRoute(); if (!locations.isEmpty() && !route.isEmpty()) {
before.points.get(startPointIndex).setTrkPtIndex(0);
return new RouteExporter("", route, locations, null).generateRouteSegment();
} else if (endPointIndex - startPointIndex >= 0) {
TrkSegment segment = new TrkSegment();
segment.points = before.points.subList(startPointIndex, endPointIndex + 1);
return segment;
}
return null;
}
private List<TrkSegment> getRouteSegments() {
List<TrkSegment> res = new ArrayList<>();
List<Integer> lastPointIndexes = new ArrayList<>();
for (int i = 0; i < before.points.size(); i++) {
WptPt pt = before.points.get(i);
if (ApplicationMode.GAP.getStringKey().equals(pt.getProfileType())) {
lastPointIndexes.add(i);
}
}
if (lastPointIndexes.isEmpty() || lastPointIndexes.get(lastPointIndexes.size() - 1) < before.points.size() - 1) {
lastPointIndexes.add(before.points.size() - 1);
}
int firstPointIndex = 0;
for (Integer lastPointIndex : lastPointIndexes) {
TrkSegment segment = getRouteSegment(firstPointIndex, lastPointIndex);
if (segment != null) {
res.add(segment);
}
firstPointIndex = lastPointIndex + 1;
}
return res;
} }
interface SnapToRoadProgressListener { interface SnapToRoadProgressListener {

View file

@ -36,9 +36,7 @@ import net.osmand.AndroidUtils;
import net.osmand.FileUtils; import net.osmand.FileUtils;
import net.osmand.GPXUtilities; import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.LocationsHolder;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.data.QuadRect; import net.osmand.data.QuadRect;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
@ -55,7 +53,6 @@ import net.osmand.plus.base.BaseOsmAndFragment;
import net.osmand.plus.base.ContextMenuFragment.MenuState; import net.osmand.plus.base.ContextMenuFragment.MenuState;
import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.measurementtool.GpxApproximationFragment.GpxApproximationFragmentListener; import net.osmand.plus.measurementtool.GpxApproximationFragment.GpxApproximationFragmentListener;
import net.osmand.plus.measurementtool.GpxData.ActionType;
import net.osmand.plus.measurementtool.OptionsBottomSheetDialogFragment.OptionsFragmentListener; import net.osmand.plus.measurementtool.OptionsBottomSheetDialogFragment.OptionsFragmentListener;
import net.osmand.plus.measurementtool.RouteBetweenPointsBottomSheetDialogFragment.RouteBetweenPointsDialogMode; import net.osmand.plus.measurementtool.RouteBetweenPointsBottomSheetDialogFragment.RouteBetweenPointsDialogMode;
import net.osmand.plus.measurementtool.RouteBetweenPointsBottomSheetDialogFragment.RouteBetweenPointsDialogType; import net.osmand.plus.measurementtool.RouteBetweenPointsBottomSheetDialogFragment.RouteBetweenPointsDialogType;
@ -140,7 +137,6 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
private static final int UNDO_MODE = 0x8; private static final int UNDO_MODE = 0x8;
private int modes = 0x0; private int modes = 0x0;
private boolean approximationApplied = false;
private boolean portrait; private boolean portrait;
private boolean nightMode; private boolean nightMode;
private int cachedMapPosition; private int cachedMapPosition;
@ -150,7 +146,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
private LatLon initialPoint; private LatLon initialPoint;
enum SaveType { enum SaveType {
ROUTE_POINT, ROUTE,
LINE LINE
} }
@ -343,7 +339,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
mainView.findViewById(R.id.options_button).setOnClickListener(new OnClickListener() { mainView.findViewById(R.id.options_button).setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
boolean trackSnappedToRoad = editingCtx.isTrackSnappedToRoad() || editingCtx.isNewData() || approximationApplied; boolean trackSnappedToRoad = !editingCtx.isApproximationNeeded();
OptionsBottomSheetDialogFragment.showInstance(mapActivity.getSupportFragmentManager(), OptionsBottomSheetDialogFragment.showInstance(mapActivity.getSupportFragmentManager(),
MeasurementToolFragment.this, MeasurementToolFragment.this,
trackSnappedToRoad, trackSnappedToRoad,
@ -475,8 +471,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
updateToolbar(); updateToolbar();
final GpxData gpxData = editingCtx.getGpxData(); final GpxData gpxData = editingCtx.getGpxData();
adapter = new MeasurementToolAdapter(getMapActivity(), editingCtx.getPoints(), adapter = new MeasurementToolAdapter(getMapActivity(), editingCtx.getPoints());
gpxData != null ? gpxData.getActionType() : null);
if (portrait) { if (portrait) {
pointsRv = mainView.findViewById(R.id.measure_points_recycler_view); pointsRv = mainView.findViewById(R.id.measure_points_recycler_view);
} else { } else {
@ -503,7 +498,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
if (savedInstanceState == null) { if (savedInstanceState == null) {
if (fileName != null) { if (fileName != null) {
addNewGpxData(getGpxFile(fileName)); addNewGpxData(getGpxFile(fileName));
} else if (!editingCtx.isNewData() && !editingCtx.hasRoutePoints() && !editingCtx.hasRoute() && editingCtx.getPointsCount() > 1) { } else if (editingCtx.isApproximationNeeded()) {
enterApproximationMode(mapActivity); enterApproximationMode(mapActivity);
} }
} else { } else {
@ -536,7 +531,6 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
if (mapActivity != null) { if (mapActivity != null) {
editingCtx.getCommandManager().setMeasurementLayer(mapActivity.getMapLayers().getMeasurementToolLayer()); editingCtx.getCommandManager().setMeasurementLayer(mapActivity.getMapLayers().getMeasurementToolLayer());
enterMeasurementMode(); enterMeasurementMode();
updateSnapToRoadControls();
if (gpxData != null && addPoints) { if (gpxData != null && addPoints) {
if (!isUndoMode()) { if (!isUndoMode()) {
List<WptPt> points = gpxData.getGpxFile().getRoutePoints(); List<WptPt> points = gpxData.getGpxFile().getRoutePoints();
@ -547,13 +541,9 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
} }
} }
} }
ActionType actionType = gpxData.getActionType(); collectPoints();
if (actionType == ActionType.ADD_ROUTE_POINTS) {
displayRoutePoints();
} else if (actionType == ActionType.EDIT_SEGMENT) {
displaySegmentPoints();
}
} }
updateSnapToRoadControls();
setMode(UNDO_MODE, false); setMode(UNDO_MODE, false);
} }
} }
@ -638,16 +628,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
private void updateMainIcon() { private void updateMainIcon() {
GpxData gpxData = editingCtx.getGpxData(); GpxData gpxData = editingCtx.getGpxData();
if (gpxData != null) { mainIcon.setImageDrawable(getActiveIcon(gpxData != null ? R.drawable.ic_action_polygom_dark : R.drawable.ic_action_ruler));
ActionType actionType = gpxData.getActionType();
if (actionType == ActionType.ADD_SEGMENT || actionType == ActionType.EDIT_SEGMENT) {
mainIcon.setImageDrawable(getActiveIcon(R.drawable.ic_action_polygom_dark));
} else {
mainIcon.setImageDrawable(getActiveIcon(R.drawable.ic_action_markers_dark));
}
} else {
mainIcon.setImageDrawable(getActiveIcon(R.drawable.ic_action_ruler));
}
} }
private void startSnapToRoad(boolean rememberPreviousTitle) { private void startSnapToRoad(boolean rememberPreviousTitle) {
@ -659,15 +640,15 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
toolBarController.setTitle(getString(R.string.route_between_points)); toolBarController.setTitle(getString(R.string.route_between_points));
mapActivity.refreshMap(); mapActivity.refreshMap();
if (editingCtx.isNewData() || editingCtx.hasRoutePoints() || editingCtx.hasRoute() || editingCtx.getPointsCount() <= 2) { if (editingCtx.isApproximationNeeded()) {
enterApproximationMode(mapActivity);
} else {
RouteBetweenPointsBottomSheetDialogFragment.showInstance(mapActivity.getSupportFragmentManager(), RouteBetweenPointsBottomSheetDialogFragment.showInstance(mapActivity.getSupportFragmentManager(),
this, RouteBetweenPointsDialogType.WHOLE_ROUTE_CALCULATION, this, RouteBetweenPointsDialogType.WHOLE_ROUTE_CALCULATION,
editingCtx.getLastCalculationMode() == CalculationMode.NEXT_SEGMENT editingCtx.getLastCalculationMode() == CalculationMode.NEXT_SEGMENT
? RouteBetweenPointsDialogMode.SINGLE ? RouteBetweenPointsDialogMode.SINGLE
: RouteBetweenPointsDialogMode.ALL, : RouteBetweenPointsDialogMode.ALL,
editingCtx.getAppMode()); editingCtx.getAppMode());
} else {
enterApproximationMode(mapActivity);
} }
} }
} }
@ -676,12 +657,11 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
MapActivity mapActivity = getMapActivity(); MapActivity mapActivity = getMapActivity();
if (mapActivity != null) { if (mapActivity != null) {
if (editingCtx.getPointsCount() > 0) { if (editingCtx.getPointsCount() > 0) {
GpxData gpxData = editingCtx.getGpxData(); if (editingCtx.isNewData() || isInEditMode()) {
if (editingCtx.isNewData() || (isInEditMode() && gpxData.getActionType() == ActionType.EDIT_SEGMENT)) {
if (showDialog) { if (showDialog) {
openSaveAsNewTrackMenu(mapActivity); openSaveAsNewTrackMenu(mapActivity);
} else { } else {
saveNewGpx(null, getSuggestedFileName(), true, false, finalSaveAction); saveNewGpx("", getSuggestedFileName(), true, false, finalSaveAction);
} }
} else { } else {
addToGpx(mapActivity, finalSaveAction); addToGpx(mapActivity, finalSaveAction);
@ -695,6 +675,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
@Override @Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
MapActivity mapActivity = getMapActivity();
switch (requestCode) { switch (requestCode) {
case SnapTrackWarningFragment.REQUEST_CODE: case SnapTrackWarningFragment.REQUEST_CODE:
switch (resultCode) { switch (resultCode) {
@ -705,14 +686,16 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
updateToolbar(); updateToolbar();
break; break;
case SnapTrackWarningFragment.CONTINUE_RESULT_CODE: case SnapTrackWarningFragment.CONTINUE_RESULT_CODE:
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) { if (mapActivity != null) {
ApplicationMode mode = editingCtx.getAppMode(); ApplicationMode mode = editingCtx.getAppMode();
if (mode == ApplicationMode.DEFAULT || "public_transport".equals(mode.getRoutingProfile())) { if (mode == ApplicationMode.DEFAULT || "public_transport".equals(mode.getRoutingProfile())) {
mode = null; mode = null;
} }
GpxApproximationFragment.showInstance(mapActivity.getSupportFragmentManager(), List<List<WptPt>> pointsSegments = editingCtx.getPointsSegments(true, false);
this, new LocationsHolder(editingCtx.getPoints()), mode); if (!pointsSegments.isEmpty()) {
GpxApproximationFragment.showInstance(
mapActivity.getSupportFragmentManager(), this, pointsSegments, mode);
}
} }
break; break;
} }
@ -720,10 +703,14 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
case ExitBottomSheetDialogFragment.REQUEST_CODE: case ExitBottomSheetDialogFragment.REQUEST_CODE:
switch (resultCode) { switch (resultCode) {
case ExitBottomSheetDialogFragment.EXIT_RESULT_CODE: case ExitBottomSheetDialogFragment.EXIT_RESULT_CODE:
dismiss(getMapActivity()); if (mapActivity != null) {
dismiss(getMapActivity());
}
break; break;
case ExitBottomSheetDialogFragment.SAVE_RESULT_CODE: case ExitBottomSheetDialogFragment.SAVE_RESULT_CODE:
openSaveAsNewTrackMenu(getMapActivity()); if (mapActivity != null) {
openSaveAsNewTrackMenu(getMapActivity());
}
break; break;
} }
} }
@ -755,7 +742,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
} else { } else {
String trackName = getSuggestedFileName(); String trackName = getSuggestedFileName();
if (editingCtx.hasRoute()) { if (editingCtx.hasRoute()) {
GPXFile gpx = editingCtx.exportRouteAsGpx(trackName); GPXFile gpx = editingCtx.exportGpx(trackName);
if (gpx != null) { if (gpx != null) {
dismiss(mapActivity); dismiss(mapActivity);
runNavigation(gpx, appMode); runNavigation(gpx, appMode);
@ -763,15 +750,15 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
Toast.makeText(mapActivity, getString(R.string.error_occurred_saving_gpx), Toast.LENGTH_SHORT).show(); Toast.makeText(mapActivity, getString(R.string.error_occurred_saving_gpx), Toast.LENGTH_SHORT).show();
} }
} else { } else {
if (editingCtx.isNewData() || editingCtx.hasRoutePoints()) { if (editingCtx.isApproximationNeeded()) {
setMode(DIRECTION_MODE, true);
enterApproximationMode(mapActivity);
} else {
GPXFile gpx = new GPXFile(Version.getFullVersion(requireMyApplication())); GPXFile gpx = new GPXFile(Version.getFullVersion(requireMyApplication()));
gpx.addRoutePoints(points); gpx.addRoutePoints(points, true);
dismiss(mapActivity); dismiss(mapActivity);
targetPointsHelper.clearAllPoints(false); targetPointsHelper.clearAllPoints(false);
mapActions.enterRoutePlanningModeGivenGpx(gpx, appMode, null, null, true, true, MenuState.HEADER_ONLY); mapActions.enterRoutePlanningModeGivenGpx(gpx, appMode, null, null, true, true, MenuState.HEADER_ONLY);
} else {
setMode(DIRECTION_MODE, true);
enterApproximationMode(mapActivity);
} }
} }
} }
@ -1044,8 +1031,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
SelectedGpxFile selectedGpxFile = mapActivity.getMyApplication().getSelectedGpxHelper() SelectedGpxFile selectedGpxFile = mapActivity.getMyApplication().getSelectedGpxHelper()
.getSelectedFileByPath(gpxFile.path); .getSelectedFileByPath(gpxFile.path);
boolean showOnMap = selectedGpxFile != null; boolean showOnMap = selectedGpxFile != null;
saveExistingGpx(gpxFile, showOnMap, ActionType.ADD_SEGMENT, saveExistingGpx(gpxFile, showOnMap, false, FinalSaveAction.SHOW_TOAST);
editingCtx.hasRoute() ? SaveType.ROUTE_POINT : SaveType.LINE, FinalSaveAction.SHOW_TOAST);
} }
} }
@ -1070,10 +1056,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
private GpxData setupGpxData(@Nullable GPXFile gpxFile) { private GpxData setupGpxData(@Nullable GPXFile gpxFile) {
GpxData gpxData = null; GpxData gpxData = null;
if (gpxFile != null) { if (gpxFile != null) {
QuadRect rect = gpxFile.getRect(); gpxData = new GpxData(gpxFile);
TrkSegment segment = gpxFile.getNonEmptyTrkSegment();
ActionType actionType = segment == null ? ActionType.ADD_ROUTE_POINTS : ActionType.EDIT_SEGMENT;
gpxData = new GpxData(gpxFile, rect, actionType, segment);
} }
editingCtx.setGpxData(gpxData); editingCtx.setGpxData(gpxData);
return gpxData; return gpxData;
@ -1091,22 +1074,8 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
} }
@Override @Override
public void onSaveAsNewTrack(String folderName, String fileName, boolean showOnMap, boolean simplifiedTrack) { public void onSaveAsNewTrack(String folderName, String fileName, boolean showOnMap, boolean simplified) {
saveNewGpx(folderName, fileName, showOnMap, simplifiedTrack, FinalSaveAction.SHOW_IS_SAVED_FRAGMENT); saveNewGpx(folderName, fileName, showOnMap, simplified, FinalSaveAction.SHOW_IS_SAVED_FRAGMENT);
}
private void saveNewGpx(String folderName, String fileName, boolean showOnMap, boolean simplifiedTrack,
FinalSaveAction finalSaveAction) {
OsmandApplication app = getMyApplication();
if (app != null) {
File dir = getMyApplication().getAppPath(GPX_INDEX_DIR);
if (folderName != null && !dir.getName().equals(folderName)) {
dir = new File(dir, folderName);
}
fileName += GPX_FILE_EXT;
SaveType saveType = simplifiedTrack ? SaveType.LINE : SaveType.ROUTE_POINT;
saveNewGpx(dir, fileName, showOnMap, saveType, finalSaveAction);
}
} }
private MeasurementAdapterListener createMeasurementAdapterListener(final ItemTouchHelper touchHelper) { private MeasurementAdapterListener createMeasurementAdapterListener(final ItemTouchHelper touchHelper) {
@ -1182,7 +1151,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
final ApplicationMode appMode = editingCtx.getAppMode(); final ApplicationMode appMode = editingCtx.getAppMode();
if (mapActivity != null) { if (mapActivity != null) {
Drawable icon; Drawable icon;
if (editingCtx.isTrackSnappedToRoad() || editingCtx.isNewData() || approximationApplied) { if (!editingCtx.isApproximationNeeded() || editingCtx.isNewData()) {
if (appMode == MeasurementEditingContext.DEFAULT_APP_MODE) { if (appMode == MeasurementEditingContext.DEFAULT_APP_MODE) {
icon = getActiveIcon(R.drawable.ic_action_split_interval); icon = getActiveIcon(R.drawable.ic_action_split_interval);
} else { } else {
@ -1204,23 +1173,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
} }
} }
private void displayRoutePoints() { private void collectPoints() {
MeasurementToolLayer measurementLayer = getMeasurementLayer();
GpxData gpxData = editingCtx.getGpxData();
GPXFile gpx = gpxData != null ? gpxData.getGpxFile() : null;
if (gpx != null) {
List<WptPt> points = gpx.getRoutePoints();
if (measurementLayer != null) {
if (!isUndoMode()) {
editingCtx.addPoints(points);
}
adapter.notifyDataSetChanged();
updateDistancePointsText();
}
}
}
private void displaySegmentPoints() {
MeasurementToolLayer measurementLayer = getMeasurementLayer(); MeasurementToolLayer measurementLayer = getMeasurementLayer();
if (measurementLayer != null) { if (measurementLayer != null) {
if (!isUndoMode()) { if (!isUndoMode()) {
@ -1515,8 +1468,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
SelectedGpxFile selectedGpxFile = SelectedGpxFile selectedGpxFile =
mapActivity.getMyApplication().getSelectedGpxHelper().getSelectedFileByPath(gpx.path); mapActivity.getMyApplication().getSelectedGpxHelper().getSelectedFileByPath(gpx.path);
boolean showOnMap = selectedGpxFile != null; boolean showOnMap = selectedGpxFile != null;
saveExistingGpx(gpx, showOnMap, gpxData.getActionType(), saveExistingGpx(gpx, showOnMap, false, finalSaveAction);
editingCtx.hasRoute() ? SaveType.ROUTE_POINT : SaveType.LINE, finalSaveAction);
} }
} }
@ -1540,30 +1492,44 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
return displayedName; return displayedName;
} }
private void saveNewGpx(@NonNull File dir, @NonNull String fileName, boolean showOnMap, SaveType saveType, FinalSaveAction finalSaveAction) { private void saveNewGpx(String folderName, String fileName, boolean showOnMap,
saveGpx(new File(dir, fileName), null, null, saveType, finalSaveAction, showOnMap); boolean simplified, FinalSaveAction finalSaveAction) {
OsmandApplication app = getMyApplication();
if (app != null) {
File dir = getMyApplication().getAppPath(GPX_INDEX_DIR);
if (!Algorithms.isEmpty(folderName) && !dir.getName().equals(folderName)) {
dir = new File(dir, folderName);
}
fileName += GPX_FILE_EXT;
saveNewGpx(dir, fileName, showOnMap, simplified, finalSaveAction);
}
} }
private void saveExistingGpx(@NonNull GPXFile gpx, boolean showOnMap, ActionType actionType, private void saveNewGpx(@NonNull File dir, @NonNull String fileName, boolean showOnMap,
SaveType saveType, FinalSaveAction finalSaveAction) { boolean simplified, FinalSaveAction finalSaveAction) {
saveGpx(new File(gpx.path), gpx, actionType, saveType, finalSaveAction, showOnMap); saveGpx(new File(dir, fileName), null, simplified, finalSaveAction, showOnMap);
} }
private void saveGpx(@NonNull final File outFile, @Nullable GPXFile gpxFile, final ActionType actionType, private void saveExistingGpx(@NonNull GPXFile gpx, boolean showOnMap,
SaveType saveType, final FinalSaveAction finalSaveAction, final boolean showOnMap) { boolean simplified, FinalSaveAction finalSaveAction) {
saveGpx(new File(gpx.path), gpx, simplified, finalSaveAction, showOnMap);
}
private void saveGpx(@NonNull final File outFile, @Nullable GPXFile gpxFile, boolean simplified,
final FinalSaveAction finalSaveAction, final boolean showOnMap) {
SaveGpxRouteListener saveGpxRouteListener = new SaveGpxRouteListener() { SaveGpxRouteListener saveGpxRouteListener = new SaveGpxRouteListener() {
@Override @Override
public void gpxSavingFinished(Exception warning, GPXFile savedGpxFile, File backupFile) { public void gpxSavingFinished(Exception warning, GPXFile savedGpxFile, File backupFile) {
onGpxSaved(warning, savedGpxFile, outFile, backupFile, actionType, finalSaveAction, showOnMap); onGpxSaved(warning, savedGpxFile, outFile, backupFile, finalSaveAction, showOnMap);
} }
}; };
SaveGpxRouteAsyncTask saveTask = new SaveGpxRouteAsyncTask(this, outFile, gpxFile, actionType, saveType, showOnMap, saveGpxRouteListener); SaveGpxRouteAsyncTask saveTask = new SaveGpxRouteAsyncTask(this, outFile, gpxFile, simplified, showOnMap, saveGpxRouteListener);
saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
private void onGpxSaved(Exception warning, GPXFile savedGpxFile, final File outFile, final File backupFile, private void onGpxSaved(Exception warning, GPXFile savedGpxFile, final File outFile, final File backupFile,
final ActionType actionType, FinalSaveAction finalSaveAction, final boolean showOnMap) { FinalSaveAction finalSaveAction, final boolean showOnMap) {
MapActivity mapActivity = getMapActivity(); MapActivity mapActivity = getMapActivity();
if (mapActivity == null) { if (mapActivity == null) {
return; return;
@ -1571,9 +1537,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
mapActivity.refreshMap(); mapActivity.refreshMap();
if (warning == null) { if (warning == null) {
if (editingCtx.isNewData() && savedGpxFile != null) { if (editingCtx.isNewData() && savedGpxFile != null) {
QuadRect rect = savedGpxFile.getRect(); GpxData gpxData = new GpxData(savedGpxFile);
TrkSegment segment = savedGpxFile.getNonEmptyTrkSegment();
GpxData gpxData = new GpxData(savedGpxFile, rect, ActionType.EDIT_SEGMENT, segment);
editingCtx.setGpxData(gpxData); editingCtx.setGpxData(gpxData);
updateToolbar(); updateToolbar();
} }
@ -1599,7 +1563,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
GPXFile gpx = GPXUtilities.loadGPXFile(outFile); GPXFile gpx = GPXUtilities.loadGPXFile(outFile);
setupGpxData(gpx); setupGpxData(gpx);
if (showOnMap) { if (showOnMap) {
showGpxOnMap(app, gpx, actionType, false); showGpxOnMap(app, gpx, false);
} }
} else { } else {
setupGpxData(null); setupGpxData(null);
@ -1645,12 +1609,10 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
} }
} }
protected static void showGpxOnMap(OsmandApplication app, GPXFile gpx, ActionType actionType, boolean isNewGpx) { protected static void showGpxOnMap(OsmandApplication app, GPXFile gpx, boolean isNewGpx) {
SelectedGpxFile sf = app.getSelectedGpxHelper().selectGpxFile(gpx, true, false); SelectedGpxFile sf = app.getSelectedGpxHelper().selectGpxFile(gpx, true, false);
if (sf != null && !isNewGpx) { if (sf != null && !isNewGpx) {
if (actionType == ActionType.ADD_SEGMENT || actionType == ActionType.EDIT_SEGMENT) { sf.processPoints(app);
sf.processPoints(app);
}
} }
} }
@ -1689,25 +1651,9 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
if (mapActivity == null) { if (mapActivity == null) {
return; return;
} }
final GpxData gpxData = editingCtx.getGpxData();
String fileName = getSuggestedFileName(); String fileName = getSuggestedFileName();
String actionStr = getString(R.string.plan_route);
boolean editMode = isInEditMode(); boolean editMode = isInEditMode();
if (editMode) { String actionStr = getString(editMode ? R.string.edit_line : R.string.plan_route);
ActionType actionType = gpxData.getActionType();
switch (actionType) {
case ADD_ROUTE_POINTS:
actionStr = getString(R.string.add_route_points);
break;
case ADD_SEGMENT:
actionStr = getString(R.string.add_line);
break;
case EDIT_SEGMENT:
case OVERWRITE_SEGMENT:
actionStr = getString(R.string.edit_line);
break;
}
}
if (!editMode && editingCtx.getPointsCount() > 1) { if (!editMode && editingCtx.getPointsCount() > 1) {
toolBarController.setTitle(fileName.replace('_', ' ')); toolBarController.setTitle(fileName.replace('_', ' '));
toolBarController.setDescription(actionStr); toolBarController.setDescription(actionStr);
@ -1954,12 +1900,13 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
} }
@Override @Override
public void onGpxApproximationDone(GpxRouteApproximation gpxApproximation, ApplicationMode mode) { public void onGpxApproximationDone(List<GpxRouteApproximation> gpxApproximations, List<List<WptPt>> pointsList, ApplicationMode mode) {
MeasurementToolLayer measurementLayer = getMeasurementLayer(); MeasurementToolLayer measurementLayer = getMeasurementLayer();
if (measurementLayer != null) { if (measurementLayer != null) {
boolean approximationMode = editingCtx.isInApproximationMode();
editingCtx.setInApproximationMode(true); editingCtx.setInApproximationMode(true);
ApplyGpxApproximationCommand command = new ApplyGpxApproximationCommand(measurementLayer, gpxApproximation, mode); ApplyGpxApproximationCommand command = new ApplyGpxApproximationCommand(measurementLayer, gpxApproximations, pointsList, mode);
if (!editingCtx.getCommandManager().update(command)) { if (!approximationMode || !editingCtx.getCommandManager().update(command)) {
editingCtx.getCommandManager().execute(command); editingCtx.getCommandManager().execute(command);
} }
if (pointsListOpened) { if (pointsListOpened) {
@ -1971,7 +1918,6 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
@Override @Override
public void onApplyGpxApproximation() { public void onApplyGpxApproximation() {
approximationApplied = true;
exitApproximationMode(); exitApproximationMode();
doAddOrMovePointCommonStuff(); doAddOrMovePointCommonStuff();
updateSnapToRoadControls(); updateSnapToRoadControls();
@ -1986,7 +1932,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
if (mapActivity != null) { if (mapActivity != null) {
if (editingCtx.hasRoute()) { if (editingCtx.hasRoute()) {
String trackName = getSuggestedFileName(); String trackName = getSuggestedFileName();
GPXFile gpx = editingCtx.exportRouteAsGpx(trackName); GPXFile gpx = editingCtx.exportGpx(trackName);
if (gpx != null) { if (gpx != null) {
ApplicationMode appMode = editingCtx.getAppMode(); ApplicationMode appMode = editingCtx.getAppMode();
dismiss(mapActivity); dismiss(mapActivity);

View file

@ -183,22 +183,27 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL
lineAttrs.updatePaints(view.getApplication(), settings, tb); lineAttrs.updatePaints(view.getApplication(), settings, tb);
if (editingCtx.isInApproximationMode()) { if (editingCtx.isInApproximationMode()) {
List<WptPt> originalTrackPointList = editingCtx.getOriginalTrackPointList(); List<List<WptPt>> originalPointsList = editingCtx.getOriginalSegmentPointsList();
if (originalTrackPointList != null) { if (originalPointsList != null) {
lineAttrs.customColorPaint.setColor(ContextCompat.getColor(view.getContext(), lineAttrs.customColorPaint.setColor(ContextCompat.getColor(view.getContext(),
R.color.activity_background_transparent_color_dark)); R.color.activity_background_transparent_color_dark));
new Renderable.StandardTrack(new ArrayList<>(originalTrackPointList), 17.2). for (List<WptPt> points : originalPointsList) {
drawSegment(view.getZoom(), lineAttrs.customColorPaint, canvas, tb); new Renderable.StandardTrack(new ArrayList<>(points), 17.2).
drawSegment(view.getZoom(), lineAttrs.customColorPaint, canvas, tb);
}
} }
} }
TrkSegment before = editingCtx.getBeforeTrkSegmentLine(); List<TrkSegment> before = editingCtx.getBeforeTrkSegmentLine();
new Renderable.StandardTrack(new ArrayList<>(before.points), 17.2). for (TrkSegment segment : before) {
drawSegment(view.getZoom(), lineAttrs.paint, canvas, tb); new Renderable.StandardTrack(new ArrayList<>(segment.points), 17.2).
drawSegment(view.getZoom(), lineAttrs.paint, canvas, tb);
TrkSegment after = editingCtx.getAfterTrkSegmentLine(); }
new Renderable.StandardTrack(new ArrayList<>(after.points), 17.2). List<TrkSegment> after = editingCtx.getAfterTrkSegmentLine();
drawSegment(view.getZoom(), lineAttrs.paint, canvas, tb); for (TrkSegment segment : after) {
new Renderable.StandardTrack(new ArrayList<>(segment.points), 17.2).
drawSegment(view.getZoom(), lineAttrs.paint, canvas, tb);
}
drawPoints(canvas, tb); drawPoints(canvas, tb);
} }
@ -308,36 +313,48 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL
} }
private void drawBeforeAfterPath(Canvas canvas, RotatedTileBox tb) { private void drawBeforeAfterPath(Canvas canvas, RotatedTileBox tb) {
TrkSegment before = editingCtx.getBeforeTrkSegmentLine(); List<TrkSegment> before = editingCtx.getBeforeTrkSegmentLine();
TrkSegment after = editingCtx.getAfterTrkSegmentLine(); List<TrkSegment> after = editingCtx.getAfterTrkSegmentLine();
if (before.points.size() > 0 || after.points.size() > 0) { if (before.size() > 0 || after.size() > 0) {
path.reset(); path.reset();
tx.clear(); tx.clear();
ty.clear(); ty.clear();
if (before.points.size() > 0) { boolean hasPointsBefore = false;
WptPt pt = before.points.get(before.points.size() - 1); if (before.size() > 0) {
float locX = tb.getPixXFromLatLon(pt.lat, pt.lon); TrkSegment segment = before.get(before.size() - 1);
float locY = tb.getPixYFromLatLon(pt.lat, pt.lon); if (segment.points.size() > 0) {
tx.add(locX); hasPointsBefore = true;
ty.add(locY); WptPt pt = segment.points.get(segment.points.size() - 1);
tx.add((float) tb.getCenterPixelX()); if (!ApplicationMode.GAP.getStringKey().equals(pt.getProfileType())) {
ty.add((float) tb.getCenterPixelY()); float locX = tb.getPixXFromLatLon(pt.lat, pt.lon);
} float locY = tb.getPixYFromLatLon(pt.lat, pt.lon);
if (after.points.size() > 0) { tx.add(locX);
if (before.points.size() == 0) { ty.add(locY);
tx.add((float) tb.getCenterPixelX()); tx.add((float) tb.getCenterPixelX());
ty.add((float) tb.getCenterPixelY()); ty.add((float) tb.getCenterPixelY());
}
}
}
if (after.size() > 0) {
TrkSegment segment = after.get(0);
if (segment.points.size() > 0) {
if (!hasPointsBefore) {
tx.add((float) tb.getCenterPixelX());
ty.add((float) tb.getCenterPixelY());
}
WptPt pt = segment.points.get(0);
float locX = tb.getPixXFromLatLon(pt.lat, pt.lon);
float locY = tb.getPixYFromLatLon(pt.lat, pt.lon);
tx.add(locX);
ty.add(locY);
} }
WptPt pt = after.points.get(0);
float locX = tb.getPixXFromLatLon(pt.lat, pt.lon);
float locY = tb.getPixYFromLatLon(pt.lat, pt.lon);
tx.add(locX);
ty.add(locY);
} }
GeometryWay.calculatePath(tb, tx, ty, path); if (!tx.isEmpty() && !ty.isEmpty()) {
canvas.drawPath(path, lineAttrs.paint); GeometryWay.calculatePath(tb, tx, ty, path);
canvas.drawPath(path, lineAttrs.paint);
}
} }
} }
@ -367,7 +384,6 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL
pt.lon = l.getLongitude(); pt.lon = l.getLongitude();
boolean allowed = editingCtx.getPointsCount() == 0 || !editingCtx.getPoints().get(editingCtx.getPointsCount() - 1).equals(pt); boolean allowed = editingCtx.getPointsCount() == 0 || !editingCtx.getPoints().get(editingCtx.getPointsCount() - 1).equals(pt);
if (allowed) { if (allowed) {
ApplicationMode applicationMode = editingCtx.getAppMode(); ApplicationMode applicationMode = editingCtx.getAppMode();
if (applicationMode != MeasurementEditingContext.DEFAULT_APP_MODE) { if (applicationMode != MeasurementEditingContext.DEFAULT_APP_MODE) {
pt.setProfileType(applicationMode.getStringKey()); pt.setProfileType(applicationMode.getStringKey());

View file

@ -4,6 +4,8 @@ import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import androidx.annotation.Nullable;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.FileUtils; import net.osmand.FileUtils;
import net.osmand.GPXUtilities; import net.osmand.GPXUtilities;
@ -15,13 +17,10 @@ import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.Version; import net.osmand.plus.Version;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.measurementtool.GpxData.ActionType;
import net.osmand.plus.measurementtool.MeasurementToolFragment.SaveType;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import java.io.File; import java.io.File;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import static net.osmand.IndexConstants.GPX_FILE_EXT; import static net.osmand.IndexConstants.GPX_FILE_EXT;
@ -31,26 +30,22 @@ class SaveGpxRouteAsyncTask extends AsyncTask<Void, Void, Exception> {
private WeakReference<MeasurementToolFragment> fragmentRef; private WeakReference<MeasurementToolFragment> fragmentRef;
private ProgressDialog progressDialog; private ProgressDialog progressDialog;
private SaveType saveType;
private ActionType actionType;
private File outFile; private File outFile;
private File backupFile; private File backupFile;
private GPXFile gpxFile; private GPXFile gpxFile;
private GPXFile savedGpxFile; private GPXFile savedGpxFile;
private boolean simplified;
private boolean showOnMap; private boolean showOnMap;
private SaveGpxRouteListener saveGpxRouteListener; private SaveGpxRouteListener saveGpxRouteListener;
public SaveGpxRouteAsyncTask(MeasurementToolFragment fragment, File outFile, GPXFile gpxFile, public SaveGpxRouteAsyncTask(MeasurementToolFragment fragment, File outFile, GPXFile gpxFile,
ActionType actionType, SaveType saveType, boolean showOnMap, SaveGpxRouteListener saveGpxRouteListener) { boolean simplified, boolean showOnMap, SaveGpxRouteListener saveGpxRouteListener) {
fragmentRef = new WeakReference<>(fragment); fragmentRef = new WeakReference<>(fragment);
this.outFile = outFile; this.outFile = outFile;
this.showOnMap = showOnMap; this.showOnMap = showOnMap;
this.gpxFile = gpxFile; this.gpxFile = gpxFile;
this.actionType = actionType; this.simplified = simplified;
this.saveType = saveType;
this.saveGpxRouteListener = saveGpxRouteListener; this.saveGpxRouteListener = saveGpxRouteListener;
} }
@ -77,119 +72,70 @@ class SaveGpxRouteAsyncTask extends AsyncTask<Void, Void, Exception> {
OsmandApplication app = mapActivity.getMyApplication(); OsmandApplication app = mapActivity.getMyApplication();
MeasurementToolLayer measurementLayer = mapActivity.getMapLayers().getMeasurementToolLayer(); MeasurementToolLayer measurementLayer = mapActivity.getMapLayers().getMeasurementToolLayer();
MeasurementEditingContext editingCtx = fragment.getEditingCtx(); MeasurementEditingContext editingCtx = fragment.getEditingCtx();
Exception res = null;
List<WptPt> points = editingCtx.getPoints();
TrkSegment before = editingCtx.getBeforeTrkSegmentLine();
TrkSegment after = editingCtx.getAfterTrkSegmentLine();
if (gpxFile == null) { if (gpxFile == null) {
String fileName = outFile.getName(); String fileName = outFile.getName();
String trackName = fileName.substring(0, fileName.length() - GPX_FILE_EXT.length()); String trackName = fileName.substring(0, fileName.length() - GPX_FILE_EXT.length());
GPXFile gpx = new GPXFile(Version.getFullVersion(app)); GPXFile gpx = generateGpxFile(measurementLayer, editingCtx, trackName, new GPXFile(Version.getFullVersion(app)));
if (measurementLayer != null) { res = GPXUtilities.writeGpxFile(outFile, gpx);
if (saveType == MeasurementToolFragment.SaveType.LINE) {
TrkSegment segment = new TrkSegment();
if (editingCtx.hasRoute()) {
segment.points.addAll(editingCtx.getRoutePoints());
} else {
segment.points.addAll(before.points);
segment.points.addAll(after.points);
}
Track track = new Track();
track.name = trackName;
track.segments.add(segment);
gpx.tracks.add(track);
} else if (saveType == MeasurementToolFragment.SaveType.ROUTE_POINT) {
if (editingCtx.hasRoute()) {
GPXFile newGpx = editingCtx.exportRouteAsGpx(trackName);
if (newGpx != null) {
gpx = newGpx;
}
}
gpx.addRoutePoints(points);
}
}
Exception res = GPXUtilities.writeGpxFile(outFile, gpx);
gpx.path = outFile.getAbsolutePath(); gpx.path = outFile.getAbsolutePath();
savedGpxFile = gpx; savedGpxFile = gpx;
if (showOnMap) { if (showOnMap) {
MeasurementToolFragment.showGpxOnMap(app, gpx, actionType, true); MeasurementToolFragment.showGpxOnMap(app, gpx, true);
} }
return res;
} else { } else {
GPXFile gpx = gpxFile;
backupFile = FileUtils.backupFile(app, outFile); backupFile = FileUtils.backupFile(app, outFile);
String trackName = Algorithms.getFileNameWithoutExtension(outFile); String trackName = Algorithms.getFileNameWithoutExtension(outFile);
if (measurementLayer != null) { GPXFile gpx = generateGpxFile(measurementLayer, editingCtx, trackName, gpxFile);
if (fragment.isPlanRouteMode()) {
if (saveType == MeasurementToolFragment.SaveType.LINE) {
TrkSegment segment = new TrkSegment();
if (editingCtx.hasRoute()) {
segment.points.addAll(editingCtx.getRoutePoints());
} else {
segment.points.addAll(before.points);
segment.points.addAll(after.points);
}
Track track = new Track();
track.name = trackName;
track.segments.add(segment);
gpx.tracks.add(track);
} else if (saveType == MeasurementToolFragment.SaveType.ROUTE_POINT) {
if (editingCtx.hasRoute()) {
GPXFile newGpx = editingCtx.exportRouteAsGpx(trackName);
if (newGpx != null) {
gpx = newGpx;
}
}
gpx.addRoutePoints(points);
}
} else if (actionType != null) {
GpxData gpxData = editingCtx.getGpxData();
switch (actionType) {
case ADD_SEGMENT: {
List<WptPt> snappedPoints = new ArrayList<>();
snappedPoints.addAll(before.points);
snappedPoints.addAll(after.points);
gpx.addTrkSegment(snappedPoints);
break;
}
case ADD_ROUTE_POINTS: {
gpx.replaceRoutePoints(points);
break;
}
case EDIT_SEGMENT: {
if (gpxData != null) {
TrkSegment segment = new TrkSegment();
segment.points.addAll(points);
gpx.replaceSegment(gpxData.getTrkSegment(), segment);
}
break;
}
case OVERWRITE_SEGMENT: {
if (gpxData != null) {
List<WptPt> snappedPoints = new ArrayList<>();
snappedPoints.addAll(before.points);
snappedPoints.addAll(after.points);
TrkSegment segment = new TrkSegment();
segment.points.addAll(snappedPoints);
gpx.replaceSegment(gpxData.getTrkSegment(), segment);
}
break;
}
}
} else {
gpx.addRoutePoints(points);
}
}
Exception res = null;
if (!gpx.showCurrentTrack) { if (!gpx.showCurrentTrack) {
res = GPXUtilities.writeGpxFile(outFile, gpx); res = GPXUtilities.writeGpxFile(outFile, gpx);
} }
savedGpxFile = gpx; savedGpxFile = gpx;
if (showOnMap) { if (showOnMap) {
MeasurementToolFragment.showGpxOnMap(app, gpx, actionType, false); MeasurementToolFragment.showGpxOnMap(app, gpx, false);
} }
return res;
} }
return res;
}
private GPXFile generateGpxFile(MeasurementToolLayer measurementLayer, MeasurementEditingContext editingCtx,
String trackName, @Nullable GPXFile gpx) {
if (measurementLayer != null) {
List<TrkSegment> before = editingCtx.getBeforeTrkSegmentLine();
List<TrkSegment> after = editingCtx.getAfterTrkSegmentLine();
if (simplified) {
Track track = new Track();
track.name = trackName;
gpx.tracks.add(track);
for (TrkSegment s : before) {
TrkSegment segment = new TrkSegment();
segment.points.addAll(s.points);
track.segments.add(segment);
}
for (TrkSegment s : after) {
TrkSegment segment = new TrkSegment();
segment.points.addAll(s.points);
track.segments.add(segment);
}
} else {
GPXFile newGpx = editingCtx.exportGpx(trackName);
if (newGpx != null) {
List<WptPt> gpxPoints = null;
if (gpx != null) {
gpxPoints = gpx.getPoints();
}
gpx = newGpx;
List<List<WptPt>> routePoints = editingCtx.getRoutePoints();
for (List<WptPt> points : routePoints) {
gpx.addRoutePoints(points, true);
}
if (!Algorithms.isEmpty(gpxPoints)) {
gpx.addPoints(gpxPoints);
}
}
}
}
return gpx;
} }
@Override @Override

View file

@ -1,5 +1,6 @@
package net.osmand.plus.measurementtool; package net.osmand.plus.measurementtool;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@ -25,7 +26,6 @@ import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription;
import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleDividerItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleDividerItem;
import net.osmand.plus.helpers.FontCache; import net.osmand.plus.helpers.FontCache;
import net.osmand.plus.measurementtool.GpxData.ActionType;
import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.util.MapUtils; import net.osmand.util.MapUtils;
@ -39,6 +39,7 @@ public class SelectedPointBottomSheetDialogFragment extends MenuBottomSheetDialo
private static final Log LOG = PlatformUtil.getLog(SelectedPointBottomSheetDialogFragment.class); private static final Log LOG = PlatformUtil.getLog(SelectedPointBottomSheetDialogFragment.class);
private MeasurementEditingContext editingCtx; private MeasurementEditingContext editingCtx;
@SuppressLint("InflateParams")
@Override @Override
public void createMenuItems(Bundle savedInstanceState) { public void createMenuItems(Bundle savedInstanceState) {
MapActivity mapActivity = getMapActivity(); MapActivity mapActivity = getMapActivity();
@ -264,11 +265,7 @@ public class SelectedPointBottomSheetDialogFragment extends MenuBottomSheetDialo
if (!TextUtils.isEmpty(pointName)) { if (!TextUtils.isEmpty(pointName)) {
return pointName; return pointName;
} }
GpxData gpxData = editingCtx.getGpxData(); return getString(R.string.ltr_or_rtl_combine_via_dash, getString(R.string.plugin_distance_point), String.valueOf(pos + 1));
if (gpxData != null && gpxData.getActionType() == ActionType.ADD_ROUTE_POINTS) {
return getString(R.string.route_point) + " - " + (pos + 1);
}
return getString(R.string.plugin_distance_point) + " - " + (pos + 1);
} }
@NonNull @NonNull
@ -305,18 +302,15 @@ public class SelectedPointBottomSheetDialogFragment extends MenuBottomSheetDialo
} }
description.append(OsmAndFormatter.getFormattedDistance(dist, mapActivity.getMyApplication())); description.append(OsmAndFormatter.getFormattedDistance(dist, mapActivity.getMyApplication()));
} }
GpxData gpxData = editingCtx.getGpxData(); double elevation = pt.ele;
if (gpxData != null && gpxData.getActionType() == ActionType.EDIT_SEGMENT) { if (!Double.isNaN(elevation)) {
double elevation = pt.ele; description.append(" ").append((getString(R.string.altitude)).substring(0, 1)).append(": ");
if (!Double.isNaN(elevation)) { description.append(OsmAndFormatter.getFormattedAlt(elevation, mapActivity.getMyApplication()));
description.append(" ").append((getString(R.string.altitude)).substring(0, 1)).append(": "); }
description.append(OsmAndFormatter.getFormattedAlt(elevation, mapActivity.getMyApplication())); float speed = (float) pt.speed;
} if (speed != 0) {
float speed = (float) pt.speed; description.append(" ").append((getString(R.string.map_widget_speed)).substring(0, 1)).append(": ");
if (speed != 0) { description.append(OsmAndFormatter.getFormattedSpeed(speed, mapActivity.getMyApplication()));
description.append(" ").append((getString(R.string.map_widget_speed)).substring(0, 1)).append(": ");
description.append(OsmAndFormatter.getFormattedSpeed(speed, mapActivity.getMyApplication()));
}
} }
return description.toString(); return description.toString();
} }

View file

@ -19,7 +19,6 @@ import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.measurementtool.GpxData.ActionType;
import net.osmand.plus.views.controls.ReorderItemTouchHelperCallback; import net.osmand.plus.views.controls.ReorderItemTouchHelperCallback;
import java.util.Collections; import java.util.Collections;
@ -32,13 +31,11 @@ public class MeasurementToolAdapter extends RecyclerView.Adapter<MeasurementTool
private final List<WptPt> points; private final List<WptPt> points;
private MeasurementAdapterListener listener; private MeasurementAdapterListener listener;
private boolean nightMode; private boolean nightMode;
private final ActionType actionType;
private final static String BULLET = ""; private final static String BULLET = "";
public MeasurementToolAdapter(MapActivity mapActivity, List<WptPt> points, ActionType actionType) { public MeasurementToolAdapter(MapActivity mapActivity, List<WptPt> points) {
this.mapActivity = mapActivity; this.mapActivity = mapActivity;
this.points = points; this.points = points;
this.actionType = actionType;
} }
public void setAdapterListener(MeasurementAdapterListener listener) { public void setAdapterListener(MeasurementAdapterListener listener) {
@ -79,11 +76,7 @@ public class MeasurementToolAdapter extends RecyclerView.Adapter<MeasurementTool
if (!TextUtils.isEmpty(pointTitle)) { if (!TextUtils.isEmpty(pointTitle)) {
holder.title.setText(pointTitle); holder.title.setText(pointTitle);
} else { } else {
if (actionType == ActionType.ADD_ROUTE_POINTS) { holder.title.setText(mapActivity.getString(R.string.ltr_or_rtl_combine_via_dash, mapActivity.getString(R.string.plugin_distance_point), String.valueOf(pos + 1)));
holder.title.setText(mapActivity.getString(R.string.route_point) + " - " + (pos + 1));
} else {
holder.title.setText(mapActivity.getString(R.string.plugin_distance_point) + " - " + (pos + 1));
}
} }
String pointDesc = pt.desc; String pointDesc = pt.desc;
if (!TextUtils.isEmpty(pointDesc)) { if (!TextUtils.isEmpty(pointDesc)) {
@ -114,21 +107,20 @@ public class MeasurementToolAdapter extends RecyclerView.Adapter<MeasurementTool
holder.descr.setText(text); holder.descr.setText(text);
} }
} }
if (actionType == ActionType.EDIT_SEGMENT) { double elevation = pt.ele;
double elevation = pt.ele; if (!Double.isNaN(elevation)) {
if (!Double.isNaN(elevation)) { String eleStr = (mapActivity.getString(R.string.altitude)).substring(0, 1);
String eleStr = (mapActivity.getString(R.string.altitude)).substring(0, 1); holder.elevation.setText(mapActivity.getString(R.string.ltr_or_rtl_combine_via_colon,
holder.elevation.setText(eleStr + ": " + OsmAndFormatter.getFormattedAlt(elevation, mapActivity.getMyApplication())); eleStr, OsmAndFormatter.getFormattedAlt(elevation, mapActivity.getMyApplication())));
} else { } else {
holder.elevation.setText(""); holder.elevation.setText("");
} }
float speed = (float) pt.speed; float speed = (float) pt.speed;
if (speed != 0) { if (speed != 0) {
String speedStr = (mapActivity.getString(R.string.map_widget_speed)).substring(0, 1); String speedStr = (mapActivity.getString(R.string.map_widget_speed)).substring(0, 1);
holder.speed.setText(speedStr + ": " + OsmAndFormatter.getFormattedSpeed(speed, mapActivity.getMyApplication())); holder.speed.setText(speedStr + ": " + OsmAndFormatter.getFormattedSpeed(speed, mapActivity.getMyApplication()));
} else { } else {
holder.speed.setText(""); holder.speed.setText("");
}
} }
holder.deleteBtn.setImageDrawable(iconsCache.getIcon(R.drawable.ic_action_remove_dark, holder.deleteBtn.setImageDrawable(iconsCache.getIcon(R.drawable.ic_action_remove_dark,
nightMode ? R.color.icon_color_default_dark : R.color.icon_color_default_light)); nightMode ? R.color.icon_color_default_dark : R.color.icon_color_default_light));

View file

@ -1,24 +1,36 @@
package net.osmand.plus.measurementtool.command; package net.osmand.plus.measurementtool.command;
import android.util.Pair;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.plus.measurementtool.MeasurementEditingContext;
import net.osmand.plus.measurementtool.MeasurementEditingContext.RoadSegmentData;
import net.osmand.plus.measurementtool.MeasurementToolLayer; import net.osmand.plus.measurementtool.MeasurementToolLayer;
import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation; import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
public class ApplyGpxApproximationCommand extends MeasurementModeCommand { public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
private ApplicationMode mode; private ApplicationMode mode;
private GpxRouteApproximation approximation;
private List<WptPt> points; private List<WptPt> points;
private Map<Pair<WptPt, WptPt>, RoadSegmentData> roadSegmentData;
private List<GpxRouteApproximation> approximations;
private List<List<WptPt>> segmentPointsList;
private final List<List<WptPt>> originalSegmentPointsList;
public ApplyGpxApproximationCommand(MeasurementToolLayer measurementLayer, GpxRouteApproximation approximation, ApplicationMode mode) { public ApplyGpxApproximationCommand(MeasurementToolLayer measurementLayer,
List<GpxRouteApproximation> approximations,
List<List<WptPt>> segmentPointsList, ApplicationMode mode) {
super(measurementLayer); super(measurementLayer);
this.approximation = approximation; this.approximations = approximations;
this.segmentPointsList = segmentPointsList;
this.originalSegmentPointsList = new ArrayList<>(segmentPointsList);
this.mode = mode; this.mode = mode;
} }
@ -26,6 +38,10 @@ public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
return points; return points;
} }
public List<List<WptPt>> getOriginalSegmentPointsList() {
return originalSegmentPointsList;
}
@Override @Override
public MeasurementCommandType getType() { public MeasurementCommandType getType() {
return MeasurementCommandType.APPROXIMATE_POINTS; return MeasurementCommandType.APPROXIMATE_POINTS;
@ -33,8 +49,9 @@ public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
@Override @Override
public boolean execute() { public boolean execute() {
List<WptPt> pts = getEditingCtx().getPoints(); MeasurementEditingContext ctx = getEditingCtx();
points = new ArrayList<>(pts); points = new ArrayList<>(ctx.getPoints());
roadSegmentData = ctx.getRoadSegmentData();
applyApproximation(); applyApproximation();
refreshMap(); refreshMap();
return true; return true;
@ -44,7 +61,7 @@ public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
public boolean update(@NonNull Command command) { public boolean update(@NonNull Command command) {
if (command instanceof ApplyGpxApproximationCommand) { if (command instanceof ApplyGpxApproximationCommand) {
ApplyGpxApproximationCommand approxCommand = (ApplyGpxApproximationCommand) command; ApplyGpxApproximationCommand approxCommand = (ApplyGpxApproximationCommand) command;
approximation = approxCommand.approximation; approximations = approxCommand.approximations;
mode = approxCommand.mode; mode = approxCommand.mode;
applyApproximation(); applyApproximation();
refreshMap(); refreshMap();
@ -55,10 +72,12 @@ public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
@Override @Override
public void undo() { public void undo() {
getEditingCtx().resetAppMode(); MeasurementEditingContext ctx = getEditingCtx();
getEditingCtx().clearSegments(); ctx.resetAppMode();
getEditingCtx().addPoints(points); ctx.clearSegments();
getEditingCtx().updateCacheForSnap(); ctx.setRoadSegmentData(roadSegmentData);
ctx.addPoints(points);
segmentPointsList = new ArrayList<>(originalSegmentPointsList);
refreshMap(); refreshMap();
} }
@ -69,8 +88,15 @@ public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
} }
public void applyApproximation() { public void applyApproximation() {
getEditingCtx().setAppMode(mode); MeasurementEditingContext ctx = getEditingCtx();
getEditingCtx().clearSegments(); ctx.setAppMode(mode);
getEditingCtx().setPoints(approximation, mode); for (int i = 0; i < approximations.size(); i++) {
GpxRouteApproximation approximation = approximations.get(i);
List<WptPt> segmentPoints = segmentPointsList.get(i);
List<WptPt> newSegmentPoints = ctx.setPoints(approximation, segmentPoints, mode);
if (newSegmentPoints != null) {
segmentPointsList.set(i, newSegmentPoints);
}
}
} }
} }

View file

@ -114,7 +114,7 @@ public class ChangeRouteModeCommand extends MeasurementModeCommand {
editingCtx.addPoints(oldPoints); editingCtx.addPoints(oldPoints);
editingCtx.setAppMode(oldMode); editingCtx.setAppMode(oldMode);
editingCtx.setRoadSegmentData(oldRoadSegmentData); editingCtx.setRoadSegmentData(oldRoadSegmentData);
editingCtx.updateCacheForSnap(); editingCtx.updateSegmentsForSnap();
} }
@Override @Override
@ -146,7 +146,7 @@ public class ChangeRouteModeCommand extends MeasurementModeCommand {
if (newRoadSegmentData != null) { if (newRoadSegmentData != null) {
editingCtx.setRoadSegmentData(newRoadSegmentData); editingCtx.setRoadSegmentData(newRoadSegmentData);
} }
editingCtx.updateCacheForSnap(); editingCtx.updateSegmentsForSnap();
} }
private void updateProfileType(WptPt pt) { private void updateProfileType(WptPt pt) {

View file

@ -1,14 +1,20 @@
package net.osmand.plus.measurementtool.command; package net.osmand.plus.measurementtool.command;
import android.util.Pair;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.plus.measurementtool.MeasurementEditingContext;
import net.osmand.plus.measurementtool.MeasurementEditingContext.RoadSegmentData;
import net.osmand.plus.measurementtool.MeasurementToolLayer; import net.osmand.plus.measurementtool.MeasurementToolLayer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
public class ClearPointsCommand extends MeasurementModeCommand { public class ClearPointsCommand extends MeasurementModeCommand {
private List<WptPt> points; private List<WptPt> points;
private Map<Pair<WptPt, WptPt>, RoadSegmentData> roadSegmentData;
private ClearCommandMode clearMode; private ClearCommandMode clearMode;
private int pointPosition; private int pointPosition;
@ -31,27 +37,30 @@ public class ClearPointsCommand extends MeasurementModeCommand {
} }
private void executeCommand() { private void executeCommand() {
List<WptPt> pts = getEditingCtx().getPoints(); MeasurementEditingContext ctx = getEditingCtx();
List<WptPt> pts = ctx.getPoints();
points = new ArrayList<>(pts); points = new ArrayList<>(pts);
roadSegmentData = ctx.getRoadSegmentData();
switch (clearMode) { switch (clearMode) {
case ALL: case ALL:
pts.clear(); pts.clear();
getEditingCtx().clearSegments(); ctx.clearSegments();
break; break;
case BEFORE: case BEFORE:
getEditingCtx().trimBefore(pointPosition); ctx.trimBefore(pointPosition);
break; break;
case AFTER: case AFTER:
getEditingCtx().trimAfter(pointPosition); ctx.trimAfter(pointPosition);
} }
refreshMap(); refreshMap();
} }
@Override @Override
public void undo() { public void undo() {
getEditingCtx().clearSegments(); MeasurementEditingContext ctx = getEditingCtx();
getEditingCtx().addPoints(points); ctx.clearSegments();
getEditingCtx().updateCacheForSnap(); ctx.setRoadSegmentData(roadSegmentData);
ctx.addPoints(points);
refreshMap(); refreshMap();
} }

View file

@ -80,6 +80,6 @@ public class MeasurementCommandManager {
} }
public MeasurementModeCommand getLastCommand() { public MeasurementModeCommand getLastCommand() {
return undoCommands.getLast(); return undoCommands.getFirst();
} }
} }

View file

@ -18,7 +18,7 @@ public class ReorderPointCommand extends MeasurementModeCommand {
@Override @Override
public boolean execute() { public boolean execute() {
getEditingCtx().updateCacheForSnap(); getEditingCtx().updateSegmentsForSnap();
refreshMap(); refreshMap();
return true; return true;
} }
@ -36,7 +36,7 @@ public class ReorderPointCommand extends MeasurementModeCommand {
private void reorder(int addTo, int removeFrom) { private void reorder(int addTo, int removeFrom) {
List<WptPt> points = getEditingCtx().getPoints(); List<WptPt> points = getEditingCtx().getPoints();
points.add(addTo, points.remove(removeFrom)); points.add(addTo, points.remove(removeFrom));
getEditingCtx().updateCacheForSnap(); getEditingCtx().updateSegmentsForSnap();
refreshMap(); refreshMap();
} }

View file

@ -47,7 +47,7 @@ public class ReversePointsCommand extends MeasurementModeCommand {
WptPt lastPoint = newPoints.get(newPoints.size() - 1); WptPt lastPoint = newPoints.get(newPoints.size() - 1);
editingCtx.setAppMode(ApplicationMode.valueOfStringKey(lastPoint.getProfileType(), DEFAULT_APP_MODE)); editingCtx.setAppMode(ApplicationMode.valueOfStringKey(lastPoint.getProfileType(), DEFAULT_APP_MODE));
} }
editingCtx.updateCacheForSnap(); editingCtx.updateSegmentsForSnap();
} }
@Override @Override
@ -57,7 +57,7 @@ public class ReversePointsCommand extends MeasurementModeCommand {
editingCtx.addPoints(oldPoints); editingCtx.addPoints(oldPoints);
editingCtx.setAppMode(oldMode); editingCtx.setAppMode(oldMode);
editingCtx.setRoadSegmentData(oldRoadSegmentData); editingCtx.setRoadSegmentData(oldRoadSegmentData);
editingCtx.updateCacheForSnap(); editingCtx.updateSegmentsForSnap();
} }

View file

@ -50,14 +50,13 @@ import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.settings.backend.CommonPreference;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.activities.TrackActivity; import net.osmand.plus.activities.TrackActivity;
import net.osmand.plus.dialogs.GpxAppearanceAdapter; import net.osmand.plus.dialogs.GpxAppearanceAdapter;
import net.osmand.plus.measurementtool.GpxData;
import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener; import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener;
import net.osmand.plus.settings.backend.CommonPreference;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.track.GpxSplitType; import net.osmand.plus.track.GpxSplitType;
import net.osmand.plus.track.SplitTrackAsyncTask; import net.osmand.plus.track.SplitTrackAsyncTask;
import net.osmand.plus.track.SplitTrackAsyncTask.SplitTrackListener; import net.osmand.plus.track.SplitTrackAsyncTask.SplitTrackListener;
@ -918,17 +917,10 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
} }
} }
public void addNewGpxData(GpxData.ActionType actionType) { public void addNewGpxData() {
TrackActivity activity = getTrackActivity(); TrackActivity activity = getTrackActivity();
if (activity != null) { if (activity != null) {
activity.addNewGpxData(actionType); activity.addNewGpxData();
}
}
public void addNewGpxData(GpxData.ActionType actionType, GPXUtilities.TrkSegment segment) {
TrackActivity activity = getTrackActivity();
if (activity != null) {
activity.addNewGpxData(actionType, segment);
} }
} }
@ -951,10 +943,8 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
PointDescription pointWptDescription = PointDescription pointWptDescription =
new PointDescription(PointDescription.POINT_TYPE_WPT, app.getString(R.string.add_waypoint)); new PointDescription(PointDescription.POINT_TYPE_WPT, app.getString(R.string.add_waypoint));
addPoint(pointWptDescription); addPoint(pointWptDescription);
} else if (i == R.id.route_text_layout || i == R.id.route_fab) { } else if (i == R.id.route_text_layout || i == R.id.route_fab || i == R.id.line_fab) {
addNewGpxData(GpxData.ActionType.ADD_ROUTE_POINTS); addNewGpxData();
} else if (i == R.id.line_text_layout || i == R.id.line_fab) {
addNewGpxData(GpxData.ActionType.ADD_SEGMENT);
} }
} }
}; };

View file

@ -65,11 +65,10 @@ import net.osmand.plus.helpers.GpxUiHelper;
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType; import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType;
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType; import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType;
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet; import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
import net.osmand.plus.measurementtool.GpxData;
import net.osmand.plus.track.SaveGpxAsyncTask;
import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener;
import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener; import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener;
import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.track.SaveGpxAsyncTask;
import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener;
import net.osmand.plus.views.controls.PagerSlidingTabStrip; import net.osmand.plus.views.controls.PagerSlidingTabStrip;
import net.osmand.plus.views.controls.PagerSlidingTabStrip.CustomTabProvider; import net.osmand.plus.views.controls.PagerSlidingTabStrip.CustomTabProvider;
import net.osmand.plus.views.controls.WrapContentHeightViewPager; import net.osmand.plus.views.controls.WrapContentHeightViewPager;
@ -1026,7 +1025,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit
private void editSegment() { private void editSegment() {
TrkSegment segment = getTrkSegment(); TrkSegment segment = getTrkSegment();
if (segment != null && fragmentAdapter != null) { if (segment != null && fragmentAdapter != null) {
fragmentAdapter.addNewGpxData(GpxData.ActionType.EDIT_SEGMENT, segment); fragmentAdapter.addNewGpxData();
} }
} }

View file

@ -24,7 +24,6 @@ import androidx.fragment.app.FragmentManager;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.CallbackWithObject; import net.osmand.CallbackWithObject;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.IndexConstants; import net.osmand.IndexConstants;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
@ -44,7 +43,6 @@ import net.osmand.plus.helpers.GpxUiHelper.GPXInfo;
import net.osmand.plus.importfiles.ImportHelper; import net.osmand.plus.importfiles.ImportHelper;
import net.osmand.plus.importfiles.ImportHelper.OnGpxImportCompleteListener; import net.osmand.plus.importfiles.ImportHelper.OnGpxImportCompleteListener;
import net.osmand.plus.measurementtool.GpxData; import net.osmand.plus.measurementtool.GpxData;
import net.osmand.plus.measurementtool.GpxData.ActionType;
import net.osmand.plus.measurementtool.MeasurementEditingContext; import net.osmand.plus.measurementtool.MeasurementEditingContext;
import net.osmand.plus.measurementtool.MeasurementToolFragment; import net.osmand.plus.measurementtool.MeasurementToolFragment;
import net.osmand.plus.routepreparationmenu.RoutingOptionsHelper.LocalRoutingParameter; import net.osmand.plus.routepreparationmenu.RoutingOptionsHelper.LocalRoutingParameter;
@ -554,10 +552,7 @@ public class FollowTrackFragment extends ContextMenuScrollFragment implements Ca
MapActivity mapActivity = getMapActivity(); MapActivity mapActivity = getMapActivity();
if (mapActivity != null && gpxFile != null) { if (mapActivity != null && gpxFile != null) {
editingTrack = true; editingTrack = true;
QuadRect rect = gpxFile.getRect(); GpxData gpxData = new GpxData(gpxFile);
TrkSegment segment = gpxFile.getNonEmptyTrkSegment();
ActionType actionType = segment == null ? ActionType.ADD_ROUTE_POINTS : ActionType.EDIT_SEGMENT;
GpxData gpxData = new GpxData(gpxFile, rect, actionType, segment);
MeasurementEditingContext editingContext = new MeasurementEditingContext(); MeasurementEditingContext editingContext = new MeasurementEditingContext();
editingContext.setGpxData(gpxData); editingContext.setGpxData(gpxData);
editingContext.setAppMode(app.getRoutingHelper().getAppMode()); editingContext.setAppMode(app.getRoutingHelper().getAppMode());

View file

@ -1,28 +1,23 @@
package net.osmand.plus.routing; package net.osmand.plus.routing;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.LocationsHolder; import net.osmand.LocationsHolder;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher; import net.osmand.ResultMatcher;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.measurementtool.GpxApproximationFragment;
import net.osmand.plus.routing.RouteProvider.RoutingEnvironment; import net.osmand.plus.routing.RouteProvider.RoutingEnvironment;
import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.router.RouteCalculationProgress; import net.osmand.router.RouteCalculationProgress;
import net.osmand.router.RoutePlannerFrontEnd;
import net.osmand.router.RoutePlannerFrontEnd.GpxPoint; import net.osmand.router.RoutePlannerFrontEnd.GpxPoint;
import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation; import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation;
import net.osmand.search.core.SearchResult;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -31,29 +26,31 @@ public class GpxApproximator {
protected static final Log log = PlatformUtil.getLog(GpxApproximator.class); protected static final Log log = PlatformUtil.getLog(GpxApproximator.class);
private OsmandApplication ctx; private final OsmandApplication ctx;
private RoutingHelper routingHelper; private final RoutingHelper routingHelper;
private RoutingEnvironment env; private RoutingEnvironment env;
private GpxRouteApproximation gctx; private GpxRouteApproximation gctx;
private ApplicationMode mode; private ApplicationMode mode;
private LocationsHolder locationsHolder; private final LocationsHolder locationsHolder;
private List<GpxPoint> points; private List<GpxPoint> points;
private LatLon start; private LatLon start;
private LatLon end; private LatLon end;
private double pointApproximation = 50; private double pointApproximation = 50;
private Runnable approximationTask;
private static final ThreadPoolExecutor SINGLE_THREAD_EXECUTOR
= new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());;
private ThreadPoolExecutor singleThreadedExecutor;
private GpxApproximationProgressCallback approximationProgress; private GpxApproximationProgressCallback approximationProgress;
private Future<?> currentApproximationTask;
public interface GpxApproximationProgressCallback { public interface GpxApproximationProgressCallback {
void start(); void start(GpxApproximator approximator);
void updateProgress(int progress); void updateProgress(GpxApproximator approximator, int progress);
void finish(); void finish(GpxApproximator approximator);
} }
public GpxApproximator(@NonNull OsmandApplication ctx, @NonNull LocationsHolder locationsHolder) throws IOException { public GpxApproximator(@NonNull OsmandApplication ctx, @NonNull LocationsHolder locationsHolder) throws IOException {
@ -61,12 +58,7 @@ public class GpxApproximator {
this.locationsHolder = locationsHolder; this.locationsHolder = locationsHolder;
this.routingHelper = ctx.getRoutingHelper(); this.routingHelper = ctx.getRoutingHelper();
this.mode = ApplicationMode.CAR; this.mode = ApplicationMode.CAR;
if (locationsHolder.getSize() > 1) { initEnvironment(mode, locationsHolder);
start = locationsHolder.getLatLon(0);
end = locationsHolder.getLatLon(locationsHolder.getSize() - 1);
prepareEnvironment(ctx, mode);
}
init();
} }
public GpxApproximator(@NonNull OsmandApplication ctx, @NonNull ApplicationMode mode, double pointApproximation, @NonNull LocationsHolder locationsHolder) throws IOException { public GpxApproximator(@NonNull OsmandApplication ctx, @NonNull ApplicationMode mode, double pointApproximation, @NonNull LocationsHolder locationsHolder) throws IOException {
@ -75,16 +67,19 @@ public class GpxApproximator {
this.pointApproximation = pointApproximation; this.pointApproximation = pointApproximation;
this.routingHelper = ctx.getRoutingHelper(); this.routingHelper = ctx.getRoutingHelper();
this.mode = mode; this.mode = mode;
initEnvironment(mode, locationsHolder);
}
private void initEnvironment(@NonNull ApplicationMode mode, @NonNull LocationsHolder locationsHolder) throws IOException {
if (locationsHolder.getSize() > 1) { if (locationsHolder.getSize() > 1) {
start = locationsHolder.getLatLon(0); start = locationsHolder.getLatLon(0);
end = locationsHolder.getLatLon(locationsHolder.getSize() - 1); end = locationsHolder.getLatLon(locationsHolder.getSize() - 1);
prepareEnvironment(ctx, mode); prepareEnvironment(ctx, mode);
} }
init();
} }
private void init() { public static void cancelPendingApproximations() {
singleThreadedExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); SINGLE_THREAD_EXECUTOR.getQueue().clear();
} }
private void prepareEnvironment(@NonNull OsmandApplication ctx, @NonNull ApplicationMode mode) throws IOException { private void prepareEnvironment(@NonNull OsmandApplication ctx, @NonNull ApplicationMode mode) throws IOException {
@ -140,6 +135,10 @@ public class GpxApproximator {
this.approximationProgress = approximationProgress; this.approximationProgress = approximationProgress;
} }
public boolean isCancelled() {
return gctx != null && gctx.ctx.calculationProgress.isCancelled;
}
public void cancelApproximation() { public void cancelApproximation() {
if (gctx != null) { if (gctx != null) {
gctx.ctx.calculationProgress.isCancelled = true; gctx.ctx.calculationProgress.isCancelled = true;
@ -154,10 +153,7 @@ public class GpxApproximator {
this.gctx = gctx; this.gctx = gctx;
startProgress(); startProgress();
updateProgress(gctx); updateProgress(gctx);
if (currentApproximationTask != null) { approximationTask = new Runnable() {
currentApproximationTask.cancel(true);
}
currentApproximationTask = singleThreadedExecutor.submit(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
@ -166,21 +162,23 @@ public class GpxApproximator {
resultMatcher.publish(null); resultMatcher.publish(null);
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
} }
approximationTask = null;
} }
}); };
SINGLE_THREAD_EXECUTOR.submit(approximationTask);
} }
private void startProgress() { private void startProgress() {
final GpxApproximationProgressCallback approximationProgress = this.approximationProgress; final GpxApproximationProgressCallback approximationProgress = this.approximationProgress;
if (approximationProgress != null) { if (approximationProgress != null) {
approximationProgress.start(); approximationProgress.start(this);
} }
} }
private void finishProgress() { private void finishProgress() {
final GpxApproximationProgressCallback approximationProgress = this.approximationProgress; final GpxApproximationProgressCallback approximationProgress = this.approximationProgress;
if (approximationProgress != null) { if (approximationProgress != null) {
approximationProgress.finish(); approximationProgress.finish(this);
} }
} }
@ -192,12 +190,12 @@ public class GpxApproximator {
@Override @Override
public void run() { public void run() {
RouteCalculationProgress calculationProgress = gctx.ctx.calculationProgress; RouteCalculationProgress calculationProgress = gctx.ctx.calculationProgress;
if (!gctx.result.isEmpty() && GpxApproximator.this.gctx == gctx) { if (approximationTask == null && GpxApproximator.this.gctx == gctx) {
finishProgress(); finishProgress();
} }
if (gctx.result.isEmpty() && calculationProgress != null && !calculationProgress.isCancelled) { if (approximationTask != null && calculationProgress != null && !calculationProgress.isCancelled) {
float pr = calculationProgress.getLinearProgress(); float pr = calculationProgress.getLinearProgress();
approximationProgress.updateProgress((int) pr); approximationProgress.updateProgress(GpxApproximator.this, (int) pr);
if (GpxApproximator.this.gctx != gctx) { if (GpxApproximator.this.gctx != gctx) {
// different calculation started // different calculation started
} else { } else {

View file

@ -78,6 +78,7 @@ public class ApplicationMode {
public static final ApplicationMode DEFAULT = createBase(R.string.app_mode_default, "default") public static final ApplicationMode DEFAULT = createBase(R.string.app_mode_default, "default")
.icon(R.drawable.ic_world_globe_dark).reg(); .icon(R.drawable.ic_world_globe_dark).reg();
public static final ApplicationMode GAP = new ApplicationMode(R.string.app_mode_gap, "gap");
public static final ApplicationMode CAR = createBase(R.string.app_mode_car, "car") public static final ApplicationMode CAR = createBase(R.string.app_mode_car, "car")
.icon(R.drawable.ic_action_car_dark) .icon(R.drawable.ic_action_car_dark)