diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteResultPreparation.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteResultPreparation.java
index 6bb4da1ce5..26d984b2da 100644
--- a/OsmAnd-java/src/main/java/net/osmand/router/RouteResultPreparation.java
+++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteResultPreparation.java
@@ -1222,7 +1222,7 @@ public class RouteResultPreparation {
// 3. calculate angle difference
// This method doesn't work if you go from S to N touching only 1 point of roundabout,
// but it is very important to identify very sharp or very large angle to understand did you pass whole roundabout or small entrance
- float turnAngleBasedOnCircle = (float) MapUtils.degreesDiff(firstRoundabout.getBearingBegin(), lastRoundabout.getBearingEnd() + 180);
+ float turnAngleBasedOnCircle = (float) -MapUtils.degreesDiff(firstRoundabout.getBearingBegin(), lastRoundabout.getBearingEnd() + 180);
if (Math.abs(turnAngleBasedOnOutRoads) > 120) {
// correctly identify if angle is +- 180, so we approach from left or right side
t.setTurnAngle(turnAngleBasedOnCircle) ;
diff --git a/OsmAnd/res/drawable/ic_action_user_account_16.xml b/OsmAnd/res/drawable/ic_action_user_account_16.xml
new file mode 100644
index 0000000000..f05533e018
--- /dev/null
+++ b/OsmAnd/res/drawable/ic_action_user_account_16.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingHelper.java b/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingHelper.java
index 9b3d042699..c62d952eb3 100644
--- a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingHelper.java
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingHelper.java
@@ -25,6 +25,8 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import static net.osmand.util.Algorithms.isEmpty;
+
public class OnlineRoutingHelper {
private static final Log LOG = PlatformUtil.getLog(OnlineRoutingHelper.class);
@@ -71,12 +73,21 @@ public class OnlineRoutingHelper {
return null;
}
- @NonNull
- public List calculateRouteOnline(@NonNull OnlineRoutingEngine engine,
- @NonNull List path) throws IOException, JSONException {
+ @Nullable
+ public OnlineRoutingResponse calculateRouteOnline(@Nullable String stringKey,
+ @NonNull List path,
+ boolean leftSideNavigation) throws IOException, JSONException {
+ OnlineRoutingEngine engine = getEngineByKey(stringKey);
+ return engine != null ? calculateRouteOnline(engine, path, leftSideNavigation) : null;
+ }
+
+ @Nullable
+ public OnlineRoutingResponse calculateRouteOnline(@NonNull OnlineRoutingEngine engine,
+ @NonNull List path,
+ boolean leftSideNavigation) throws IOException, JSONException {
String url = engine.getFullUrl(path);
String content = makeRequest(url);
- return engine.parseServerResponse(content);
+ return engine.parseServerResponse(content, leftSideNavigation);
}
@NonNull
@@ -131,7 +142,7 @@ public class OnlineRoutingHelper {
@NonNull
private String createEngineKeyIfNeeded(@NonNull OnlineRoutingEngine engine) {
String key = engine.get(EngineParameter.KEY);
- if (Algorithms.isEmpty(key)) {
+ if (isEmpty(key)) {
key = OnlineRoutingEngine.generateKey();
engine.put(EngineParameter.KEY, key);
}
@@ -151,7 +162,7 @@ public class OnlineRoutingHelper {
private List readFromSettings() {
List engines = new ArrayList<>();
String jsonString = settings.ONLINE_ROUTING_ENGINES.get();
- if (!Algorithms.isEmpty(jsonString)) {
+ if (!isEmpty(jsonString)) {
try {
JSONObject json = new JSONObject(jsonString);
OnlineRoutingUtils.readFromJson(json, engines);
@@ -163,7 +174,7 @@ public class OnlineRoutingHelper {
}
private void saveCacheToSettings() {
- if (!Algorithms.isEmpty(cachedEngines)) {
+ if (!isEmpty(cachedEngines)) {
try {
JSONObject json = new JSONObject();
OnlineRoutingUtils.writeToJson(json, getEngines());
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingResponse.java b/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingResponse.java
new file mode 100644
index 0000000000..02a6a8f53c
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/OnlineRoutingResponse.java
@@ -0,0 +1,24 @@
+package net.osmand.plus.onlinerouting;
+
+import net.osmand.Location;
+import net.osmand.plus.routing.RouteDirectionInfo;
+
+import java.util.List;
+
+public class OnlineRoutingResponse {
+ private List route;
+ private List directions;
+
+ public OnlineRoutingResponse(List route, List directions) {
+ this.route = route;
+ this.directions = directions;
+ }
+
+ public List getRoute() {
+ return route;
+ }
+
+ public List getDirections() {
+ return directions;
+ }
+}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/GraphhopperEngine.java b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/GraphhopperEngine.java
index df9381352b..f2c7e19829 100644
--- a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/GraphhopperEngine.java
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/GraphhopperEngine.java
@@ -3,15 +3,21 @@ package net.osmand.plus.onlinerouting.engine;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import net.osmand.Location;
import net.osmand.data.LatLon;
import net.osmand.plus.R;
import net.osmand.plus.onlinerouting.EngineParameter;
+import net.osmand.plus.onlinerouting.OnlineRoutingResponse;
import net.osmand.plus.onlinerouting.VehicleType;
+import net.osmand.plus.routing.RouteDirectionInfo;
+import net.osmand.router.TurnType;
import net.osmand.util.GeoPolylineParserUtil;
+import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -23,8 +29,9 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
super(params);
}
+ @NonNull
@Override
- public @NonNull EngineType getType() {
+ public EngineType getType() {
return EngineType.GRAPHHOPPER;
}
@@ -71,15 +78,49 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
if (!isEmpty(apiKey)) {
sb.append('&').append("key=").append(apiKey);
}
+ sb.append('&').append("details=").append("lanes");
}
- @NonNull
+ @Nullable
@Override
- public List parseServerResponse(@NonNull String content) throws JSONException {
+ public OnlineRoutingResponse parseServerResponse(@NonNull String content,
+ boolean leftSideNavigation) throws JSONException {
JSONObject obj = new JSONObject(content);
- return GeoPolylineParserUtil.parse(
- obj.getJSONArray("paths").getJSONObject(0).getString("points"),
- GeoPolylineParserUtil.PRECISION_5);
+ JSONObject root = obj.getJSONArray("paths").getJSONObject(0);
+
+ String encoded = root.getString("points");
+ List points = GeoPolylineParserUtil.parse(encoded, GeoPolylineParserUtil.PRECISION_5);
+ if (isEmpty(points)) return null;
+ List route = convertRouteToLocationsList(points);
+
+ JSONArray instructions = root.getJSONArray("instructions");
+ List directions = new ArrayList<>();
+ for (int i = 0; i < instructions.length(); i++) {
+ JSONObject item = instructions.getJSONObject(i);
+ int sign = Integer.parseInt(item.getString("sign"));
+ int distance = (int) Math.round(Double.parseDouble(item.getString("distance")));
+ String description = item.getString("text");
+ String streetName = item.getString("street_name");
+ int timeInSeconds = (int) Math.round(Integer.parseInt(item.getString("time")) / 1000f);
+ JSONArray interval = item.getJSONArray("interval");
+ int startPointOffset = interval.getInt(0);
+ int endPointOffset = interval.getInt(1);
+
+ float averageSpeed = (float) distance / timeInSeconds;
+ TurnType turnType = identifyTurnType(sign, leftSideNavigation);
+ // TODO turnType.setTurnAngle()
+
+ RouteDirectionInfo direction = new RouteDirectionInfo(averageSpeed, turnType);
+ direction.routePointOffset = startPointOffset;
+ if (turnType != null && turnType.isRoundAbout()) {
+ direction.routeEndPointOffset = endPointOffset;
+ }
+ direction.setDescriptionRoute(description);
+ direction.setStreetName(streetName);
+ direction.setDistance(distance);
+ directions.add(direction);
+ }
+ return new OnlineRoutingResponse(route, directions);
}
@Override
@@ -92,4 +133,82 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
}
return obj.has("paths");
}
+
+ /**
+ * @param sign - a number which specifies the turn type to show (Graphhopper API value)
+ * @return a TurnType object defined in OsmAnd which is equivalent to a value from the Graphhopper API
+ *
+ * For future compatibility it is important that all clients
+ * are able to handle also unknown instruction sign numbers
+ */
+ @Nullable
+ public static TurnType identifyTurnType(int sign, boolean leftSide) {
+ int id = INVALID_ID;
+
+ if (sign == -98) {
+ // an U-turn without the knowledge
+ // if it is a right or left U-turn
+ id = TurnType.TU;
+
+ } else if (sign == -8) {
+ // a left U-turn
+ leftSide = false;
+ id = TurnType.TU;
+
+ } else if (sign == -7) {
+ // keep left
+ id = TurnType.KL;
+
+ } else if (sign == -6) {
+ // not yet used: leave roundabout
+
+ } else if (sign == -3) {
+ // turn sharp left
+ id = TurnType.TSHL;
+
+ } else if (sign == -2) {
+ // turn left
+ id = TurnType.TL;
+
+ } else if (sign == -1) {
+ // turn slight left
+ id = TurnType.TSLL;
+
+ } else if (sign == 0) {
+ // continue on street
+ id = TurnType.C;
+
+ } else if (sign == 1) {
+ // turn slight right
+ id = TurnType.TSLR;
+
+ } else if (sign == 2) {
+ // turn right
+ id = TurnType.TR;
+
+ } else if (sign == 3) {
+ // turn sharp right
+ id = TurnType.TSHR;
+
+ } else if (sign == 4) {
+ // the finish instruction before the last point
+
+ } else if (sign == 5) {
+ // the instruction before a via point
+
+ } else if (sign == 6) {
+ // the instruction before entering a roundabout
+ id = TurnType.RNDB;
+
+ } else if (sign == 7) {
+ // keep right
+ id = TurnType.KR;
+
+ } else if (sign == 8) {
+ // a right U-turn
+ id = TurnType.TRU;
+ }
+
+ return id != INVALID_ID ? TurnType.valueOf(id, leftSide) : null;
+ }
}
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OnlineRoutingEngine.java b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OnlineRoutingEngine.java
index 511f31cc67..af94be4282 100644
--- a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OnlineRoutingEngine.java
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OnlineRoutingEngine.java
@@ -5,11 +5,15 @@ import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import net.osmand.GPXUtilities.WptPt;
+import net.osmand.Location;
import net.osmand.data.LatLon;
import net.osmand.plus.R;
import net.osmand.plus.onlinerouting.EngineParameter;
import net.osmand.plus.onlinerouting.OnlineRoutingFactory;
+import net.osmand.plus.onlinerouting.OnlineRoutingResponse;
import net.osmand.plus.onlinerouting.VehicleType;
+import net.osmand.plus.routing.RouteProvider;
import net.osmand.util.Algorithms;
import org.json.JSONException;
@@ -28,7 +32,8 @@ import static net.osmand.util.Algorithms.isEmpty;
public abstract class OnlineRoutingEngine implements Cloneable {
public final static String ONLINE_ROUTING_ENGINE_PREFIX = "online_routing_engine_";
- public static final VehicleType CUSTOM_VEHICLE = new VehicleType("", R.string.shared_string_custom);
+ public final static VehicleType CUSTOM_VEHICLE = new VehicleType("", R.string.shared_string_custom);
+ public final static int INVALID_ID = -1;
private final Map params = new HashMap<>();
private final List allowedVehicles = new ArrayList<>();
@@ -71,15 +76,6 @@ public abstract class OnlineRoutingEngine implements Cloneable {
}
}
- @NonNull
- public String getBaseUrl() {
- String customUrl = get(EngineParameter.CUSTOM_URL);
- if (isEmpty(customUrl)) {
- return getStandardUrl();
- }
- return customUrl;
- }
-
@NonNull
public String getFullUrl(@NonNull List path) {
StringBuilder sb = new StringBuilder(getBaseUrl());
@@ -91,11 +87,35 @@ public abstract class OnlineRoutingEngine implements Cloneable {
@NonNull List path);
@NonNull
- public abstract List parseServerResponse(@NonNull String content) throws JSONException;
+ public String getBaseUrl() {
+ String customUrl = get(EngineParameter.CUSTOM_URL);
+ if (isEmpty(customUrl)) {
+ return getStandardUrl();
+ }
+ return customUrl;
+ }
@NonNull
public abstract String getStandardUrl();
+ @Nullable
+ public abstract OnlineRoutingResponse parseServerResponse(@NonNull String content,
+ boolean leftSideNavigation) throws JSONException;
+
+ @NonNull
+ protected List convertRouteToLocationsList(@NonNull List route) {
+ List result = new ArrayList<>();
+ if (!isEmpty(route)) {
+ for (LatLon pt : route) {
+ WptPt wpt = new WptPt();
+ wpt.lat = pt.getLatitude();
+ wpt.lon = pt.getLongitude();
+ result.add(RouteProvider.createLocation(wpt));
+ }
+ }
+ return result;
+ }
+
@NonNull
public Map getParams() {
return params;
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OrsEngine.java b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OrsEngine.java
index 7c57737d46..4502f5e957 100644
--- a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OrsEngine.java
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OrsEngine.java
@@ -3,9 +3,11 @@ package net.osmand.plus.onlinerouting.engine;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import net.osmand.Location;
import net.osmand.data.LatLon;
import net.osmand.plus.R;
import net.osmand.plus.onlinerouting.EngineParameter;
+import net.osmand.plus.onlinerouting.OnlineRoutingResponse;
import net.osmand.plus.onlinerouting.VehicleType;
import org.json.JSONArray;
@@ -24,8 +26,9 @@ public class OrsEngine extends OnlineRoutingEngine {
super(params);
}
+ @NonNull
@Override
- public @NonNull EngineType getType() {
+ public EngineType getType() {
return EngineType.ORS;
}
@@ -75,20 +78,25 @@ public class OrsEngine extends OnlineRoutingEngine {
}
}
- @NonNull
+ @Nullable
@Override
- public List parseServerResponse(@NonNull String content) throws JSONException {
+ public OnlineRoutingResponse parseServerResponse(@NonNull String content,
+ boolean leftSideNavigation) throws JSONException {
JSONObject obj = new JSONObject(content);
JSONArray array = obj.getJSONArray("features").getJSONObject(0)
.getJSONObject("geometry").getJSONArray("coordinates");
- List track = new ArrayList<>();
+ List points = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
JSONArray point = array.getJSONArray(i);
double lon = Double.parseDouble(point.getString(0));
double lat = Double.parseDouble(point.getString(1));
- track.add(new LatLon(lat, lon));
+ points.add(new LatLon(lat, lon));
}
- return track;
+ if (!isEmpty(points)) {
+ List route = convertRouteToLocationsList(points);
+ new OnlineRoutingResponse(route, null);
+ }
+ return null;
}
@Override
diff --git a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OsrmEngine.java b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OsrmEngine.java
index 1ef9c1a622..478c24886e 100644
--- a/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OsrmEngine.java
+++ b/OsmAnd/src/net/osmand/plus/onlinerouting/engine/OsrmEngine.java
@@ -3,9 +3,11 @@ package net.osmand.plus.onlinerouting.engine;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import net.osmand.Location;
import net.osmand.data.LatLon;
import net.osmand.plus.R;
import net.osmand.plus.onlinerouting.EngineParameter;
+import net.osmand.plus.onlinerouting.OnlineRoutingResponse;
import net.osmand.plus.onlinerouting.VehicleType;
import net.osmand.util.GeoPolylineParserUtil;
@@ -24,7 +26,8 @@ public class OsrmEngine extends OnlineRoutingEngine {
}
@Override
- public @NonNull EngineType getType() {
+ public @NonNull
+ EngineType getType() {
return EngineType.OSRM;
}
@@ -35,7 +38,8 @@ public class OsrmEngine extends OnlineRoutingEngine {
}
@Override
- protected void collectAllowedParameters() { }
+ protected void collectAllowedParameters() {
+ }
@Override
protected void collectAllowedVehicles(@NonNull List vehicles) {
@@ -60,15 +64,21 @@ public class OsrmEngine extends OnlineRoutingEngine {
}
sb.append('?');
sb.append("overview=full");
+ sb.append('&').append("steps=true");
}
- @NonNull
+ @Nullable
@Override
- public List parseServerResponse(@NonNull String content) throws JSONException {
+ public OnlineRoutingResponse parseServerResponse(@NonNull String content,
+ boolean leftSideNavigation) throws JSONException {
JSONObject obj = new JSONObject(content);
- return GeoPolylineParserUtil.parse(
- obj.getJSONArray("routes").getJSONObject(0).getString("geometry"),
- GeoPolylineParserUtil.PRECISION_5);
+ String encoded = obj.getJSONArray("routes").getJSONObject(0).getString("geometry");
+ List points = GeoPolylineParserUtil.parse(encoded, GeoPolylineParserUtil.PRECISION_5);
+ if (!isEmpty(points)) {
+ List route = convertRouteToLocationsList(points);
+ return new OnlineRoutingResponse(route, null);
+ }
+ return null;
}
@Override
diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java
index e6efcc321f..4ef286826d 100644
--- a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java
+++ b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java
@@ -711,7 +711,7 @@ public class RouteCalculationResult {
if (directions != null && directions.size() > 1) {
for (int i = 1; i < directions.size();) {
RouteDirectionInfo r = directions.get(i);
- if (r.getTurnType().getValue() == TurnType.C) {
+ if (r.getTurnType() != null && r.getTurnType().getValue() == TurnType.C) {
RouteDirectionInfo prev = directions.get(i - 1);
prev.setAverageSpeed((prev.distance + r.distance)
/ (prev.distance / prev.getAverageSpeed() + r.distance / r.getAverageSpeed()));
diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java b/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java
index 96017bc96c..96bb1b19dc 100644
--- a/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java
+++ b/OsmAnd/src/net/osmand/plus/routing/RouteProvider.java
@@ -21,7 +21,7 @@ import net.osmand.data.LocationPoint;
import net.osmand.data.WptLocationPoint;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.onlinerouting.OnlineRoutingHelper;
-import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
+import net.osmand.plus.onlinerouting.OnlineRoutingResponse;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.settings.backend.CommonPreference;
import net.osmand.plus.R;
@@ -326,7 +326,7 @@ public class RouteProvider {
}
}
- private static Location createLocation(WptPt pt){
+ public static Location createLocation(WptPt pt){
Location loc = new Location("OsmandRouteProvider");
loc.setLatitude(pt.lat);
loc.setLongitude(pt.lon);
@@ -1202,27 +1202,17 @@ public class RouteProvider {
private RouteCalculationResult findOnlineRoute(RouteCalculationParams params) throws IOException, JSONException {
OnlineRoutingHelper helper = params.ctx.getOnlineRoutingHelper();
String stringKey = params.mode.getRoutingProfile();
- OnlineRoutingEngine engine = helper.getEngineByKey(stringKey);
- List route = null;
- if (engine != null) {
- route = helper.calculateRouteOnline(engine, getFullPathFromParams(params));
- }
- if (!Algorithms.isEmpty(route)) {
- List res = new ArrayList<>();
- for (LatLon pt : route) {
- WptPt wpt = new WptPt();
- wpt.lat = pt.getLatitude();
- wpt.lon = pt.getLongitude();
- res.add(createLocation(wpt));
- }
+ OnlineRoutingResponse response =
+ helper.calculateRouteOnline(stringKey, getPathFromParams(params), params.leftSide);
+ if (response != null) {
params.intermediates = null;
- return new RouteCalculationResult(res, null, params, null, true);
+ return new RouteCalculationResult(response.getRoute(), response.getDirections(), params, null, true);
} else {
return new RouteCalculationResult("Route is empty");
}
}
- private static List getFullPathFromParams(RouteCalculationParams params) {
+ private static List getPathFromParams(RouteCalculationParams params) {
List points = new ArrayList<>();
points.add(new LatLon(params.start.getLatitude(), params.start.getLongitude()));
if (Algorithms.isEmpty(params.intermediates)) {