在手机进行室外线路规划,需要采用第三方的线路规划算法,在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; } }
源代码下载路径: