implement OSRM turn types parsing
This commit is contained in:
parent
3108dfbfbe
commit
fbb1c770a1
8 changed files with 178 additions and 22 deletions
|
@ -668,8 +668,13 @@ public class MapUtils {
|
||||||
|
|
||||||
public static boolean areLatLonEqual(Location l1, Location l2) {
|
public static boolean areLatLonEqual(Location l1, Location l2) {
|
||||||
return l1 == null && l2 == null
|
return l1 == null && l2 == null
|
||||||
|| (l1 != null && l2 != null && Math.abs(l1.getLatitude() - l2.getLatitude()) < 0.00001
|
|| (l2 != null && areLatLonEqual(l1, l2.getLatitude(), l2.getLongitude()));
|
||||||
&& Math.abs(l1.getLongitude() - l2.getLongitude()) < 0.00001);
|
}
|
||||||
|
|
||||||
|
public static boolean areLatLonEqual(Location l, double lat, double lon) {
|
||||||
|
return l != null
|
||||||
|
&& Math.abs(l.getLatitude() - lat) < 0.00001
|
||||||
|
&& Math.abs(l.getLongitude() - lon) < 0.00001;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LatLon rhumbDestinationPoint(LatLon latLon, double distance, double bearing){
|
public static LatLon rhumbDestinationPoint(LatLon latLon, double distance, double bearing){
|
||||||
|
|
|
@ -82,12 +82,12 @@ public class OnlineRoutingHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public OnlineRoutingResponse calculateRouteOnline(@NonNull OnlineRoutingEngine engine,
|
private OnlineRoutingResponse calculateRouteOnline(@NonNull OnlineRoutingEngine engine,
|
||||||
@NonNull List<LatLon> path,
|
@NonNull List<LatLon> path,
|
||||||
boolean leftSideNavigation) throws IOException, JSONException {
|
boolean leftSideNavigation) throws IOException, JSONException {
|
||||||
String url = engine.getFullUrl(path);
|
String url = engine.getFullUrl(path);
|
||||||
String content = makeRequest(url);
|
String content = makeRequest(url);
|
||||||
return engine.parseServerResponse(content, leftSideNavigation);
|
return engine.parseServerResponse(content, app, leftSideNavigation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|
|
@ -5,6 +5,7 @@ import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import net.osmand.Location;
|
import net.osmand.Location;
|
||||||
import net.osmand.data.LatLon;
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.onlinerouting.EngineParameter;
|
import net.osmand.plus.onlinerouting.EngineParameter;
|
||||||
import net.osmand.plus.onlinerouting.OnlineRoutingResponse;
|
import net.osmand.plus.onlinerouting.OnlineRoutingResponse;
|
||||||
|
@ -84,6 +85,7 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public OnlineRoutingResponse parseServerResponse(@NonNull String content,
|
public OnlineRoutingResponse parseServerResponse(@NonNull String content,
|
||||||
|
@NonNull OsmandApplication app,
|
||||||
boolean leftSideNavigation) throws JSONException {
|
boolean leftSideNavigation) throws JSONException {
|
||||||
JSONObject obj = new JSONObject(content);
|
JSONObject obj = new JSONObject(content);
|
||||||
JSONObject root = obj.getJSONArray("paths").getJSONObject(0);
|
JSONObject root = obj.getJSONArray("paths").getJSONObject(0);
|
||||||
|
@ -97,24 +99,24 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
|
||||||
List<RouteDirectionInfo> directions = new ArrayList<>();
|
List<RouteDirectionInfo> directions = new ArrayList<>();
|
||||||
for (int i = 0; i < instructions.length(); i++) {
|
for (int i = 0; i < instructions.length(); i++) {
|
||||||
JSONObject item = instructions.getJSONObject(i);
|
JSONObject item = instructions.getJSONObject(i);
|
||||||
int sign = Integer.parseInt(item.getString("sign"));
|
int sign = item.getInt("sign");
|
||||||
int distance = (int) Math.round(Double.parseDouble(item.getString("distance")));
|
int distance = (int) Math.round(item.getDouble("distance"));
|
||||||
String description = item.getString("text");
|
String description = item.getString("text");
|
||||||
String streetName = item.getString("street_name");
|
String streetName = item.getString("street_name");
|
||||||
int timeInSeconds = (int) Math.round(Integer.parseInt(item.getString("time")) / 1000f);
|
int timeInSeconds = Math.round(item.getInt("time") / 1000f);
|
||||||
JSONArray interval = item.getJSONArray("interval");
|
JSONArray interval = item.getJSONArray("interval");
|
||||||
int startPointOffset = interval.getInt(0);
|
int startPointOffset = interval.getInt(0);
|
||||||
int endPointOffset = interval.getInt(1);
|
int endPointOffset = interval.getInt(1);
|
||||||
|
|
||||||
float averageSpeed = (float) distance / timeInSeconds;
|
float averageSpeed = (float) distance / timeInSeconds;
|
||||||
TurnType turnType = identifyTurnType(sign, leftSideNavigation);
|
TurnType turnType = identifyTurnType(sign, leftSideNavigation);
|
||||||
|
if (turnType == null) {
|
||||||
|
turnType = TurnType.straight();
|
||||||
|
}
|
||||||
// TODO turnType.setTurnAngle()
|
// TODO turnType.setTurnAngle()
|
||||||
|
|
||||||
RouteDirectionInfo direction = new RouteDirectionInfo(averageSpeed, turnType);
|
RouteDirectionInfo direction = new RouteDirectionInfo(averageSpeed, turnType);
|
||||||
direction.routePointOffset = startPointOffset;
|
direction.routePointOffset = startPointOffset;
|
||||||
if (turnType != null && turnType.isRoundAbout()) {
|
|
||||||
direction.routeEndPointOffset = endPointOffset;
|
|
||||||
}
|
|
||||||
direction.setDescriptionRoute(description);
|
direction.setDescriptionRoute(description);
|
||||||
direction.setStreetName(streetName);
|
direction.setStreetName(streetName);
|
||||||
direction.setDistance(distance);
|
direction.setDistance(distance);
|
||||||
|
@ -143,7 +145,7 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static TurnType identifyTurnType(int sign, boolean leftSide) {
|
public static TurnType identifyTurnType(int sign, boolean leftSide) {
|
||||||
int id = INVALID_ID;
|
Integer id = null;
|
||||||
|
|
||||||
if (sign == -98) {
|
if (sign == -98) {
|
||||||
// an U-turn without the knowledge
|
// an U-turn without the knowledge
|
||||||
|
@ -192,6 +194,7 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
|
||||||
|
|
||||||
} else if (sign == 4) {
|
} else if (sign == 4) {
|
||||||
// the finish instruction before the last point
|
// the finish instruction before the last point
|
||||||
|
id = TurnType.C;
|
||||||
|
|
||||||
} else if (sign == 5) {
|
} else if (sign == 5) {
|
||||||
// the instruction before a via point
|
// the instruction before a via point
|
||||||
|
@ -209,6 +212,6 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
|
||||||
id = TurnType.TRU;
|
id = TurnType.TRU;
|
||||||
}
|
}
|
||||||
|
|
||||||
return id != INVALID_ID ? TurnType.valueOf(id, leftSide) : null;
|
return id != null ? TurnType.valueOf(id, leftSide) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import androidx.annotation.Nullable;
|
||||||
import net.osmand.GPXUtilities.WptPt;
|
import net.osmand.GPXUtilities.WptPt;
|
||||||
import net.osmand.Location;
|
import net.osmand.Location;
|
||||||
import net.osmand.data.LatLon;
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.onlinerouting.EngineParameter;
|
import net.osmand.plus.onlinerouting.EngineParameter;
|
||||||
import net.osmand.plus.onlinerouting.OnlineRoutingFactory;
|
import net.osmand.plus.onlinerouting.OnlineRoutingFactory;
|
||||||
|
@ -33,7 +34,6 @@ public abstract class OnlineRoutingEngine implements Cloneable {
|
||||||
|
|
||||||
public final static String ONLINE_ROUTING_ENGINE_PREFIX = "online_routing_engine_";
|
public final static String ONLINE_ROUTING_ENGINE_PREFIX = "online_routing_engine_";
|
||||||
public final static 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<String, String> params = new HashMap<>();
|
private final Map<String, String> params = new HashMap<>();
|
||||||
private final List<VehicleType> allowedVehicles = new ArrayList<>();
|
private final List<VehicleType> allowedVehicles = new ArrayList<>();
|
||||||
|
@ -100,6 +100,7 @@ public abstract class OnlineRoutingEngine implements Cloneable {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public abstract OnlineRoutingResponse parseServerResponse(@NonNull String content,
|
public abstract OnlineRoutingResponse parseServerResponse(@NonNull String content,
|
||||||
|
@NonNull OsmandApplication app,
|
||||||
boolean leftSideNavigation) throws JSONException;
|
boolean leftSideNavigation) throws JSONException;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|
|
@ -5,6 +5,7 @@ import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import net.osmand.Location;
|
import net.osmand.Location;
|
||||||
import net.osmand.data.LatLon;
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.onlinerouting.EngineParameter;
|
import net.osmand.plus.onlinerouting.EngineParameter;
|
||||||
import net.osmand.plus.onlinerouting.OnlineRoutingResponse;
|
import net.osmand.plus.onlinerouting.OnlineRoutingResponse;
|
||||||
|
@ -81,6 +82,7 @@ public class OrsEngine extends OnlineRoutingEngine {
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public OnlineRoutingResponse parseServerResponse(@NonNull String content,
|
public OnlineRoutingResponse parseServerResponse(@NonNull String content,
|
||||||
|
@NonNull OsmandApplication app,
|
||||||
boolean leftSideNavigation) throws JSONException {
|
boolean leftSideNavigation) throws JSONException {
|
||||||
JSONObject obj = new JSONObject(content);
|
JSONObject obj = new JSONObject(content);
|
||||||
JSONArray array = obj.getJSONArray("features").getJSONObject(0)
|
JSONArray array = obj.getJSONArray("features").getJSONObject(0)
|
||||||
|
|
|
@ -5,19 +5,27 @@ import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import net.osmand.Location;
|
import net.osmand.Location;
|
||||||
import net.osmand.data.LatLon;
|
import net.osmand.data.LatLon;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.onlinerouting.EngineParameter;
|
import net.osmand.plus.onlinerouting.EngineParameter;
|
||||||
import net.osmand.plus.onlinerouting.OnlineRoutingResponse;
|
import net.osmand.plus.onlinerouting.OnlineRoutingResponse;
|
||||||
import net.osmand.plus.onlinerouting.VehicleType;
|
import net.osmand.plus.onlinerouting.VehicleType;
|
||||||
|
import net.osmand.plus.routing.RouteCalculationResult;
|
||||||
|
import net.osmand.plus.routing.RouteDirectionInfo;
|
||||||
|
import net.osmand.router.TurnType;
|
||||||
import net.osmand.util.GeoPolylineParserUtil;
|
import net.osmand.util.GeoPolylineParserUtil;
|
||||||
|
import net.osmand.util.MapUtils;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static net.osmand.util.Algorithms.isEmpty;
|
import static net.osmand.util.Algorithms.isEmpty;
|
||||||
|
import static net.osmand.util.Algorithms.objectEquals;
|
||||||
|
|
||||||
public class OsrmEngine extends OnlineRoutingEngine {
|
public class OsrmEngine extends OnlineRoutingEngine {
|
||||||
|
|
||||||
|
@ -70,17 +78,154 @@ public class OsrmEngine extends OnlineRoutingEngine {
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public OnlineRoutingResponse parseServerResponse(@NonNull String content,
|
public OnlineRoutingResponse parseServerResponse(@NonNull String content,
|
||||||
|
@NonNull OsmandApplication app,
|
||||||
boolean leftSideNavigation) throws JSONException {
|
boolean leftSideNavigation) throws JSONException {
|
||||||
JSONObject obj = new JSONObject(content);
|
JSONObject obj = new JSONObject(content);
|
||||||
String encoded = obj.getJSONArray("routes").getJSONObject(0).getString("geometry");
|
JSONObject routeInfo = obj.getJSONArray("routes").getJSONObject(0);
|
||||||
List<LatLon> points = GeoPolylineParserUtil.parse(encoded, GeoPolylineParserUtil.PRECISION_5);
|
String encodedPoints = routeInfo.getString("geometry");
|
||||||
if (!isEmpty(points)) {
|
List<LatLon> points = GeoPolylineParserUtil.parse(encodedPoints, GeoPolylineParserUtil.PRECISION_5);
|
||||||
|
if (isEmpty(points)) return null;
|
||||||
|
|
||||||
List<Location> route = convertRouteToLocationsList(points);
|
List<Location> route = convertRouteToLocationsList(points);
|
||||||
return new OnlineRoutingResponse(route, null);
|
List<RouteDirectionInfo> directions = new ArrayList<>();
|
||||||
|
int startSearchingId = 0;
|
||||||
|
JSONArray legs = routeInfo.getJSONArray("legs");
|
||||||
|
for (int i = 0; i < legs.length(); i++) {
|
||||||
|
JSONObject leg = legs.getJSONObject(i);
|
||||||
|
if (!leg.has("steps")) continue;
|
||||||
|
|
||||||
|
JSONArray steps = leg.getJSONArray("steps");
|
||||||
|
for (int j = 0; j < steps.length(); j++) {
|
||||||
|
JSONObject instruction = steps.getJSONObject(j);
|
||||||
|
JSONObject maneuver = instruction.getJSONObject("maneuver");
|
||||||
|
String maneuverType = maneuver.getString("type");
|
||||||
|
|
||||||
|
JSONArray location = maneuver.getJSONArray("location");
|
||||||
|
double lon = location.getDouble(0);
|
||||||
|
double lat = location.getDouble(1);
|
||||||
|
Integer routePointOffset = getLocationIndexInList(route, startSearchingId, lat, lon);
|
||||||
|
if (routePointOffset == null) continue;
|
||||||
|
startSearchingId = routePointOffset;
|
||||||
|
|
||||||
|
// in meters
|
||||||
|
int distance = (int) Math.round(instruction.getDouble("distance"));
|
||||||
|
// in seconds
|
||||||
|
int duration = (int) Math.round(instruction.getDouble("duration"));
|
||||||
|
|
||||||
|
float averageSpeed = (float) distance / duration;
|
||||||
|
TurnType turnType = parseTurnType(maneuver, leftSideNavigation);
|
||||||
|
RouteDirectionInfo direction = new RouteDirectionInfo(averageSpeed, turnType);
|
||||||
|
direction.setDistance(distance);
|
||||||
|
|
||||||
|
String streetName = instruction.getString("name");
|
||||||
|
String description = "";
|
||||||
|
if (!objectEquals(maneuverType, "arrive")) {
|
||||||
|
description = RouteCalculationResult.toString(turnType, app, false) + " " + streetName;
|
||||||
|
}
|
||||||
|
description = description.trim();
|
||||||
|
|
||||||
|
direction.setStreetName(streetName);
|
||||||
|
direction.setDescriptionRoute(description);
|
||||||
|
direction.routePointOffset = routePointOffset;
|
||||||
|
directions.add(direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OnlineRoutingResponse(route, directions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Integer getLocationIndexInList(@NonNull List<Location> locations,
|
||||||
|
int startIndex, double lat, double lon) {
|
||||||
|
for (int i = startIndex; i < locations.size(); i++) {
|
||||||
|
Location l = locations.get(i);
|
||||||
|
if (MapUtils.areLatLonEqual(l, lat, lon)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private TurnType parseTurnType(@NonNull JSONObject maneuver,
|
||||||
|
boolean leftSide) throws JSONException {
|
||||||
|
TurnType turnType = null;
|
||||||
|
|
||||||
|
String type = maneuver.getString("type");
|
||||||
|
String modifier = null;
|
||||||
|
if (maneuver.has("modifier")) {
|
||||||
|
modifier = maneuver.getString("modifier");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectEquals(type, "roundabout")
|
||||||
|
|| objectEquals(type, "rotary")
|
||||||
|
|| objectEquals(type, "roundabout turn")) {
|
||||||
|
if (maneuver.has("exit")) {
|
||||||
|
int exit = maneuver.getInt("exit");
|
||||||
|
turnType = TurnType.getExitTurn(exit, 0.0f, leftSide);
|
||||||
|
} else if (modifier != null) {
|
||||||
|
// for simple roundabout turn without "exit" parameter
|
||||||
|
turnType = identifyTurnType(modifier, leftSide);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// for other maneuver types find TurnType
|
||||||
|
// like a basic turn into direction of the modifier
|
||||||
|
if (modifier != null) {
|
||||||
|
turnType = identifyTurnType(modifier, leftSide);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (turnType == null) {
|
||||||
|
turnType = TurnType.straight();
|
||||||
|
}
|
||||||
|
|
||||||
|
int bearingBefore = maneuver.getInt("bearing_before");
|
||||||
|
int bearingAfter = maneuver.getInt("bearing_after");
|
||||||
|
float angle = (float) MapUtils.degreesDiff(bearingAfter, bearingBefore);
|
||||||
|
turnType.setTurnAngle(angle);
|
||||||
|
|
||||||
|
return turnType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private TurnType identifyTurnType(@NonNull String modifier,
|
||||||
|
boolean leftSide) {
|
||||||
|
Integer id = null;
|
||||||
|
switch (modifier) {
|
||||||
|
case "uturn":
|
||||||
|
id = TurnType.TU;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "sharp right":
|
||||||
|
id = TurnType.TSHR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "right":
|
||||||
|
id = TurnType.TR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "slight right":
|
||||||
|
id = TurnType.TSLR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "straight":
|
||||||
|
id = TurnType.C;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "slight left":
|
||||||
|
id = TurnType.TSLL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "left":
|
||||||
|
id = TurnType.TL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "sharp left":
|
||||||
|
id = TurnType.TSHL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return id != null ? TurnType.valueOf(id, leftSide) : null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean parseServerMessage(@NonNull StringBuilder sb,
|
public boolean parseServerMessage(@NonNull StringBuilder sb,
|
||||||
@NonNull String content) throws JSONException {
|
@NonNull String content) throws JSONException {
|
||||||
|
|
|
@ -711,7 +711,7 @@ public class RouteCalculationResult {
|
||||||
if (directions != null && directions.size() > 1) {
|
if (directions != null && directions.size() > 1) {
|
||||||
for (int i = 1; i < directions.size();) {
|
for (int i = 1; i < directions.size();) {
|
||||||
RouteDirectionInfo r = directions.get(i);
|
RouteDirectionInfo r = directions.get(i);
|
||||||
if (r.getTurnType() != null && r.getTurnType().getValue() == TurnType.C) {
|
if (r.getTurnType().getValue() == TurnType.C) {
|
||||||
RouteDirectionInfo prev = directions.get(i - 1);
|
RouteDirectionInfo prev = directions.get(i - 1);
|
||||||
prev.setAverageSpeed((prev.distance + r.distance)
|
prev.setAverageSpeed((prev.distance + r.distance)
|
||||||
/ (prev.distance / prev.getAverageSpeed() + r.distance / r.getAverageSpeed()));
|
/ (prev.distance / prev.getAverageSpeed() + r.distance / r.getAverageSpeed()));
|
||||||
|
|
|
@ -1206,7 +1206,7 @@ public class RouteProvider {
|
||||||
helper.calculateRouteOnline(stringKey, getPathFromParams(params), params.leftSide);
|
helper.calculateRouteOnline(stringKey, getPathFromParams(params), params.leftSide);
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
params.intermediates = null;
|
params.intermediates = null;
|
||||||
return new RouteCalculationResult(response.getRoute(), response.getDirections(), params, null, true);
|
return new RouteCalculationResult(response.getRoute(), response.getDirections(), params, null, false);
|
||||||
} else {
|
} else {
|
||||||
return new RouteCalculationResult("Route is empty");
|
return new RouteCalculationResult("Route is empty");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue