在手机进行室外线路规划,需要采用第三方的线路规划算法,在osmbonuspack中线路规划算法,需要实现RoadManager抽象类:
package org.osmdroid.bonuspack.routing;
import java.util.ArrayList;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.overlay.PathOverlay;
import android.content.Context;
import android.graphics.Paint;
/**
* Generic class to get a route between a start and a destination point,
* going through a list of waypoints.
* @see MapQuestRoadManager
* @see GoogleRoadManager
* @see OSRMRoadManager
*
* @author M.Kergall
*/
public abstract class RoadManager {
protected String mOptions;
public abstract Road getRoad(ArrayList<GeoPoint> waypoints);
public RoadManager(){
mOptions = "";
}
/**
* Add an option that will be used in the route request.
* Note that some options are set in the request in all cases.
* @param requestOption see provider documentation.
* Just one example: "routeType=bicycle" for MapQuest; "mode=bicycling" for Google.
*/
public void addRequestOption(String requestOption){
mOptions += "&" + requestOption;
}
protected String geoPointAsString(GeoPoint p){
StringBuffer result = new StringBuffer();
double d = p.getLatitudeE6()*1E-6;
result.append(Double.toString(d));
d = p.getLongitudeE6()*1E-6;
result.append("," + Double.toString(d));
return result.toString();
}
public static PathOverlay buildRoadOverlay(Road road, Paint paint, Context context){
PathOverlay roadOverlay = new PathOverlay(0, context);
roadOverlay.setPaint(paint);
if (road != null) {
ArrayList<GeoPoint> polyline = road.mRouteHigh;
for (GeoPoint p:polyline){
roadOverlay.addPoint(p);
}
}
return roadOverlay;
}
/**
* Builds an overlay for the road shape with a default (and nice!) color.
* @return route shape overlay
*/
public static PathOverlay buildRoadOverlay(Road road, Context context){
Paint paint = new Paint();
paint.setColor(0x800000FF);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
return buildRoadOverlay(road, paint, context);
}
}
三种方案:
第一种方案:google线路规划方案:GoogleRoadManager
package org.osmdroid.bonuspack.routing;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Locale;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.osmdroid.bonuspack.utils.BonusPackHelper;
import org.osmdroid.bonuspack.utils.HttpConnection;
import org.osmdroid.bonuspack.utils.PolylineEncoder;
import org.osmdroid.util.BoundingBoxE6;
import org.osmdroid.util.GeoPoint;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
/** 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 GoogleRoadManager extends RoadManager {
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
*/
protected String getUrl(ArrayList<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 road
*/
@Override public Road getRoad(ArrayList<GeoPoint> waypoints) {
String url = getUrl(waypoints);
Log.d(BonusPackHelper.LOG_TAG, "GoogleRoadManager.getRoad:"+url);
Road road = null;
HttpConnection connection = new HttpConnection();
connection.doGet(url);
InputStream stream = connection.getStream();
if (stream != null)
road = getRoadXML(stream);
connection.close();
if (road == null || road.mRouteHigh.size()==0){
//Create default road:
road = new Road(waypoints);
} else {
//finalize road data update:
for (RoadLeg leg : road.mLegs){
road.mDuration += leg.mDuration;
road.mLength += leg.mLength;
}
road.mStatus = Road.STATUS_OK;
}
Log.d(BonusPackHelper.LOG_TAG, "GoogleRoadManager.getRoad - finished");
return road;
}
protected Road getRoadXML(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.mRoad;
}
}
第二种算法采用MapQuest 算法的API
使用的详细说明,参数的详细讲解,参考以下文档
http://open.mapquestapi.com/directions/
It uses MapQuest open, public and free API, based on OpenStreetMap data.
package org.osmdroid.bonuspack.routing;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.osmdroid.bonuspack.utils.BonusPackHelper;
import org.osmdroid.bonuspack.utils.HttpConnection;
import org.osmdroid.bonuspack.utils.PolylineEncoder;
import org.osmdroid.util.BoundingBoxE6;
import org.osmdroid.util.GeoPoint;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
/** class to get a route between a start and a destination point,
* going through a list of waypoints.
*
* It uses MapQuest open, public and free API, based on OpenStreetMap data. <br>
* See http://open.mapquestapi.com/guidance
* @return a "Road" object.
*
* @author M.Kergall
*/
public class MapQuestRoadManager extends RoadManager {
static final String MAPQUEST_GUIDANCE_SERVICE = "http://open.mapquestapi.com/guidance/v0/route?";
/**
* Build the URL to MapQuest service returning a route in XML format
* @param waypoints: array of waypoints, as [lat, lng], from start point to end point.
*/
protected String getUrl(ArrayList<GeoPoint> waypoints) {
StringBuffer urlString = new StringBuffer(MAPQUEST_GUIDANCE_SERVICE);
urlString.append("from=");
GeoPoint p = waypoints.get(0);
urlString.append(geoPointAsString(p));
for (int i=1; i<waypoints.size(); i++){
p = waypoints.get(i);
urlString.append("&to="+geoPointAsString(p));
}
urlString.append("&outFormat=xml");
urlString.append("&shapeFormat=cmp"); //encoded polyline, much faster
urlString.append("&narrativeType=text"); //or "none"
//Locale locale = Locale.getDefault();
//urlString.append("&locale="+locale.getLanguage()+"_"+locale.getCountry());
urlString.append("&unit=k&fishbone=false");
//urlString.append("&generalizeAfter=500" /*+&generalize=2"*/);
//500 points max, 2 meters tolerance
//Warning: MapQuest Open API doc is sometimes WRONG:
//- use unit, not units
//- use fishbone, not enableFishbone
//- locale (fr_FR, en_US) is supported but not documented.
//- generalize and generalizeAfter are not properly implemented
urlString.append(mOptions);
return urlString.toString();
}
/**
* @param waypoints: list of GeoPoints. Must have at least 2 entries, start and end points.
* @return the road
*/
@Override public Road getRoad(ArrayList<GeoPoint> waypoints) {
String url = getUrl(waypoints);
Log.d(BonusPackHelper.LOG_TAG, "MapQuestRoadManager.getRoute:"+url);
Road road = null;
HttpConnection connection = new HttpConnection();
connection.doGet(url);
InputStream stream = connection.getStream();
if (stream != null)
road = getRoadXML(stream, waypoints);
if (road == null || road.mRouteHigh.size()==0){
//Create default road:
road = new Road(waypoints);
}
connection.close();
Log.d(BonusPackHelper.LOG_TAG, "MapQuestRoadManager.getRoute - finished");
return road;
}
/**
* XML implementation
* @param is: input stream to parse
* @return the road
*/
protected Road getRoadXML(InputStream is, ArrayList<GeoPoint> waypoints) {
XMLHandler handler = new XMLHandler();
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();
}
Road road = handler.mRoad;
if (road != null && road.mRouteHigh.size()>0){
road.mNodes = finalizeNodes(road.mNodes, handler.mLinks, road.mRouteHigh);
road.buildLegs(waypoints);
road.mStatus = Road.STATUS_OK;
}
return road;
}
protected ArrayList<RoadNode> finalizeNodes(ArrayList<RoadNode> mNodes,
ArrayList<RoadLink> mLinks, ArrayList<GeoPoint> polyline){
int n = mNodes.size();
if (n == 0)
return mNodes;
ArrayList<RoadNode> newNodes = new ArrayList<RoadNode>(n);
RoadNode lastNode = null;
for (int i=1; i<n-1; i++){ //1, n-1 => first and last MapQuest nodes are irrelevant.
RoadNode node = mNodes.get(i);
RoadLink link = mLinks.get(node.mNextRoadLink);
if (lastNode!=null && (node.mInstructions == null || node.mManeuverType == 0)){
//this node is irrelevant, don't keep it,
//but update values of last node:
lastNode.mLength += link.mLength;
lastNode.mDuration += (node.mDuration + link.mDuration);
} else {
node.mLength = link.mLength;
node.mDuration += link.mDuration;
int locationIndex = link.mShapeIndex;
node.mLocation = polyline.get(locationIndex);
newNodes.add(node);
lastNode = node;
}
}
//switch to the new array of nodes:
return newNodes;
}
}
第三种采用OSRM
package org.osmdroid.bonuspack.routing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.osmdroid.bonuspack.utils.BonusPackHelper;
import org.osmdroid.bonuspack.utils.HttpConnection;
import org.osmdroid.bonuspack.utils.PolylineEncoder;
import org.osmdroid.util.BoundingBoxE6;
import org.osmdroid.util.GeoPoint;
import android.util.Log;
/** get a route between a start and a destination point.
* It uses OSRM, a free open source routing service based on OpenSteetMap data. <br>
* See https://github.com/DennisOSRM/Project-OSRM/wiki/Server-api<br>
*
* It requests by default the OSRM demo site.
* Use setService() to request an other (for instance your own) OSRM service. <br>
* TODO: improve internationalization of instructions
* @author M.Kergall
*/
public class OSRMRoadManager extends RoadManager {
static final String OSRM_SERVICE = "http://router.project-osrm.org/viaroute?";
//Note that the result of OSRM is quite close to Cloudmade NavEngine format:
//http://developers.cloudmade.com/wiki/navengine/JSON_format
protected String mServiceUrl;
protected String mUserAgent;
/** mapping from OSRM directions to MapQuest maneuver IDs: */
static final HashMap<String, Integer> MANEUVERS;
static {
MANEUVERS = new HashMap<String, Integer>();
MANEUVERS.put("0", 0); //No instruction
MANEUVERS.put("1", 1); //Continue
MANEUVERS.put("2", 6); //Slight right
MANEUVERS.put("3", 7); //Right
MANEUVERS.put("4", 8); //Sharp right
MANEUVERS.put("5", 12); //U-turn
MANEUVERS.put("6", 5); //Sharp left
MANEUVERS.put("7", 4); //Left
MANEUVERS.put("8", 3); //Slight left
MANEUVERS.put("9", 24); //Arrived (at waypoint)
//MANEUVERS.put("10", 0); //"Head" => used by OSRM as the start node
MANEUVERS.put("11-1", 27); //Round-about, 1st exit
MANEUVERS.put("11-2", 28); //2nd exit, etc ...
MANEUVERS.put("11-3", 29);
MANEUVERS.put("11-4", 30);
MANEUVERS.put("11-5", 31);
MANEUVERS.put("11-6", 32);
MANEUVERS.put("11-7", 33);
MANEUVERS.put("11-8", 34); //Round-about, 8th exit
MANEUVERS.put("15", 24); //Arrived
}
//From: Project-OSRM-Web / WebContent / localization / OSRM.Locale.en.js
// driving directions
// %s: road name
// %d: direction => removed
// <*>: will only be printed when there actually is a road name
static final HashMap<String, Object> DIRECTIONS;
static {
DIRECTIONS = new HashMap<String, Object>();
HashMap<String, String> directions;
directions = new HashMap<String, String>();
DIRECTIONS.put("en", directions);
directions.put("0", "Unknown instruction< on %s>");
directions.put("1","Continue< on %s>");
directions.put("2","Turn slight right< on %s>");
directions.put("3","Turn right< on %s>");
directions.put("4","Turn sharp right< on %s>");
directions.put("5","U-Turn< on %s>");
directions.put("6","Turn sharp left< on %s>");
directions.put("7","Turn left< on %s>");
directions.put("8","Turn slight left< on %s>");
directions.put("9","You have reached a waypoint of your trip");
directions.put("10","<Go on %s>");
directions.put("11-1","Enter roundabout and leave at first exit< on %s>");
directions.put("11-2","Enter roundabout and leave at second exit< on %s>");
directions.put("11-3","Enter roundabout and leave at third exit< on %s>");
directions.put("11-4","Enter roundabout and leave at fourth exit< on %s>");
directions.put("11-5","Enter roundabout and leave at fifth exit< on %s>");
directions.put("11-6","Enter roundabout and leave at sixth exit< on %s>");
directions.put("11-7","Enter roundabout and leave at seventh exit< on %s>");
directions.put("11-8","Enter roundabout and leave at eighth exit< on %s>");
directions.put("11-9","Enter roundabout and leave at nineth exit< on %s>");
directions.put("15","You have reached your destination");
directions = new HashMap<String, String>();
DIRECTIONS.put("fr", directions);
directions.put("0", "Instruction inconnue< sur %s>");
directions.put("1","Continuez< sur %s>");
directions.put("2","Tournez légèrement à droite< sur %s>");
directions.put("3","Tournez à droite< sur %s>");
directions.put("4","Tournez fortement à droite< sur %s>");
directions.put("5","Faites demi-tour< sur %s>");
directions.put("6","Tournez fortement à gauche< sur %s>");
directions.put("7","Tournez à gauche< sur %s>");
directions.put("8","Tournez légèrement à gauche< sur %s>");
directions.put("9","Vous êtes arrivé à une étape de votre voyage");
directions.put("10","<Prenez %s>");
directions.put("11-1","Au rond-point, prenez la première sortie< sur %s>");
directions.put("11-2","Au rond-point, prenez la deuxième sortie< sur %s>");
directions.put("11-3","Au rond-point, prenez la troisième sortie< sur %s>");
directions.put("11-4","Au rond-point, prenez la quatrième sortie< sur %s>");
directions.put("11-5","Au rond-point, prenez la cinquième sortie< sur %s>");
directions.put("11-6","Au rond-point, prenez la sixième sortie< sur %s>");
directions.put("11-7","Au rond-point, prenez la septième sortie< sur %s>");
directions.put("11-8","Au rond-point, prenez la huitième sortie< sur %s>");
directions.put("11-9","Au rond-point, prenez la neuvième sortie< sur %s>");
directions.put("15","Vous êtes arrivé");
directions = new HashMap<String, String>();
DIRECTIONS.put("pl", directions);
directions.put("0", "Nieznana instrukcja<w %s>");
directions.put("1","Kontynuuj jazdę<na %s>");
directions.put("2","Skręć lekko w prawo<w %s>");
directions.put("3","Skręć w prawo<w %s>");
directions.put("4","Skręć ostro w prawo<w %s>");
directions.put("5","Zawróć<na %s>");
directions.put("6","Skręć ostro w lewo<w %s>");
directions.put("7","Skręć w lewo<w %s>");
directions.put("8","Skręć lekko w lewo<w %s>");
directions.put("9","Dotarłeś do punktu pośredniego");
directions.put("10","<Jedź %s>");
directions.put("11-1","Wjedź na rondo i opuść je pierwszym zjazdem<w %s>");
directions.put("11-2","Wjedź na rondo i opuść je drugim zjazdem<w %s>");
directions.put("11-3","Wjedź na rondo i opuść je trzecim zjazdem<w %s>");
directions.put("11-4","Wjedź na rondo i opuść je czwartym zjazdem<w %s>");
directions.put("11-5","Wjedź na rondo i opuść je piątym zjazdem<w %s>");
directions.put("11-6","Wjedź na rondo i opuść je szóstym zjazdem<w %s>");
directions.put("11-7","Wjedź na rondo i opuść je siódmym zjazdem<w %s>");
directions.put("11-8","Wjedź na rondo i opuść je ósmym zjazdem<w %s>");
directions.put("11-9","Wjedź na rondo i opuść je dziewiątym zjazdem<w %s>");
directions.put("15","Dotarłeś do celu podróży");
}
public OSRMRoadManager(){
super();
mServiceUrl = OSRM_SERVICE;
mUserAgent = BonusPackHelper.DEFAULT_USER_AGENT; //set user agent to the default one.
}
/** allows to request on an other site than OSRM demo site */
public void setService(String serviceUrl){
mServiceUrl = serviceUrl;
}
/** allows to send to OSRM service a user agent specific to the app,
* instead of the default user agent of OSMBonusPack lib.
*/
public void setUserAgent(String userAgent){
mUserAgent = userAgent;
}
protected String getUrl(ArrayList<GeoPoint> waypoints){
StringBuffer urlString = new StringBuffer(mServiceUrl);
for (int i=0; i<waypoints.size(); i++){
GeoPoint p = waypoints.get(i);
urlString.append("&loc="+geoPointAsString(p));
}
urlString.append(mOptions);
return urlString.toString();
}
@Override public Road getRoad(ArrayList<GeoPoint> waypoints) {
String url = getUrl(waypoints);
Log.d(BonusPackHelper.LOG_TAG, "OSRMRoadManager.getRoad:"+url);
//String jString = BonusPackHelper.requestStringFromUrl(url);
HttpConnection connection = new HttpConnection();
connection.setUserAgent(mUserAgent);
connection.doGet(url);
String jString = connection.getContentAsString();
connection.close();
if (jString == null) {
Log.e(BonusPackHelper.LOG_TAG, "OSRMRoadManager::getRoad: request failed.");
return new Road(waypoints);
}
Locale l = Locale.getDefault();
HashMap<String, String> directions = (HashMap<String, String>)DIRECTIONS.get(l.getLanguage());
if (directions == null)
directions = (HashMap<String, String>)DIRECTIONS.get("en");
Road road = new Road();
try {
JSONObject jObject = new JSONObject(jString);
String route_geometry = jObject.getString("route_geometry");
road.mRouteHigh = PolylineEncoder.decode(route_geometry, 10);
JSONArray jInstructions = jObject.getJSONArray("route_instructions");
int n = jInstructions.length();
RoadNode lastNode = null;
for (int i=0; i<n; i++){
JSONArray jInstruction = jInstructions.getJSONArray(i);
RoadNode node = new RoadNode();
int positionIndex = jInstruction.getInt(3);
node.mLocation = road.mRouteHigh.get(positionIndex);
node.mLength = jInstruction.getInt(2)/1000.0;
node.mDuration = jInstruction.getInt(4); //Segment duration in seconds.
String direction = jInstruction.getString(0);
String roadName = jInstruction.getString(1);
if (lastNode!=null && "1".equals(direction) && "".equals(roadName)){
//node "Continue" with no road name is useless, don't add it
lastNode.mLength += node.mLength;
lastNode.mDuration += node.mDuration;
} else {
node.mManeuverType = getManeuverCode(direction);
node.mInstructions = buildInstructions(direction, roadName, directions);
//Log.d(BonusPackHelper.LOG_TAG, direction+"=>"+node.mManeuverType+"; "+node.mInstructions);
road.mNodes.add(node);
lastNode = node;
}
}
JSONObject jSummary = jObject.getJSONObject("route_summary");
road.mLength = jSummary.getInt("total_distance")/1000.0;
road.mDuration = jSummary.getInt("total_time");
} catch (JSONException e) {
e.printStackTrace();
return new Road(waypoints);
}
if (road.mRouteHigh.size()==0){
//Create default road:
road = new Road(waypoints);
} else {
road.buildLegs(waypoints);
BoundingBoxE6 bb = BoundingBoxE6.fromGeoPoints(road.mRouteHigh);
//Correcting osmdroid bug #359:
road.mBoundingBox = new BoundingBoxE6(
bb.getLatSouthE6(), bb.getLonWestE6(), bb.getLatNorthE6(), bb.getLonEastE6());
road.mStatus = Road.STATUS_OK;
}
Log.d(BonusPackHelper.LOG_TAG, "OSRMRoadManager.getRoad - finished");
return road;
}
protected int getManeuverCode(String direction){
Integer code = MANEUVERS.get(direction);
if (code != null)
return code;
else
return 0;
}
protected String buildInstructions(String direction, String roadName,
HashMap<String, String> directions){
if (directions == null)
return null;
direction = directions.get(direction);
if (direction == null)
return null;
String instructions = null;
if (roadName.equals(""))
//remove "<*>"
instructions = direction.replaceFirst("<[^>]*>", "");
else {
direction = direction.replace('<', ' ');
direction = direction.replace('>', ' ');
instructions = String.format(direction, roadName);
}
return instructions;
}
}
源代码下载路径: