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 BACKGROUND_TYPE_EXTENSION = "background";
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 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);
}
public boolean hasProfile() {
String profileType = getProfileType();
return profileType != null && !GAP_PROFILE_TYPE.equals(profileType);
}
public void removeProfileType() {
getExtensionsToWrite().remove(PROFILE_TYPE_EXTENSION);
}
@ -374,11 +380,16 @@ public class GPXUtilities {
public static class TrkSegment extends GPXExtensions {
public boolean generalSegment = false;
public List<WptPt> points = new ArrayList<>();
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) {
return split(getDistanceMetric(), getTimeSplit(), meters, joinSegments);
@ -393,7 +404,6 @@ public class GPXUtilities {
splitSegment(metric, secondaryMetric, metricLimit, splitSegments, this, joinSegments);
return convert(splitSegments);
}
}
public static class Track extends GPXExtensions {
@ -1078,9 +1088,6 @@ public class GPXUtilities {
private List<WptPt> points = new ArrayList<>();
public List<Route> routes = new ArrayList<>();
public List<RouteSegment> routeSegments = new ArrayList<>();
public List<RouteType> routeTypes = new ArrayList<>();
public Exception error = null;
public String path = "";
public boolean showCurrentTrack;
@ -1108,7 +1115,7 @@ public class GPXUtilities {
}
public boolean hasRoute() {
return !routeSegments.isEmpty() && !routeTypes.isEmpty();
return getNonEmptyTrkSegments(true).size() > 0;
}
public List<WptPt> getPoints() {
@ -1218,7 +1225,7 @@ public class GPXUtilities {
GPXTrackAnalysis g = new GPXTrackAnalysis();
g.wptPoints = points.size();
g.wptCategoryNames = getWaypointCategories(true);
List<SplitSegment> splitSegments = new ArrayList<GPXUtilities.SplitSegment>();
List<SplitSegment> splitSegments = new ArrayList<>();
for (int i = 0; i < tracks.size(); i++) {
Track subtrack = tracks.get(i);
for (TrkSegment segment : subtrack.segments) {
@ -1243,6 +1250,15 @@ public class GPXUtilities {
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() {
for (Route r : routes) {
if (r.points.size() > 0) {
@ -1318,15 +1334,16 @@ public class GPXUtilities {
return pt;
}
public TrkSegment getNonEmptyTrkSegment() {
for (GPXUtilities.Track t : tracks) {
public List<TrkSegment> getNonEmptyTrkSegments(boolean routesOnly) {
List<TrkSegment> segments = new ArrayList<>();
for (Track t : tracks) {
for (TrkSegment s : t.segments) {
if (s.points.size() > 0) {
return s;
if (!s.generalSegment && s.points.size() > 0 && (!routesOnly || s.hasRoute())) {
segments.add(s);
}
}
}
return null;
return segments;
}
public void addTrkSegment(List<WptPt> points) {
@ -1365,8 +1382,8 @@ public class GPXUtilities {
return false;
}
public void addRoutePoints(List<WptPt> points) {
if (routes.size() == 0) {
public void addRoutePoints(List<WptPt> points, boolean addRoute) {
if (routes.size() == 0 || addRoute) {
Route route = new Route();
routes.add(route);
}
@ -1608,7 +1625,7 @@ public class GPXUtilities {
bottom = Math.min(bottom, p.getLatitude());
}
}
for (GPXUtilities.Route route : routes) {
for (Route route : routes) {
for (WptPt p : route.points) {
if (left == 0 && right == 0) {
left = p.getLongitude();
@ -1720,7 +1737,7 @@ public class GPXUtilities {
public static String asString(GPXFile file) {
final Writer writer = new StringWriter();
GPXUtilities.writeGpx(writer, file);
writeGpx(writer, file);
return writer.toString();
}
@ -1807,6 +1824,8 @@ public class GPXUtilities {
writeWpt(format, serializer, p);
serializer.endTag(null, "trkpt"); //$NON-NLS-1$
}
assignRouteExtensionWriter(segment);
writeExtensions(serializer, segment);
serializer.endTag(null, "trkseg"); //$NON-NLS-1$
}
writeExtensions(serializer, track);
@ -1834,7 +1853,6 @@ public class GPXUtilities {
serializer.endTag(null, "wpt"); //$NON-NLS-1$
}
assignRouteExtensionWriter(file);
writeExtensions(serializer, file);
serializer.endTag(null, "gpx"); //$NON-NLS-1$
@ -1847,19 +1865,19 @@ public class GPXUtilities {
return null;
}
private static void assignRouteExtensionWriter(final GPXFile gpxFile) {
if (gpxFile.hasRoute() && gpxFile.getExtensionsWriter() == null) {
gpxFile.setExtensionsWriter(new GPXExtensionsWriter() {
private static void assignRouteExtensionWriter(final TrkSegment segment) {
if (segment.hasRoute() && segment.getExtensionsWriter() == null) {
segment.setExtensionsWriter(new GPXExtensionsWriter() {
@Override
public void writeExtensions(XmlSerializer serializer) {
StringBundle bundle = new StringBundle();
List<StringBundle> segmentsBundle = new ArrayList<>();
for (RouteSegment segment : gpxFile.routeSegments) {
for (RouteSegment segment : segment.routeSegments) {
segmentsBundle.add(segment.toStringBundle());
}
bundle.putBundleList("route", "segment", segmentsBundle);
List<StringBundle> typesBundle = new ArrayList<>();
for (RouteType routeType : gpxFile.routeTypes) {
for (RouteType routeType : segment.routeTypes) {
typesBundle.add(routeType.toStringBundle());
}
bundle.putBundleList("types", "type", typesBundle);
@ -1901,12 +1919,15 @@ public class GPXUtilities {
}
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();
if (!extensionsToRead.isEmpty() || extensionsWriter != null) {
if (!extensions.isEmpty() || extensionsWriter != null) {
serializer.startTag(null, "extensions");
if (!extensionsToRead.isEmpty()) {
for (Entry<String, String> s : extensionsToRead.entrySet()) {
if (!extensions.isEmpty()) {
for (Entry<String, String> s : extensions.entrySet()) {
writeNotNullText(serializer, s.getKey(), s.getValue());
}
}
@ -1943,7 +1964,20 @@ public class GPXUtilities {
if (!Float.isNaN(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 {
@ -2099,10 +2133,11 @@ public class GPXUtilities {
TrkSegment routeTrackSegment = new TrkSegment();
routeTrack.segments.add(routeTrackSegment);
Stack<GPXExtensions> parserState = new Stack<>();
TrkSegment firstSegment = null;
boolean extensionReadMode = false;
boolean routePointExtension = false;
List<RouteSegment> routeSegments = gpxFile.routeSegments;
List<RouteType> routeTypes = gpxFile.routeTypes;
List<RouteSegment> routeSegments = new ArrayList<>();
List<RouteType> routeTypes = new ArrayList<>();
boolean routeExtension = false;
boolean typesExtension = false;
parserState.push(gpxFile);
@ -2403,6 +2438,16 @@ public class GPXUtilities {
assert pop instanceof Route;
} else if (tag.equals("trkseg")) {
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;
} else if (tag.equals("rpt")) {
Object pop = parserState.pop();
@ -2413,6 +2458,10 @@ public class GPXUtilities {
if (!routeTrackSegment.points.isEmpty()) {
gpxFile.tracks.add(routeTrack);
}
if (!routeSegments.isEmpty() && !routeTypes.isEmpty() && firstSegment != null) {
firstSegment.routeSegments = routeSegments;
firstSegment.routeTypes = routeTypes;
}
} catch (Exception e) {
gpxFile.error = e;
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";
private String name;
private List<RouteSegmentResult> route;
private List<Location> locations;
private List<WptPt> points;
private final String name;
private final List<RouteSegmentResult> route;
private final List<Location> locations;
private final List<WptPt> points;
public RouteExporter(String name, List<RouteSegmentResult> route, List<Location> locations, List<WptPt> points) {
this.name = name;
@ -33,6 +33,34 @@ public class RouteExporter {
}
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);
List<StringBundle> routeItems = new ArrayList<>();
if (!Algorithms.isEmpty(route)) {
@ -57,15 +85,9 @@ public class RouteExporter {
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();
track.segments.add(trkSegment);
if (locations == null || locations.isEmpty()) {
return gpx;
return trkSegment;
}
for (int i = 0; i < locations.size(); i++) {
Location loc = locations.get(i);
@ -83,23 +105,17 @@ public class RouteExporter {
}
trkSegment.points.add(pt);
}
if (points != null) {
for (WptPt pt : points) {
gpx.addPoint(pt);
}
}
List<RouteSegment> routeSegments = new ArrayList<>();
for (StringBundle item : routeItems) {
routeSegments.add(RouteSegment.fromStringBundle(item));
}
gpx.routeSegments = routeSegments;
trkSegment.routeSegments = routeSegments;
List<RouteType> routeTypes = new ArrayList<>();
for (StringBundle item : typeList) {
routeTypes.add(RouteType.fromStringBundle(item));
}
gpx.routeTypes = routeTypes;
return gpx;
trkSegment.routeTypes = routeTypes;
return trkSegment;
}
}

View file

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

View file

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

View file

@ -255,7 +255,8 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
@Override
public void writeToBundle(RouteDataBundle bundle) {
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("speed", speed, 2);
if (turnType != null) {
@ -278,17 +279,22 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
int end = Math.max(startPointIndex, endPointIndex) + 1;
if (object.pointTypes != null && start < 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));
}
if (object.nameIds != null) {
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));
if (object.pointNames != null) {
String[][] names = Arrays.copyOfRange(object.pointNames, start, Math.min(end, object.pointNames.length));
bundle.putArray("pointNames", convertPointNames(types, names, rules));
String[][] names = Arrays.copyOfRange(object.pointNames, start, Math.min(end, object.pointNames.length));
if (reversed) {
Algorithms.reverseArray(types);
Algorithms.reverseArray(names);
}
bundle.putArray("pointNames", convertPointNames(types, names, rules));
}
}

View file

@ -886,6 +886,14 @@ public class Algorithms {
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) {
return Arrays.binarySearch(array, value) >= 0;
}

View file

@ -11,6 +11,8 @@
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="osm_edit_logout_success">Logout successful</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.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.IndexConstants;
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.SelectedGpxFile;
import net.osmand.plus.LockableViewPager;
import net.osmand.plus.settings.backend.OsmAndAppCustomization;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.R;
import net.osmand.plus.mapmarkers.CoordinateInputDialogFragment;
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.TrackPointFragment;
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 java.io.File;
@ -136,14 +135,9 @@ public class TrackActivity extends TabActivity {
}
}
public void addNewGpxData(GpxData.ActionType actionType) {
addNewGpxData(actionType, null);
}
public void addNewGpxData(GpxData.ActionType actionType, TrkSegment segment) {
public void addNewGpxData() {
GPXFile gpxFile = getGpx();
QuadRect rect = getRect();
GpxData gpxData = new GpxData(gpxFile, rect, actionType, segment);
GpxData gpxData = new GpxData(gpxFile);
WptPt pointToShow = gpxFile != null ? gpxFile.findPointToShow() : null;
if (pointToShow != null) {
LatLon location = new LatLon(pointToShow.getLatitude(), pointToShow.getLongitude());
@ -154,7 +148,6 @@ public class TrackActivity extends TabActivity {
false,
gpxData
);
MapActivity.launchMapActivityMoveToTop(this);
}
}

View file

@ -17,6 +17,7 @@ import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import net.osmand.AndroidUtils;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.LocationsHolder;
import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
@ -27,11 +28,18 @@ import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.ContextMenuScrollFragment;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.routing.GpxApproximator;
import net.osmand.plus.routing.GpxApproximator.GpxApproximationProgressCallback;
import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation;
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.SliderCard.SliderCardListener;
@ -49,8 +57,11 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
private ApplicationMode snapToRoadAppMode = ApplicationMode.CAR;
private int distanceThreshold = 50;
private boolean applyApproximation;
private GpxApproximationProgressCallback approximationProgress;
private List<LocationsHolder> locationsHolders;
private final Map<LocationsHolder, GpxRouteApproximation> resultMap = new HashMap<>();
private LocationsHolder locationsHolder;
@Nullable
private GpxApproximator gpxApproximator;
private ProgressBar progressBar;
@ -119,44 +130,26 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
distanceThreshold = savedInstanceState.getInt(DISTANCE_THRESHOLD_KEY);
snapToRoadAppMode = ApplicationMode.valueOfStringKey(
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) {
gpxApproximator.setApproximationProgress(new GpxApproximator.GpxApproximationProgressCallback() {
@Override
public void start(GpxApproximator approximator) {
}
@Override
public void start() {
if (isResumed()) {
startProgress();
}
@Override
public void updateProgress(GpxApproximator approximator, int progress) {
if (isResumed() && approximator == GpxApproximationFragment.this.gpxApproximator) {
float partSize = 100f / locationsHolders.size();
float p = resultMap.size() * partSize + (progress / 100f) * partSize;
GpxApproximationFragment.this.updateProgress((int) p);
}
}
@Override
public void updateProgress(int progress) {
if (isResumed()) {
GpxApproximationFragment.this.updateProgress(progress);
}
}
@Override
public void finish() {
if (isResumed()) {
finishProgress();
}
}
});
}
@Override
public void finish(GpxApproximator approximator) {
}
};
applyButton = mainView.findViewById(R.id.right_bottom_button);
cancelButton = mainView.findViewById(R.id.dismiss_button);
@ -180,7 +173,7 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
}
runLayoutListener();
calculateGpxApproximation();
calculateGpxApproximation(true);
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() {
View mainView = getMainView();
if (mainView != null) {
@ -299,13 +306,17 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
}
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 {
if (!fm.isStateSaved()) {
GpxApproximationFragment fragment = new GpxApproximationFragment();
fragment.setRetainInstance(true);
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);
fm.beginTransaction()
.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) {
try {
this.gpxApproximator = gpxApproximator;
gpxApproximator.setMode(snapToRoadAppMode);
gpxApproximator.setPointApproximation(distanceThreshold);
approximateGpx();
approximateGpx(gpxApproximator);
return true;
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
return false;
}
@Override
public void onSliderChange(int sliderValue) {
if (distanceThreshold != sliderValue) {
distanceThreshold = sliderValue;
calculateGpxApproximation();
calculateGpxApproximation(true);
}
}
@Override
public void onProfileSelect(ApplicationMode applicationMode) {
if (setSnapToRoadAppMode(applicationMode)) {
calculateGpxApproximation();
calculateGpxApproximation(true);
}
}
@ -363,12 +392,12 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
return false;
}
public LocationsHolder getLocationsHolder() {
return locationsHolder;
public List<LocationsHolder> getLocationsHolders() {
return locationsHolders;
}
public void setLocationsHolder(LocationsHolder locationsHolder) {
this.locationsHolder = locationsHolder;
public void setLocationsHolders(List<LocationsHolder> locationsHolders) {
this.locationsHolders = locationsHolders;
}
public void startProgress() {
@ -393,35 +422,58 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
}
}
private void approximateGpx() {
if (gpxApproximator != null) {
setApplyButtonEnabled(false);
gpxApproximator.calculateGpxApproximation(new ResultMatcher<GpxRouteApproximation>() {
@Override
public boolean publish(final GpxRouteApproximation gpxApproximation) {
OsmandApplication app = getMyApplication();
if (app != null) {
app.runInUIThread(new Runnable() {
@Override
public void run() {
Fragment fragment = getTargetFragment();
if (fragment instanceof GpxApproximationFragmentListener) {
((GpxApproximationFragmentListener) fragment).onGpxApproximationDone(gpxApproximation, gpxApproximator.getMode());
private void approximateGpx(@NonNull final GpxApproximator gpxApproximator) {
onApproximationStarted();
gpxApproximator.calculateGpxApproximation(new ResultMatcher<GpxRouteApproximation>() {
@Override
public boolean publish(final GpxRouteApproximation gpxApproximation) {
OsmandApplication app = getMyApplication();
if (app != null) {
app.runInUIThread(new Runnable() {
@Override
public void run() {
if (!gpxApproximator.isCancelled()) {
if (gpxApproximation != null) {
resultMap.put(gpxApproximator.getLocationsHolder(), gpxApproximation);
}
if (!calculateGpxApproximation(false)) {
onApproximationFinished();
}
setApplyButtonEnabled(gpxApproximation != null);
}
});
return true;
}
return false;
}
});
}
return true;
}
@Override
public boolean isCancelled() {
return false;
}
});
@Override
public boolean isCancelled() {
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
@ -431,7 +483,7 @@ public class GpxApproximationFragment extends ContextMenuScrollFragment
public interface GpxApproximationFragmentListener {
void onGpxApproximationDone(GpxRouteApproximation gpxApproximation, ApplicationMode mode);
void onGpxApproximationDone(List<GpxRouteApproximation> gpxApproximations, List<List<WptPt>> pointsList, ApplicationMode mode);
void onApplyGpxApproximation();

View file

@ -1,35 +1,20 @@
package net.osmand.plus.measurementtool;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.data.QuadRect;
public class GpxData {
public enum ActionType {
ADD_SEGMENT,
ADD_ROUTE_POINTS,
EDIT_SEGMENT,
OVERWRITE_SEGMENT
}
private final GPXFile gpxFile;
private final QuadRect rect;
private GPXFile gpxFile;
private TrkSegment trkSegment;
private QuadRect rect;
private ActionType actionType;
public GpxData(GPXFile gpxFile, QuadRect rect, ActionType actionType, TrkSegment trkSegment) {
public GpxData(GPXFile gpxFile) {
this.gpxFile = gpxFile;
this.rect = rect;
this.actionType = actionType;
this.trkSegment = trkSegment;
}
public GpxData(GPXFile gpxFile, GpxData gpxData) {
this.gpxFile = gpxFile;
this.rect = gpxData.rect;
this.actionType = gpxData.actionType;
this.trkSegment = gpxData.trkSegment;
if (gpxFile != null) {
this.rect = gpxFile.getRect();
} else {
this.rect = new QuadRect(0, 0, 0, 0);
}
}
public GPXFile getGpxFile() {
@ -39,12 +24,4 @@ public class GpxData {
public QuadRect getRect() {
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 TrkSegment before = new TrkSegment();
private TrkSegment beforeCacheForSnap;
private List<TrkSegment> beforeSegments = new ArrayList<>();
private List<TrkSegment> beforeSegmentsForSnap;
private final TrkSegment after = new TrkSegment();
private TrkSegment afterCacheForSnap;
private List<TrkSegment> afterSegments = new ArrayList<>();
private List<TrkSegment> afterSegmentsForSnap;
private GpxData gpxData;
@ -79,11 +81,11 @@ public class MeasurementEditingContext {
}
public static class RoadSegmentData {
private ApplicationMode appMode;
private WptPt start;
private WptPt end;
private List<WptPt> points;
private List<RouteSegmentResult> segments;
private final ApplicationMode appMode;
private final WptPt start;
private final WptPt end;
private final List<WptPt> points;
private final List<RouteSegmentResult> segments;
private double distance;
public RoadSegmentData(@NonNull ApplicationMode appMode, @NonNull WptPt start, @NonNull WptPt end,
@ -156,8 +158,8 @@ public class MeasurementEditingContext {
return inAddPointMode;
}
public void updateCacheForSnap() {
updateCacheForSnap(true);
public void updateSegmentsForSnap() {
updateSegmentsForSnap(true);
}
public int getSelectedPointPosition() {
@ -188,10 +190,10 @@ public class MeasurementEditingContext {
this.inApproximationMode = inApproximationMode;
}
public List<WptPt> getOriginalTrackPointList() {
public List<List<WptPt>> getOriginalSegmentPointsList() {
MeasurementModeCommand command = commandManager.getLastCommand();
if (command.getType() == APPROXIMATE_POINTS) {
return ((ApplyGpxApproximationCommand) command).getPoints();
return ((ApplyGpxApproximationCommand) command).getOriginalSegmentPointsList();
}
return null;
}
@ -213,10 +215,6 @@ public class MeasurementEditingContext {
return gpxData != null && gpxData.getGpxFile() != null && gpxData.getGpxFile().hasRtePt();
}
public boolean hasSavedRoute() {
return gpxData != null && gpxData.getGpxFile() != null && gpxData.getGpxFile().hasRoute();
}
public CalculationMode getLastCalculationMode() {
return lastCalculationMode;
}
@ -252,7 +250,7 @@ public class MeasurementEditingContext {
if (appMode != MeasurementEditingContext.DEFAULT_APP_MODE || !pair.first.lastPoint || !pair.second.firstPoint) {
double localDist = MapUtils.getDistance(pair.first.getLatitude(), pair.first.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) {
double h = Math.abs(pair.first.ele - pair.second.ele);
localDist = Math.sqrt(localDist * localDist + h * h);
@ -279,28 +277,70 @@ public class MeasurementEditingContext {
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() {
roadSegmentData.clear();
}
TrkSegment getBeforeTrkSegmentLine() {
if (beforeCacheForSnap != null) {
return beforeCacheForSnap;
List<TrkSegment> getBeforeTrkSegmentLine() {
if (beforeSegmentsForSnap != null) {
return beforeSegmentsForSnap;
}
return before;
return beforeSegments;
}
TrkSegment getAfterTrkSegmentLine() {
if (afterCacheForSnap != null) {
return afterCacheForSnap;
List<TrkSegment> getAfterTrkSegmentLine() {
if (afterSegmentsForSnap != null) {
return afterSegmentsForSnap;
}
return after;
return afterSegments;
}
public List<WptPt> getPoints() {
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() {
return before.points;
}
@ -321,22 +361,43 @@ public class MeasurementEditingContext {
after.points.clear();
before.points.addAll(points.subList(0, position));
after.points.addAll(points.subList(position, points.size()));
updateCacheForSnap(true);
updateSegmentsForSnap(true);
}
public void addPoint(WptPt pt) {
before.points.add(pt);
updateCacheForSnap(false);
updateSegmentsForSnap(false);
}
public void addPoint(int position, WptPt pt) {
before.points.add(position, pt);
updateCacheForSnap(false);
updateSegmentsForSnap(false);
}
public void addPoints(List<WptPt> 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) {
@ -345,7 +406,7 @@ public class MeasurementEditingContext {
}
WptPt pt = before.points.remove(position);
if (updateSnapToRoad) {
updateCacheForSnap(false);
updateSegmentsForSnap(false);
}
return pt;
}
@ -368,15 +429,15 @@ public class MeasurementEditingContext {
public void clearBeforeSegments() {
before.points.clear();
if (beforeCacheForSnap != null) {
beforeCacheForSnap.points.clear();
if (beforeSegmentsForSnap != null) {
beforeSegmentsForSnap.clear();
}
}
public void clearAfterSegments() {
after.points.clear();
if (afterCacheForSnap != null) {
afterCacheForSnap.points.clear();
if (afterSegmentsForSnap != null) {
afterSegmentsForSnap.clear();
}
}
@ -424,8 +485,10 @@ public class MeasurementEditingContext {
List<Pair<WptPt, WptPt>> res = new ArrayList<>();
for (List<WptPt> points : Arrays.asList(before.points, after.points)) {
for (int i = 0; i < points.size() - 1; i++) {
Pair<WptPt, WptPt> pair = new Pair<>(points.get(i), points.get(i + 1));
if (roadSegmentData.get(pair) == null) {
WptPt startPoint = points.get(i);
WptPt endPoint = points.get(i + 1);
Pair<WptPt, WptPt> pair = new Pair<>(startPoint, endPoint);
if (roadSegmentData.get(pair) == null && startPoint.hasProfile()) {
res.add(pair);
}
}
@ -433,98 +496,135 @@ public class MeasurementEditingContext {
return res;
}
private void recreateCacheForSnap(TrkSegment cache, TrkSegment original, boolean calculateIfNeeded) {
boolean hasDefaultModeOnly = true;
if (original.points.size() > 1) {
for (int i = 0; i < original.points.size(); i++) {
String profileType = original.points.get(i).getProfileType();
if (profileType != null && !profileType.equals(DEFAULT_APP_MODE.getStringKey())) {
hasDefaultModeOnly = false;
break;
private void recreateSegments(List<TrkSegment> segments, List<TrkSegment> segmentsForSnap, List<WptPt> points, boolean calculateIfNeeded) {
List<Integer> roadSegmentIndexes = new ArrayList<>();
TrkSegment s = new TrkSegment();
segments.add(s);
boolean defaultMode = true;
if (points.size() > 1) {
for (int i = 0; i < points.size(); i++) {
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) {
for (int i = 0; i < original.points.size() - 1; i++) {
Pair<WptPt, WptPt> pair = new Pair<>(original.points.get(i), original.points.get(i + 1));
RoadSegmentData data = this.roadSegmentData.get(pair);
List<WptPt> pts = data != null ? data.getPoints() : null;
if (pts != null) {
cache.points.addAll(pts);
} else {
if (calculateIfNeeded && !hasDefaultModeOnly) {
scheduleRouteCalculateIfNotEmpty();
if (s.points.isEmpty()) {
segments.remove(s);
}
if (!segments.isEmpty()) {
for (TrkSegment segment : segments) {
TrkSegment segmentForSnap = new TrkSegment();
for (int i = 0; i < segment.points.size() - 1; i++) {
Pair<WptPt, WptPt> pair = new Pair<>(segment.points.get(i), segment.points.get(i + 1));
RoadSegmentData data = this.roadSegmentData.get(pair);
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 {
cache.points.addAll(original.points);
} else if (!points.isEmpty()) {
TrkSegment segmentForSnap = new TrkSegment();
segmentForSnap.points.addAll(points);
segmentsForSnap.add(segmentForSnap);
}
}
void addPoints() {
GpxData gpxData = getGpxData();
if (gpxData == null || gpxData.getTrkSegment() == null || Algorithms.isEmpty(gpxData.getTrkSegment().points)) {
if (gpxData == null || gpxData.getGpxFile() == null) {
return;
}
List<WptPt> points = gpxData.getTrkSegment().points;
if (isTrackSnappedToRoad()) {
RouteImporter routeImporter = new RouteImporter(gpxData.getGpxFile());
List<RouteSegmentResult> segments = routeImporter.importRoute();
List<WptPt> routePoints = gpxData.getGpxFile().getRoutePoints();
int prevPointIndex = 0;
if (routePoints.isEmpty() && points.size() > 1) {
routePoints.add(points.get(0));
routePoints.add(points.get(points.size() - 1));
}
for (int i = 0; i < routePoints.size() - 1; i++) {
Pair<WptPt, WptPt> pair = new Pair<>(routePoints.get(i), routePoints.get(i + 1));
int startIndex = pair.first.getTrkPtIndex();
if (startIndex < 0 || startIndex < prevPointIndex || startIndex >= points.size()) {
startIndex = findPointIndex(pair.first, points, prevPointIndex);
List<TrkSegment> segments = gpxData.getGpxFile().getNonEmptyTrkSegments(false);
if (Algorithms.isEmpty(segments)) {
return;
}
for (int si = 0; si < segments.size(); si++) {
TrkSegment segment = segments.get(si);
List<WptPt> points = segment.points;
if (segment.hasRoute()) {
RouteImporter routeImporter = new RouteImporter(segment);
List<RouteSegmentResult> routeSegments = routeImporter.importRoute();
List<WptPt> routePoints = gpxData.getGpxFile().getRoutePoints(si);
int prevPointIndex = 0;
if (routePoints.isEmpty() && points.size() > 1) {
routePoints.add(points.get(0));
routePoints.add(points.get(points.size() - 1));
}
int endIndex = pair.second.getTrkPtIndex();
if (endIndex < 0 || endIndex < startIndex || endIndex >= points.size()) {
endIndex = findPointIndex(pair.second, points, startIndex);
}
if (startIndex >= 0 && endIndex >= 0) {
List<WptPt> pairPoints = new ArrayList<>();
for (int j = startIndex; j < endIndex && j < points.size(); j++) {
pairPoints.add(points.get(j));
prevPointIndex = j;
for (int i = 0; i < routePoints.size() - 1; i++) {
Pair<WptPt, WptPt> pair = new Pair<>(routePoints.get(i), routePoints.get(i + 1));
int startIndex = pair.first.getTrkPtIndex();
if (startIndex < 0 || startIndex < prevPointIndex || startIndex >= points.size()) {
startIndex = findPointIndex(pair.first, points, prevPointIndex);
}
if (points.size() > prevPointIndex + 1) {
pairPoints.add(points.get(prevPointIndex + 1));
int endIndex = pair.second.getTrkPtIndex();
if (endIndex < 0 || endIndex < startIndex || endIndex >= points.size()) {
endIndex = findPointIndex(pair.second, points, startIndex);
}
Iterator<RouteSegmentResult> it = segments.iterator();
int k = endIndex - startIndex - 1;
List<RouteSegmentResult> pairSegments = new ArrayList<>();
if (k == 0 && !segments.isEmpty()) {
pairSegments.add(segments.remove(0));
} else {
while (it.hasNext() && k > 0) {
RouteSegmentResult s = it.next();
pairSegments.add(s);
it.remove();
k -= Math.abs(s.getEndPointIndex() - s.getStartPointIndex());
if (startIndex >= 0 && endIndex >= 0) {
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) {
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)) {
return;
return null;
}
roadSegmentData.clear();
List<WptPt> routePoints = new ArrayList<>();
List<GpxPoint> gpxPoints = gpxApproximation.finalPoints;
for (int i = 0; i < gpxPoints.size(); i++) {
@ -566,7 +666,13 @@ public class MeasurementEditingContext {
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) {
@ -624,24 +730,21 @@ public class MeasurementEditingContext {
return index;
}
boolean isTrackSnappedToRoad() {
GpxData gpxData = getGpxData();
return gpxData != null && gpxData.getTrkSegment() != null
&& !gpxData.getTrkSegment().points.isEmpty()
&& gpxData.getGpxFile().hasRoute();
}
private void updateCacheForSnap(boolean both) {
recreateCacheForSnap(beforeCacheForSnap = new TrkSegment(), before, true);
private void updateSegmentsForSnap(boolean both) {
recreateSegments(beforeSegments = new ArrayList<>(),
beforeSegmentsForSnap = new ArrayList<>(), before.points, true);
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) {
recreateCacheForSnap(beforeCacheForSnap = new TrkSegment(), before, calculateIfNeeded);
private void updateSegmentsForSnap(boolean both, boolean calculateIfNeeded) {
recreateSegments(beforeSegments = new ArrayList<>(),
beforeSegmentsForSnap = new ArrayList<>(), before.points, calculateIfNeeded);
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;
if (pairs != 0) {
float pairProgress = 100f / pairs;
progress = (int)(calculatedPairs * pairProgress + (float) progress / pairs);
progress = (int) (calculatedPairs * pairProgress + (float) progress / pairs);
}
progressListener.updateProgress(progress);
}
@ -738,7 +841,7 @@ public class MeasurementEditingContext {
application.runInUIThread(new Runnable() {
@Override
public void run() {
updateCacheForSnap(true, false);
updateSegmentsForSnap(true, false);
progressListener.refresh();
RouteCalculationParams params = getParams(false);
if (params != null) {
@ -753,34 +856,41 @@ public class MeasurementEditingContext {
return params;
}
public List<WptPt> getRoutePoints() {
List<WptPt> res = new ArrayList<>();
List<WptPt> points = new ArrayList<>(before.points);
points.addAll(after.points);
int size = points.size();
for (int i = 0; i < size - 1; i++) {
Pair<WptPt, WptPt> pair = new Pair<>(points.get(i), points.get(i + 1));
RoadSegmentData data = this.roadSegmentData.get(pair);
if (data != null) {
res.addAll(data.points);
public List<List<WptPt>> getRoutePoints() {
List<List<WptPt>> res = new ArrayList<>();
List<WptPt> plainPoints = new ArrayList<>(before.points);
plainPoints.addAll(after.points);
List<WptPt> points = new ArrayList<>();
for (WptPt point : plainPoints) {
if (point.getTrkPtIndex() != -1) {
points.add(point);
if (ApplicationMode.GAP.getStringKey().equals(point.getProfileType())) {
res.add(points);
points = new ArrayList<>();
}
}
}
if (!points.isEmpty()) {
res.add(points);
}
return res;
}
@Nullable
public GPXFile exportRouteAsGpx(@NonNull String gpxName) {
if (application == null || before.points.isEmpty() || !hasRoute()) {
public GPXFile exportGpx(@NonNull String gpxName) {
if (application == null || before.points.isEmpty()) {
return null;
}
return RouteExporter.exportRoute(gpxName, getRouteSegments(), null);
}
private TrkSegment getRouteSegment(int startPointIndex, int endPointIndex) {
List<RouteSegmentResult> route = new ArrayList<>();
List<Location> locations = new ArrayList<>();
before.points.get(0).setTrkPtIndex(0);
int size = before.points.size();
for (int i = 0; i < size - 1; i++) {
for (int i = startPointIndex; i < endPointIndex; i++) {
Pair<WptPt, WptPt> pair = new Pair<>(before.points.get(i), before.points.get(i + 1));
RoadSegmentData data = this.roadSegmentData.get(pair);
if (data != null) {
if (data != null && data.points != null && data.segments != null) {
for (WptPt pt : data.points) {
Location l = new Location("");
l.setLatitude(pt.getLatitude());
@ -790,11 +900,42 @@ public class MeasurementEditingContext {
}
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);
}
}
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 {

View file

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

View file

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

View file

@ -4,6 +4,8 @@ import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import androidx.annotation.Nullable;
import net.osmand.AndroidUtils;
import net.osmand.FileUtils;
import net.osmand.GPXUtilities;
@ -15,13 +17,10 @@ import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.Version;
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 java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import static net.osmand.IndexConstants.GPX_FILE_EXT;
@ -31,26 +30,22 @@ class SaveGpxRouteAsyncTask extends AsyncTask<Void, Void, Exception> {
private WeakReference<MeasurementToolFragment> fragmentRef;
private ProgressDialog progressDialog;
private SaveType saveType;
private ActionType actionType;
private File outFile;
private File backupFile;
private GPXFile gpxFile;
private GPXFile savedGpxFile;
private boolean simplified;
private boolean showOnMap;
private SaveGpxRouteListener saveGpxRouteListener;
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);
this.outFile = outFile;
this.showOnMap = showOnMap;
this.gpxFile = gpxFile;
this.actionType = actionType;
this.saveType = saveType;
this.simplified = simplified;
this.saveGpxRouteListener = saveGpxRouteListener;
}
@ -77,119 +72,70 @@ class SaveGpxRouteAsyncTask extends AsyncTask<Void, Void, Exception> {
OsmandApplication app = mapActivity.getMyApplication();
MeasurementToolLayer measurementLayer = mapActivity.getMapLayers().getMeasurementToolLayer();
MeasurementEditingContext editingCtx = fragment.getEditingCtx();
List<WptPt> points = editingCtx.getPoints();
TrkSegment before = editingCtx.getBeforeTrkSegmentLine();
TrkSegment after = editingCtx.getAfterTrkSegmentLine();
Exception res = null;
if (gpxFile == null) {
String fileName = outFile.getName();
String trackName = fileName.substring(0, fileName.length() - GPX_FILE_EXT.length());
GPXFile gpx = new GPXFile(Version.getFullVersion(app));
if (measurementLayer != null) {
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);
GPXFile gpx = generateGpxFile(measurementLayer, editingCtx, trackName, new GPXFile(Version.getFullVersion(app)));
res = GPXUtilities.writeGpxFile(outFile, gpx);
gpx.path = outFile.getAbsolutePath();
savedGpxFile = gpx;
if (showOnMap) {
MeasurementToolFragment.showGpxOnMap(app, gpx, actionType, true);
MeasurementToolFragment.showGpxOnMap(app, gpx, true);
}
return res;
} else {
GPXFile gpx = gpxFile;
backupFile = FileUtils.backupFile(app, outFile);
String trackName = Algorithms.getFileNameWithoutExtension(outFile);
if (measurementLayer != null) {
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;
GPXFile gpx = generateGpxFile(measurementLayer, editingCtx, trackName, gpxFile);
if (!gpx.showCurrentTrack) {
res = GPXUtilities.writeGpxFile(outFile, gpx);
}
savedGpxFile = gpx;
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

View file

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

View file

@ -19,7 +19,6 @@ import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.measurementtool.GpxData.ActionType;
import net.osmand.plus.views.controls.ReorderItemTouchHelperCallback;
import java.util.Collections;
@ -32,13 +31,11 @@ public class MeasurementToolAdapter extends RecyclerView.Adapter<MeasurementTool
private final List<WptPt> points;
private MeasurementAdapterListener listener;
private boolean nightMode;
private final ActionType actionType;
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.points = points;
this.actionType = actionType;
}
public void setAdapterListener(MeasurementAdapterListener listener) {
@ -79,11 +76,7 @@ public class MeasurementToolAdapter extends RecyclerView.Adapter<MeasurementTool
if (!TextUtils.isEmpty(pointTitle)) {
holder.title.setText(pointTitle);
} else {
if (actionType == ActionType.ADD_ROUTE_POINTS) {
holder.title.setText(mapActivity.getString(R.string.route_point) + " - " + (pos + 1));
} else {
holder.title.setText(mapActivity.getString(R.string.plugin_distance_point) + " - " + (pos + 1));
}
holder.title.setText(mapActivity.getString(R.string.ltr_or_rtl_combine_via_dash, mapActivity.getString(R.string.plugin_distance_point), String.valueOf(pos + 1)));
}
String pointDesc = pt.desc;
if (!TextUtils.isEmpty(pointDesc)) {
@ -114,21 +107,20 @@ public class MeasurementToolAdapter extends RecyclerView.Adapter<MeasurementTool
holder.descr.setText(text);
}
}
if (actionType == ActionType.EDIT_SEGMENT) {
double elevation = pt.ele;
if (!Double.isNaN(elevation)) {
String eleStr = (mapActivity.getString(R.string.altitude)).substring(0, 1);
holder.elevation.setText(eleStr + ": " + OsmAndFormatter.getFormattedAlt(elevation, mapActivity.getMyApplication()));
} else {
holder.elevation.setText("");
}
float speed = (float) pt.speed;
if (speed != 0) {
String speedStr = (mapActivity.getString(R.string.map_widget_speed)).substring(0, 1);
holder.speed.setText(speedStr + ": " + OsmAndFormatter.getFormattedSpeed(speed, mapActivity.getMyApplication()));
} else {
holder.speed.setText("");
}
double elevation = pt.ele;
if (!Double.isNaN(elevation)) {
String eleStr = (mapActivity.getString(R.string.altitude)).substring(0, 1);
holder.elevation.setText(mapActivity.getString(R.string.ltr_or_rtl_combine_via_colon,
eleStr, OsmAndFormatter.getFormattedAlt(elevation, mapActivity.getMyApplication())));
} else {
holder.elevation.setText("");
}
float speed = (float) pt.speed;
if (speed != 0) {
String speedStr = (mapActivity.getString(R.string.map_widget_speed)).substring(0, 1);
holder.speed.setText(speedStr + ": " + OsmAndFormatter.getFormattedSpeed(speed, mapActivity.getMyApplication()));
} else {
holder.speed.setText("");
}
holder.deleteBtn.setImageDrawable(iconsCache.getIcon(R.drawable.ic_action_remove_dark,
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;
import android.util.Pair;
import androidx.annotation.NonNull;
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.settings.backend.ApplicationMode;
import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
private ApplicationMode mode;
private GpxRouteApproximation approximation;
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);
this.approximation = approximation;
this.approximations = approximations;
this.segmentPointsList = segmentPointsList;
this.originalSegmentPointsList = new ArrayList<>(segmentPointsList);
this.mode = mode;
}
@ -26,6 +38,10 @@ public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
return points;
}
public List<List<WptPt>> getOriginalSegmentPointsList() {
return originalSegmentPointsList;
}
@Override
public MeasurementCommandType getType() {
return MeasurementCommandType.APPROXIMATE_POINTS;
@ -33,8 +49,9 @@ public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
@Override
public boolean execute() {
List<WptPt> pts = getEditingCtx().getPoints();
points = new ArrayList<>(pts);
MeasurementEditingContext ctx = getEditingCtx();
points = new ArrayList<>(ctx.getPoints());
roadSegmentData = ctx.getRoadSegmentData();
applyApproximation();
refreshMap();
return true;
@ -44,7 +61,7 @@ public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
public boolean update(@NonNull Command command) {
if (command instanceof ApplyGpxApproximationCommand) {
ApplyGpxApproximationCommand approxCommand = (ApplyGpxApproximationCommand) command;
approximation = approxCommand.approximation;
approximations = approxCommand.approximations;
mode = approxCommand.mode;
applyApproximation();
refreshMap();
@ -55,10 +72,12 @@ public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
@Override
public void undo() {
getEditingCtx().resetAppMode();
getEditingCtx().clearSegments();
getEditingCtx().addPoints(points);
getEditingCtx().updateCacheForSnap();
MeasurementEditingContext ctx = getEditingCtx();
ctx.resetAppMode();
ctx.clearSegments();
ctx.setRoadSegmentData(roadSegmentData);
ctx.addPoints(points);
segmentPointsList = new ArrayList<>(originalSegmentPointsList);
refreshMap();
}
@ -69,8 +88,15 @@ public class ApplyGpxApproximationCommand extends MeasurementModeCommand {
}
public void applyApproximation() {
getEditingCtx().setAppMode(mode);
getEditingCtx().clearSegments();
getEditingCtx().setPoints(approximation, mode);
MeasurementEditingContext ctx = getEditingCtx();
ctx.setAppMode(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.setAppMode(oldMode);
editingCtx.setRoadSegmentData(oldRoadSegmentData);
editingCtx.updateCacheForSnap();
editingCtx.updateSegmentsForSnap();
}
@Override
@ -146,7 +146,7 @@ public class ChangeRouteModeCommand extends MeasurementModeCommand {
if (newRoadSegmentData != null) {
editingCtx.setRoadSegmentData(newRoadSegmentData);
}
editingCtx.updateCacheForSnap();
editingCtx.updateSegmentsForSnap();
}
private void updateProfileType(WptPt pt) {

View file

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

View file

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

View file

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

View file

@ -47,7 +47,7 @@ public class ReversePointsCommand extends MeasurementModeCommand {
WptPt lastPoint = newPoints.get(newPoints.size() - 1);
editingCtx.setAppMode(ApplicationMode.valueOfStringKey(lastPoint.getProfileType(), DEFAULT_APP_MODE));
}
editingCtx.updateCacheForSnap();
editingCtx.updateSegmentsForSnap();
}
@Override
@ -57,7 +57,7 @@ public class ReversePointsCommand extends MeasurementModeCommand {
editingCtx.addPoints(oldPoints);
editingCtx.setAppMode(oldMode);
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.OsmAndFormatter;
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.activities.MapActivity;
import net.osmand.plus.activities.TrackActivity;
import net.osmand.plus.dialogs.GpxAppearanceAdapter;
import net.osmand.plus.measurementtool.GpxData;
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.SplitTrackAsyncTask;
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();
if (activity != null) {
activity.addNewGpxData(actionType);
}
}
public void addNewGpxData(GpxData.ActionType actionType, GPXUtilities.TrkSegment segment) {
TrackActivity activity = getTrackActivity();
if (activity != null) {
activity.addNewGpxData(actionType, segment);
activity.addNewGpxData();
}
}
@ -951,10 +943,8 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
PointDescription pointWptDescription =
new PointDescription(PointDescription.POINT_TYPE_WPT, app.getString(R.string.add_waypoint));
addPoint(pointWptDescription);
} else if (i == R.id.route_text_layout || i == R.id.route_fab) {
addNewGpxData(GpxData.ActionType.ADD_ROUTE_POINTS);
} else if (i == R.id.line_text_layout || i == R.id.line_fab) {
addNewGpxData(GpxData.ActionType.ADD_SEGMENT);
} else if (i == R.id.route_text_layout || i == R.id.route_fab || i == R.id.line_fab) {
addNewGpxData();
}
}
};

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.GPXDataSetType;
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.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.CustomTabProvider;
import net.osmand.plus.views.controls.WrapContentHeightViewPager;
@ -1026,7 +1025,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit
private void editSegment() {
TrkSegment segment = getTrkSegment();
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.CallbackWithObject;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.IndexConstants;
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.OnGpxImportCompleteListener;
import net.osmand.plus.measurementtool.GpxData;
import net.osmand.plus.measurementtool.GpxData.ActionType;
import net.osmand.plus.measurementtool.MeasurementEditingContext;
import net.osmand.plus.measurementtool.MeasurementToolFragment;
import net.osmand.plus.routepreparationmenu.RoutingOptionsHelper.LocalRoutingParameter;
@ -554,10 +552,7 @@ public class FollowTrackFragment extends ContextMenuScrollFragment implements Ca
MapActivity mapActivity = getMapActivity();
if (mapActivity != null && gpxFile != null) {
editingTrack = true;
QuadRect rect = gpxFile.getRect();
TrkSegment segment = gpxFile.getNonEmptyTrkSegment();
ActionType actionType = segment == null ? ActionType.ADD_ROUTE_POINTS : ActionType.EDIT_SEGMENT;
GpxData gpxData = new GpxData(gpxFile, rect, actionType, segment);
GpxData gpxData = new GpxData(gpxFile);
MeasurementEditingContext editingContext = new MeasurementEditingContext();
editingContext.setGpxData(gpxData);
editingContext.setAppMode(app.getRoutingHelper().getAppMode());

View file

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

View file

@ -78,6 +78,7 @@ public class ApplicationMode {
public static final ApplicationMode DEFAULT = createBase(R.string.app_mode_default, "default")
.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")
.icon(R.drawable.ic_action_car_dark)