package org.osmdroid.bonuspack.routing;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.osmdroid.bonuspack.utils.BonusPackHelper;
import org.osmdroid.bonuspack.utils.PolylineEncoder;
import org.osmdroid.util.BoundingBox;
import org.osmdroid.util.GeoPoint;
import java.util.ArrayList;
import java.util.HashMap;
/** get a route between a start and a destination point, going through a list of waypoints.
* It uses GraphHopper, an open source routing service based on OpenSteetMap data. <br>
*
* It requests by default the GraphHopper demo site.
* Use setService() to request another (for instance your own) GraphHopper-compliant service. <br>
*
* @see <a href="https://github.com/graphhopper/web-api/blob/master/docs-routing.md">GraphHopper</a>
* @author M.Kergall
*/
public class GraphHopperRoadManager extends RoadManager {
protected static final String SERVICE = "https://graphhopper.com/api/1/route?";
public static final int STATUS_NO_ROUTE = Road.STATUS_TECHNICAL_ISSUE+1;
protected String mServiceUrl;
protected String mKey;
protected boolean mWithElevation;
protected boolean mAlternateAvailable;
/** mapping from GraphHopper directions to MapQuest maneuver IDs: */
static final HashMap<Integer, Integer> MANEUVERS;
static {
MANEUVERS = new HashMap<Integer, Integer>();
MANEUVERS.put(0, 1); //Continue
MANEUVERS.put(1, 6); //Slight right
MANEUVERS.put(2, 7); //Right
MANEUVERS.put(3, 8); //Sharp right
MANEUVERS.put(-3, 5); //Sharp left
MANEUVERS.put(-2, 4); //Left
MANEUVERS.put(-1, 3); //Slight left
MANEUVERS.put(4, 24); //Arrived
MANEUVERS.put(5, 24); //Arrived at waypoint
}
/**
* @param apiKey GraphHopper API key, mandatory to use the public GraphHopper service.
* @see <a href="http://graphhopper.com/#enterprise">GraphHopper</a> to obtain an API key.
*/
public GraphHopperRoadManager(String apiKey, boolean alternateAvailable) {
super();
mServiceUrl = SERVICE;
mKey = apiKey;
mWithElevation = false;
mAlternateAvailable = alternateAvailable;
}
/** allows to request on an other site than GraphHopper demo site */
public void setService(String serviceUrl){
mServiceUrl = serviceUrl;
}
/** set if altitude of every route point should be requested or not. Default is false. */
public void setElevation(boolean withElevation){
mWithElevation = withElevation;
}
protected String getUrl(ArrayList<GeoPoint> waypoints, boolean getAlternate) {
StringBuilder urlString = new StringBuilder(mServiceUrl);
urlString.append("key="+mKey);
for (int i=0; i<waypoints.size(); i++){
GeoPoint p = waypoints.get(i);
urlString.append("&point="+geoPointAsString(p));
}
//urlString.append("&instructions=true"); already set by default
urlString.append("&elevation="+(mWithElevation?"true":"false"));
if (getAlternate && mAlternateAvailable)
urlString.append("&ch.disable=true&algorithm=alternative_route");
urlString.append(mOptions);
return urlString.toString();
}
protected Road[] defaultRoad(ArrayList<GeoPoint> waypoints) {
Road[] roads = new Road[1];
roads[0] = new Road(waypoints);
return roads;
}
public Road[] getRoads(ArrayList<GeoPoint> waypoints, boolean getAlternate) {
String url = getUrl(waypoints, getAlternate);
Log.d(BonusPackHelper.LOG_TAG, "GraphHopper.getRoads:" + url);
String jString = BonusPackHelper.requestStringFromUrl(url);
if (jString == null) {
return defaultRoad(waypoints);
}
try {
JSONObject jRoot = new JSONObject(jString);
JSONArray jPaths = jRoot.optJSONArray("paths");
if (jPaths == null || jPaths.length() == 0){
return defaultRoad(waypoints);
/*
road = new Road(waypoints);
road.mStatus = STATUS_NO_ROUTE;
return road;
*/
}
Road[] roads = new Road[jPaths.length()];
for (int r = 0; r < jPaths.length(); r++) {
JSONObject jPath = jPaths.getJSONObject(r);
String route_geometry = jPath.getString("points");
Road road = new Road();
roads[r] = road;
road.mRouteHigh = PolylineEncoder.decode(route_geometry, 10, mWithElevation);
JSONArray jInstructions = jPath.getJSONArray("instructions");
int n = jInstructions.length();
for (int i = 0; i < n; i++) {
JSONObject jInstruction = jInstructions.getJSONObject(i);
RoadNode node = new RoadNode();
JSONArray jInterval = jInstruction.getJSONArray("interval");
int positionIndex = jInterval.getInt(0);
node.mLocation = road.mRouteHigh.get(positionIndex);
node.mLength = jInstruction.getDouble("distance") / 1000.0;
node.mDuration = jInstruction.getInt("time") / 1000.0; //Segment duration in seconds.
int direction = jInstruction.getInt("sign");
node.mManeuverType = getManeuverCode(direction);
node.mInstructions = jInstruction.getString("text");
road.mNodes.add(node);
}
road.mLength = jPath.getDouble("distance") / 1000.0;
road.mDuration = jPath.getInt("time") / 1000.0;
JSONArray jBBox = jPath.getJSONArray("bbox");
road.mBoundingBox = new BoundingBox(jBBox.getDouble(3), jBBox.getDouble(2),
jBBox.getDouble(1), jBBox.getDouble(0));
road.mStatus = Road.STATUS_OK;
road.buildLegs(waypoints);
Log.d(BonusPackHelper.LOG_TAG, "GraphHopper.getRoads - finished");
}
return roads;
} catch (JSONException e) {
e.printStackTrace();
return defaultRoad(waypoints);
}
}
@Override public Road[] getRoads(ArrayList<GeoPoint> waypoints) {
return getRoads(waypoints, true);
}
@Override
public Road getRoad(ArrayList<GeoPoint> waypoints) {
Road[] roads = getRoads(waypoints, false);
return roads[0];
}
protected int getManeuverCode(int direction){
Integer code = MANEUVERS.get(direction);
if (code != null)
return code;
else
return 0;
}
}