diff --git a/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java b/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java
index aeb0570950..7c83cb8f89 100644
--- a/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java
+++ b/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java
@@ -661,6 +661,41 @@ public class MapUtils {
return new LatLon(Math.toDegrees(phi2), Math.toDegrees(lambda2));
}
+
+ public static double getVectorMagnitude(int startX, int startY, int endX, int endY) {
+ return Math.sqrt(Math.pow((double) (endX - startX), 2.0) + Math.pow((double) (endY - startY), 2.0));
+ }
+
+ //angle of vector
+ public static double getAngleForRadiusVector(int startX, int startY, int endX, int endY) {
+ return 2 * Math.atan((endY - startY) / (endX - startX
+ + Math.sqrt(Math.pow((double) (endX - startX), 2.0) + Math.pow((double) (endY - startY), 2.0))));
+ }
+
+ //returns coordinates of point on circle
+ public static double[] getCoordinatesFromRadiusAndAngle(double centerX, double centerY, double radius, double angle) {
+ double x = centerX + radius * Math.cos(angle);
+ double y = centerY + radius * Math.sin(angle);
+ return new double[]{x,y};
+ }
+
+ //returns signed angle between vectors in radians
+ public static double getAngleBetweenVectors(int vectorAStartX, int vectorAStartY, int vectorAEndX, int vectorAEndY,
+ int vectorBStartX, int vectorBStartY, int vectorBEndX, int vectorBEndY) {
+ int[] vectorA = new int[] {getVectorAxisValue(vectorAStartX, vectorAEndX), getVectorAxisValue(vectorAStartY, vectorAEndY)};
+ int[] vectorB = new int[] {getVectorAxisValue(vectorBStartX, vectorBEndX), getVectorAxisValue(vectorBStartY, vectorBEndY)};
+ return Math.atan2(vectorA[0] * vectorB[1] - vectorA[1] * vectorB [0], vectorA[0] * vectorB[0] + vectorA[1] * vectorB[1]);
+ }
+
+ //calculates vector value for axis
+ public static int getVectorAxisValue(int axisStart, int axisEnd) {
+ if (axisEnd < axisStart) {
+ return Math.abs(axisEnd) - Math.abs(axisStart);
+ } else {
+ return Math.abs(axisStart) - Math.abs(axisEnd);
+ }
+ }
+
}
diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml
index 05a85ae427..abf0c5d13d 100644
--- a/OsmAnd/res/values/strings.xml
+++ b/OsmAnd/res/values/strings.xml
@@ -11,6 +11,7 @@
Thx - Hardy
-->
+ Direct-to-point
Clear recorded data
• Profiles: now you can change order, set icon for map, change all setting for base profiles and restore them back to defaults\n\n
diff --git a/OsmAnd/src/net/osmand/plus/profiles/RoutingProfileDataObject.java b/OsmAnd/src/net/osmand/plus/profiles/RoutingProfileDataObject.java
index b812022ec1..3cd3c74293 100644
--- a/OsmAnd/src/net/osmand/plus/profiles/RoutingProfileDataObject.java
+++ b/OsmAnd/src/net/osmand/plus/profiles/RoutingProfileDataObject.java
@@ -20,6 +20,7 @@ public class RoutingProfileDataObject extends ProfileDataObject {
}
public enum RoutingProfilesResources {
+ DIRECT_TO_MODE(R.string.routing_profile_direct_to, R.drawable.ic_action_navigation_type_direct_to),
STRAIGHT_LINE_MODE(R.string.routing_profile_straightline, R.drawable.ic_action_split_interval),
BROUTER_MODE(R.string.routing_profile_broutrer, R.drawable.ic_action_split_interval),
CAR(R.string.rendering_value_car_name, R.drawable.ic_action_car_dark),
diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationParams.java b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationParams.java
index 00b414465c..99d7c6a959 100644
--- a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationParams.java
+++ b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationParams.java
@@ -18,7 +18,6 @@ public class RouteCalculationParams {
public LatLon end;
public List intermediates;
-
public OsmandApplication ctx;
public RoutingContext cachedRoutingContext;
public ApplicationMode mode;
@@ -35,6 +34,8 @@ public class RouteCalculationParams {
public RouteCalculationProgressCallback calculationProgressCallback;
public RouteCalculationResultListener resultListener;
+ public boolean showOriginalRoute;
+
public interface RouteCalculationResultListener {
void onRouteCalculated(RouteCalculationResult route);
}
diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java
index f93886ac3d..1c838ec327 100644
--- a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java
+++ b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java
@@ -64,6 +64,8 @@ public class RouteCalculationResult {
protected int lastWaypointGPX = 0;
protected ApplicationMode appMode;
+ protected boolean showOriginalRoute = false;
+
public RouteCalculationResult(String errorMessage) {
this.errorMessage = errorMessage;
this.routingTime = 0;
@@ -77,7 +79,7 @@ public class RouteCalculationResult {
this.directions = new ArrayList();
this.alarmInfo = new ArrayList();
}
-
+
public RouteCalculationResult(List list, List directions, RouteCalculationParams params, List waypoints, boolean addMissingTurns) {
this.routingTime = 0;
this.loadedTiles = 0;
@@ -109,6 +111,8 @@ public class RouteCalculationResult {
calculateIntermediateIndexes(params.ctx, this.locations, params.intermediates, localDirections, this.intermediatePoints);
this.directions = Collections.unmodifiableList(localDirections);
updateDirectionsTime(this.directions, this.listDistance);
+
+ this.showOriginalRoute = params.showOriginalRoute;
}
public RouteCalculationResult(List list, Location start, LatLon end, List intermediates,
@@ -1206,5 +1210,8 @@ public class RouteCalculationResult {
public int imminent;
private int directionInfoInd;
}
-
+
+ public boolean isShowOriginalRoute() {
+ return showOriginalRoute;
+ }
}
diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java b/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java
index 018bb5ae9c..f24a47851a 100644
--- a/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java
+++ b/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java
@@ -79,7 +79,8 @@ public class RouteProvider {
public enum RouteService {
OSMAND("OsmAnd (offline)"),
BROUTER("BRouter (offline)"),
- STRAIGHT("Straight line");
+ STRAIGHT("Straight line"),
+ DIRECT_TO("Direct To");
private final String name;
@@ -305,7 +306,7 @@ public class RouteProvider {
try {
RouteCalculationResult res;
boolean calcGPXRoute = params.gpxRoute != null && !params.gpxRoute.points.isEmpty();
- if(calcGPXRoute && !params.gpxRoute.calculateOsmAndRoute){
+ if (calcGPXRoute && !params.gpxRoute.calculateOsmAndRoute) {
res = calculateGpxRoute(params);
} else if (params.mode.getRouteService() == RouteService.OSMAND) {
res = findVectorMapsRoute(params, calcGPXRoute);
@@ -315,10 +316,11 @@ public class RouteProvider {
// res = findORSRoute(params);
// } else if (params.type == RouteService.OSRM) {
// res = findOSRMRoute(params);
- } else if (params.mode.getRouteService() == RouteService.STRAIGHT){
+ } else if (params.mode.getRouteService() == RouteService.STRAIGHT) {
res = findStraightRoute(params);
- }
- else {
+ } else if (params.mode.getRouteService() == RouteService.DIRECT_TO) {
+ res = findDirectTo(params);
+ } else {
res = new RouteCalculationResult("Selected route service is not available");
}
if(log.isInfoEnabled() ){
@@ -1257,4 +1259,31 @@ public class RouteProvider {
dots.add(location);
return new RouteCalculationResult(dots, null, params, null, true);
}
+
+ private RouteCalculationResult findDirectTo(RouteCalculationParams params) {
+ params.showOriginalRoute = true;
+ double[] lats = new double[] { params.start.getLatitude(), params.end.getLatitude() };
+ double[] lons = new double[] { params.start.getLongitude(), params.end.getLongitude() };
+ List intermediates = params.intermediates;
+ List dots = new ArrayList();
+ //writing start location
+ Location location = new Location(String.valueOf("start"));
+ location.setLatitude(lats[0]);
+ location.setLongitude(lons[0]);
+ //adding intermediate dots if they exists
+ if (intermediates != null){
+ for(int i =0; i intermediatePoints;
private Location lastProjection;
private Location lastFixedLocation;
+ private Location originalStartingLocation;
+
+ private RouteCalculationResult originalRoute = null;
private static final int RECALCULATE_THRESHOLD_COUNT_CAUSING_FULL_RECALCULATE = 3;
private static final int RECALCULATE_THRESHOLD_CAUSING_FULL_RECALCULATE_INTERVAL = 2*60*1000;
@@ -176,6 +179,7 @@ public class RoutingHelper {
}
public synchronized void setFinalAndCurrentLocation(LatLon finalLocation, List intermediatePoints, Location currentLocation){
+ setOriginalStartLocation(currentLocation);
checkAndUpdateStartLocation(currentLocation);
RouteCalculationResult previousRoute = route;
clearCurrentRoute(finalLocation, intermediatePoints);
@@ -183,10 +187,30 @@ public class RoutingHelper {
setCurrentLocation(currentLocation, false, previousRoute, true);
}
+ public RouteCalculationResult getOriginalRoute() {
+ return originalRoute;
+ }
+ public List getOriginalRouteAllLoc() {
+ return originalRoute.getImmutableAllLocations();
+ }
+
+ public void setOriginalRoute(RouteCalculationResult originalRoute) {
+ this.originalRoute = originalRoute;
+ }
+
+ private void setOriginalStartLocation(Location currentLocation) {
+ originalStartingLocation = currentLocation;
+ }
+
+ public Location getOriginalStartingLocation() {
+ return originalStartingLocation;
+ }
+
public synchronized void clearCurrentRoute(LatLon newFinalLocation, List newIntermediatePoints) {
route = new RouteCalculationResult("");
isDeviatedFromRoute = false;
evalWaitInterval = 0;
+ originalRoute = null;
app.getWaypointHelper().setNewRoute(route);
app.runInUIThread(new Runnable() {
@Override
@@ -401,6 +425,7 @@ public class RoutingHelper {
// 0. Route empty or needs to be extended? Then re-calculate route.
if(route.isEmpty()) {
calculateRoute = true;
+ //originalRoute = null;
} else {
// 1. Update current route position status according to latest received location
boolean finished = updateCurrentRouteStatus(currentLocation, posTolerance);
@@ -989,6 +1014,7 @@ public class RoutingHelper {
if (res.isCalculated()) {
if (!params.inSnapToRoadMode && !params.inPublicTransportMode) {
route = res;
+ updateOriginalRoute();
}
if (params.resultListener != null) {
params.resultListener.onRouteCalculated(res);
@@ -1094,6 +1120,12 @@ public class RoutingHelper {
}
}
+ private void updateOriginalRoute() {
+ if (originalRoute == null) {
+ originalRoute = route;
+ }
+ }
+
public void startRouteCalculationThread(RouteCalculationParams params, boolean paramsChanged, boolean updateProgress) {
synchronized (this) {
final Thread prevRunningJob = currentRunningJob;
diff --git a/OsmAnd/src/net/osmand/plus/settings/NavigationFragment.java b/OsmAnd/src/net/osmand/plus/settings/NavigationFragment.java
index 8699504c92..2c559e2312 100644
--- a/OsmAnd/src/net/osmand/plus/settings/NavigationFragment.java
+++ b/OsmAnd/src/net/osmand/plus/settings/NavigationFragment.java
@@ -157,6 +157,8 @@ public class NavigationFragment extends BaseSettingsFragment {
RouteProvider.RouteService routeService;
if (profileKey.equals(RoutingProfilesResources.STRAIGHT_LINE_MODE.name())) {
routeService = RouteProvider.RouteService.STRAIGHT;
+ } else if (profileKey.equals(RoutingProfilesResources.DIRECT_TO_MODE.name())){
+ routeService = RouteProvider.RouteService.DIRECT_TO;
} else if (profileKey.equals(RoutingProfilesResources.BROUTER_MODE.name())) {
routeService = RouteProvider.RouteService.BROUTER;
} else {
@@ -209,6 +211,12 @@ public class NavigationFragment extends BaseSettingsFragment {
context.getString(R.string.special_routing_type),
RoutingProfilesResources.STRAIGHT_LINE_MODE.getIconRes(),
false, null));
+ profilesObjects.put(RoutingProfilesResources.DIRECT_TO_MODE.name(), new RoutingProfileDataObject(
+ RoutingProfilesResources.DIRECT_TO_MODE.name(),
+ context.getString(RoutingProfilesResources.DIRECT_TO_MODE.getStringRes()),
+ context.getString(R.string.special_routing_type),
+ RoutingProfilesResources.DIRECT_TO_MODE.getIconRes(),
+ false, null));
if (context.getBRouterService() != null) {
profilesObjects.put(RoutingProfilesResources.BROUTER_MODE.name(), new RoutingProfileDataObject(
RoutingProfilesResources.BROUTER_MODE.name(),
diff --git a/OsmAnd/src/net/osmand/plus/views/RouteLayer.java b/OsmAnd/src/net/osmand/plus/views/RouteLayer.java
index 6475fa6d78..97b22822b4 100644
--- a/OsmAnd/src/net/osmand/plus/views/RouteLayer.java
+++ b/OsmAnd/src/net/osmand/plus/views/RouteLayer.java
@@ -18,6 +18,7 @@ import android.support.v4.content.ContextCompat;
import android.util.Pair;
import net.osmand.Location;
+import net.osmand.PlatformUtil;
import net.osmand.data.LatLon;
import net.osmand.data.PointDescription;
import net.osmand.data.QuadRect;
@@ -45,6 +46,8 @@ import net.osmand.router.TransportRoutePlanner.TransportRouteResult;
import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment;
import net.osmand.util.MapUtils;
+import org.apache.commons.logging.Log;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -68,6 +71,7 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
// keep array lists created
private List actionPoints = new ArrayList();
private List routeTransportStops = new ArrayList<>();
+ private double[] lastProjectionOnPathPoint;
// cache
private Bitmap actionArrow;
@@ -87,6 +91,8 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
private GeometryWayContext wayContext;
+ private LayerDrawable projectionIcon;
+ private final static Log log = PlatformUtil.getLog(RouteLayer.class);
public RouteLayer(RoutingHelper helper) {
this.helper = helper;
this.transportHelper = helper.getTransportRoutingHelper();
@@ -309,6 +315,21 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
}
}
+ private void drawProjectionPoint(RotatedTileBox box, Canvas canvas, double[] projectionXY) {
+ if (projectionIcon == null) {
+ projectionIcon = (LayerDrawable) view.getResources().getDrawable(helper.getSettings().getApplicationMode().getLocationIcon().getIconId());
+ }
+ int locationX = (int) projectionXY[0];
+ int locationY = (int) projectionXY[1];
+
+ projectionIcon.setBounds(locationX - projectionIcon.getIntrinsicWidth() / 2,
+ locationY - projectionIcon.getIntrinsicHeight() / 2,
+ locationX + projectionIcon.getIntrinsicWidth() / 2,
+ locationY + projectionIcon.getIntrinsicHeight() / 2);
+ projectionIcon.draw(canvas);
+
+ }
+
@ColorInt
public int getRouteLineColor(boolean night) {
updateAttrs(new DrawSettings(night), view.getCurrentRotatedTileBox());
@@ -820,7 +841,7 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
return simplifyPoints;
}
}
-
+
private class RouteSimplificationGeometry {
RouteCalculationResult route;
TransportRouteResult transportRoute;
@@ -930,7 +951,7 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
}
private void drawSegments(RotatedTileBox tb, Canvas canvas, double topLatitude, double leftLongitude,
- double bottomLatitude, double rightLongitude, Location lastProjection, int currentRoute) {
+ double bottomLatitude, double rightLongitude, Location lastProjection, int currentRoute, boolean showOriginalRoute) {
if (locations.size() == 0) {
return;
}
@@ -951,12 +972,17 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
previousVisible = true;
}
}
- List routeNodes = locations;
+ List routeNodes;
+ if (showOriginalRoute && helper.getOriginalRoute() != null && helper.getOriginalRouteAllLoc() != null) {
+ routeNodes = helper.getOriginalRouteAllLoc();
+ } else {
+ routeNodes = locations;
+ }
int previous = -1;
for (int i = currentRoute; i < routeNodes.size(); i++) {
Location ls = routeNodes.get(i);
style = getStyle(i, defaultWayStyle);
- if (simplification.getQuick(i) == 0 && !styleMap.containsKey(i)) {
+ if (!showOriginalRoute && (simplification.getQuick(i) == 0 && !styleMap.containsKey(i))) {
continue;
}
if (leftLongitude <= ls.getLongitude() && ls.getLongitude() <= rightLongitude && bottomLatitude <= ls.getLatitude()
@@ -1072,25 +1098,76 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
Location startLocation = new Location("transport");
startLocation.setLatitude(start.getLatitude());
startLocation.setLongitude(start.getLongitude());
- routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude, startLocation, 0);
+ routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude, startLocation, 0, false);
}
} else {
RouteCalculationResult route = helper.getRoute();
routeGeometry.clearTransportRoute();
routeGeometry.updateRoute(tb, route);
- routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude,
- helper.getLastProjection(), route == null ? 0 : route.getCurrentRoute());
+ if (helper.getRoute().isShowOriginalRoute() && helper.getOriginalStartingLocation() != null) {
+ routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude,
+ helper.getOriginalStartingLocation(), 0, true);
+ } else {
+ routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude,
+ helper.getLastProjection(), route == null ? 0 : route.getCurrentRoute(), false);
+ }
List rd = helper.getRouteDirections();
Iterator it = rd.iterator();
- if (tb.getZoom() >= 14) {
+ if (!helper.getRoute().isShowOriginalRoute() && tb.getZoom() >= 14) {
List actionPoints = calculateActionPoints(topLatitude, leftLongitude, bottomLatitude, rightLongitude, helper.getLastProjection(),
helper.getRoute().getRouteLocations(), helper.getRoute().getCurrentRoute(), it, tb.getZoom());
drawAction(tb, canvas, actionPoints);
}
+ if (helper.getRoute().isShowOriginalRoute()) {
+ //add projection point on original route
+ double[] projectionOnRoute = calculateProjectionOnRoutePoint(helper.getLastProjection(),
+ helper.getOriginalRouteAllLoc(), helper, tb);
+ if (projectionOnRoute != null) {
+ drawProjectionPoint(tb, canvas, projectionOnRoute);
+ }
+ }
}
}
-
+ private double[] calculateProjectionOnRoutePoint(Location lastProjection, List routeNodes, RoutingHelper helper, RotatedTileBox box) {
+ double[] projectionXY;
+ boolean visible;
+ Location previousInRoute = null;
+ Location nextInRoute = null;
+ //need to change this code by fixing helper.route.getCurrentRoute() miscalculation
+ if (helper.getRoute().getIntermediatePointsToPass() > 0) {
+ for (int i = 1; i < routeNodes.size(); i++) {
+ LatLon routePoint = new LatLon(routeNodes.get(i).getLatitude(), routeNodes.get(i).getLongitude());
+ if (routePoint.equals(helper.getIntermediatePoints().get(0))) {
+ previousInRoute = routeNodes.get(i - 1);
+ nextInRoute = routeNodes.get(i);
+ }
+ }
+ } else {
+ previousInRoute = routeNodes.get(routeNodes.size() - 2);
+ nextInRoute = routeNodes.get(routeNodes.size() - 1);
+ }
+
+ int centerX = box.getPixXFromLonNoRot(nextInRoute.getLongitude());
+ int centerY = box.getPixYFromLatNoRot(nextInRoute.getLatitude());
+ int aX = box.getPixXFromLonNoRot(lastProjection.getLongitude());
+ int aY = box.getPixYFromLatNoRot(lastProjection.getLatitude());
+ int bX = box.getPixXFromLonNoRot(previousInRoute.getLongitude());
+ int bY = box.getPixYFromLatNoRot(previousInRoute.getLatitude());
+
+ double radius = MapUtils.getVectorMagnitude(centerX, centerY, aX, aY);
+ double angle2 = MapUtils.getAngleForRadiusVector(centerX, centerY, bX, bY);
+ projectionXY = MapUtils.getCoordinatesFromRadiusAndAngle(centerX, centerY, radius, angle2);
+ visible = box.containsPoint((float)projectionXY[0], (float)projectionXY[1], 20.0f)
+ && Math.abs(Math.toDegrees(MapUtils.getAngleBetweenVectors(centerX, centerY, aX, aY, centerX, centerY, bX, bY))) < 90;
+
+ if (visible) {
+ return projectionXY;
+ } else {
+ return null;
+ }
+
+ }
private List calculateActionPoints(double topLatitude, double leftLongitude, double bottomLatitude,
double rightLongitude, Location lastProjection, List routeNodes, int cd,