236 lines
8.1 KiB
Java
236 lines
8.1 KiB
Java
package org.osmdroid.routing.provider;
|
|
|
|
import org.oscim.core.BoundingBox;
|
|
import org.oscim.core.GeoPoint;
|
|
import org.osmdroid.routing.Route;
|
|
import org.osmdroid.routing.RouteLeg;
|
|
import org.osmdroid.routing.RouteNode;
|
|
import org.osmdroid.routing.RouteProvider;
|
|
import org.osmdroid.utils.HttpConnection;
|
|
import org.osmdroid.utils.PolylineEncoder;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.xml.sax.Attributes;
|
|
import org.xml.sax.SAXException;
|
|
import org.xml.sax.helpers.DefaultHandler;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
import javax.xml.parsers.SAXParser;
|
|
import javax.xml.parsers.SAXParserFactory;
|
|
|
|
/**
|
|
* class to get a route between a start and a destination point, going through a
|
|
* list of waypoints. <br>
|
|
* https://developers.google.com/maps/documentation/directions/<br>
|
|
* Note that displaying a route provided by Google on a non-Google map (like
|
|
* OSM) is not allowed by Google T&C.
|
|
*
|
|
* @author M.Kergall
|
|
*/
|
|
public class GoogleRouteProvider extends RouteProvider {
|
|
|
|
static final Logger log = LoggerFactory.getLogger(GoogleRouteProvider.class);
|
|
|
|
static final String GOOGLE_DIRECTIONS_SERVICE = "http://maps.googleapis.com/maps/api/directions/xml?";
|
|
|
|
/**
|
|
* Build the URL to Google Directions service returning a route in XML
|
|
* format
|
|
*
|
|
* @param waypoints ...
|
|
* @return ...
|
|
*/
|
|
protected String getUrl(List<GeoPoint> waypoints) {
|
|
StringBuffer urlString = new StringBuffer(GOOGLE_DIRECTIONS_SERVICE);
|
|
urlString.append("origin=");
|
|
GeoPoint p = waypoints.get(0);
|
|
urlString.append(geoPointAsString(p));
|
|
urlString.append("&destination=");
|
|
int destinationIndex = waypoints.size() - 1;
|
|
p = waypoints.get(destinationIndex);
|
|
urlString.append(geoPointAsString(p));
|
|
|
|
for (int i = 1; i < destinationIndex; i++) {
|
|
if (i == 1)
|
|
urlString.append("&waypoints=");
|
|
else
|
|
urlString.append("%7C"); // the pipe (|), url-encoded
|
|
p = waypoints.get(i);
|
|
urlString.append(geoPointAsString(p));
|
|
}
|
|
urlString.append("&units=metric&sensor=false");
|
|
Locale locale = Locale.getDefault();
|
|
urlString.append("&language=" + locale.getLanguage());
|
|
urlString.append(mOptions);
|
|
return urlString.toString();
|
|
}
|
|
|
|
/**
|
|
* @param waypoints : list of GeoPoints. Must have at least 2 entries, start and
|
|
* end points.
|
|
* @return the route
|
|
*/
|
|
@Override
|
|
public Route getRoute(List<GeoPoint> waypoints) {
|
|
String url = getUrl(waypoints);
|
|
log.debug("GoogleRouteManager.getRoute:" + url);
|
|
Route route = null;
|
|
HttpConnection connection = new HttpConnection();
|
|
connection.doGet(url);
|
|
InputStream stream = connection.getStream();
|
|
if (stream != null)
|
|
route = getRouteXML(stream);
|
|
connection.close();
|
|
if (route == null || route.routeHigh.size() == 0) {
|
|
//Create default route:
|
|
route = new Route(waypoints);
|
|
} else {
|
|
//finalize route data update:
|
|
for (RouteLeg leg : route.legs) {
|
|
route.duration += leg.duration;
|
|
route.length += leg.length;
|
|
}
|
|
route.status = Route.STATUS_OK;
|
|
}
|
|
log.debug("GoogleRouteManager.getRoute - finished");
|
|
return route;
|
|
}
|
|
|
|
protected Route getRouteXML(InputStream is) {
|
|
GoogleDirectionsHandler handler = new GoogleDirectionsHandler();
|
|
try {
|
|
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
|
parser.parse(is, handler);
|
|
} catch (ParserConfigurationException e) {
|
|
e.printStackTrace();
|
|
} catch (SAXException e) {
|
|
e.printStackTrace();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
return handler.mRoute;
|
|
}
|
|
|
|
}
|
|
|
|
class GoogleDirectionsHandler extends DefaultHandler {
|
|
Route mRoute;
|
|
RouteLeg mLeg;
|
|
RouteNode mNode;
|
|
boolean isPolyline, isOverviewPolyline, isLeg, isStep, isDuration, isDistance, isBB;
|
|
int mValue;
|
|
double mLat, mLng;
|
|
double mNorth, mWest, mSouth, mEast;
|
|
private String mString;
|
|
|
|
public GoogleDirectionsHandler() {
|
|
isOverviewPolyline = isBB = isPolyline = isLeg = isStep = isDuration = isDistance = false;
|
|
mRoute = new Route();
|
|
}
|
|
|
|
@Override
|
|
public void startElement(String uri, String localName, String name,
|
|
Attributes attributes) {
|
|
if (localName.equals("polyline")) {
|
|
isPolyline = true;
|
|
} else if (localName.equals("overview_polyline")) {
|
|
isOverviewPolyline = true;
|
|
} else if (localName.equals("leg")) {
|
|
mLeg = new RouteLeg();
|
|
isLeg = true;
|
|
} else if (localName.equals("step")) {
|
|
mNode = new RouteNode();
|
|
isStep = true;
|
|
} else if (localName.equals("duration")) {
|
|
isDuration = true;
|
|
} else if (localName.equals("distance")) {
|
|
isDistance = true;
|
|
} else if (localName.equals("bounds")) {
|
|
isBB = true;
|
|
}
|
|
mString = new String();
|
|
}
|
|
|
|
/**
|
|
* Overrides org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
|
|
*/
|
|
public
|
|
@Override
|
|
void characters(char[] ch, int start, int length) {
|
|
String chars = new String(ch, start, length);
|
|
mString = mString.concat(chars);
|
|
}
|
|
|
|
@Override
|
|
public void endElement(String uri, String localName, String name) {
|
|
if (localName.equals("points")) {
|
|
if (isPolyline) {
|
|
//detailed piece of route for the step, to add:
|
|
ArrayList<GeoPoint> polyLine = PolylineEncoder.decode(mString, 10);
|
|
mRoute.routeHigh.addAll(polyLine);
|
|
} else if (isOverviewPolyline) {
|
|
//low-def polyline for the whole route:
|
|
mRoute.setRouteLow(PolylineEncoder.decode(mString, 10));
|
|
}
|
|
} else if (localName.equals("polyline")) {
|
|
isPolyline = false;
|
|
} else if (localName.equals("overview_polyline")) {
|
|
isOverviewPolyline = false;
|
|
} else if (localName.equals("value")) {
|
|
mValue = Integer.parseInt(mString);
|
|
} else if (localName.equals("duration")) {
|
|
if (isStep)
|
|
mNode.duration = mValue;
|
|
else
|
|
mLeg.duration = mValue;
|
|
isDuration = false;
|
|
} else if (localName.equals("distance")) {
|
|
if (isStep)
|
|
mNode.length = mValue / 1000.0;
|
|
else
|
|
mLeg.length = mValue / 1000.0;
|
|
isDistance = false;
|
|
} else if (localName.equals("html_instructions")) {
|
|
if (isStep) {
|
|
mString = mString.replaceAll("<[^>]*>", " "); //remove everything in <...>
|
|
mString = mString.replaceAll(" ", " ");
|
|
mNode.instructions = mString;
|
|
//log.debug(mString);
|
|
}
|
|
} else if (localName.equals("start_location")) {
|
|
if (isStep)
|
|
mNode.location = new GeoPoint(mLat, mLng);
|
|
} else if (localName.equals("step")) {
|
|
mRoute.nodes.add(mNode);
|
|
isStep = false;
|
|
} else if (localName.equals("leg")) {
|
|
mRoute.legs.add(mLeg);
|
|
isLeg = false;
|
|
} else if (localName.equals("lat")) {
|
|
mLat = Double.parseDouble(mString);
|
|
} else if (localName.equals("lng")) {
|
|
mLng = Double.parseDouble(mString);
|
|
} else if (localName.equals("northeast")) {
|
|
if (isBB) {
|
|
mNorth = mLat;
|
|
mEast = mLng;
|
|
}
|
|
} else if (localName.equals("southwest")) {
|
|
if (isBB) {
|
|
mSouth = mLat;
|
|
mWest = mLng;
|
|
}
|
|
} else if (localName.equals("bounds")) {
|
|
mRoute.boundingBox = new BoundingBox(mNorth, mEast, mSouth, mWest);
|
|
isBB = false;
|
|
}
|
|
}
|
|
|
|
}
|