我想在我的Android应用中显示两个位置之间的行车路线。我想在路段之上绘制路线。
堆栈溢出本身有几个答案,所有这些都使用相同的方法。使用Google Directions API获取从起点到目的地的路线,并在返回的点上绘制折线。以下是使用此方法的一些答案。
https://stackoverflow.com/a/17007360/1015678
https://stackoverflow.com/a/40563930/1015678
但是,上述方法的问题是,当道路不直时,黎明死记硬背并不总是在道路的顶部,因为方向API只返回您需要从一条道路转向另一条道路的点(在交叉路口) 。它没有在同一道路段的弯道中给出点细节。因此,当我在道路有很多弯道的区域使用上述方法时,几乎总是画出的路线不在路段之上。
我找到this回答,这是我需要做的,使用javascript API。在这个解决方案中,绘制的路线很好地遵循道路,类似于谷歌地图Android应用程序。有人知道这是否可以在Android应用程序中实现?
谷歌地图Android应用程序可以很好地绘制从一个点到另一个点的路线,保持路线在道路上。有谁知道谷歌地图是如何做到这一点的?它是否使用任何其他未公开的API?
答案 0 :(得分:29)
实际上,您可以使用Directions API网络服务提供的结果在Google Maps Android API中绘制精确路线。如果您阅读Directions API的文档,您会看到该响应包含有关路径支路和步骤的信息。每个步骤都有一个字段polyline
,在文档中描述为
折线包含单个点对象,该对象包含步骤的编码折线表示。此折线是步骤的近似(平滑)路径。
因此,解决您的问题的主要想法是从Directions API获取响应,循环路径支路和步骤,为每个步骤获取encoded polyline并将其解码为坐标列表。完成后,您将获得所有坐标的列表,这些坐标不仅包括每个步骤的开始和结束点。
为简单起见,我建议将Java客户端库用于Google Maps Web服务:
https://github.com/googlemaps/google-maps-services-java
使用此库可以避免为折线实现自己的异步任务和解码功能。阅读文档以了解如何在项目中添加客户端库。
在Gradle中它应该类似于
compile 'com.google.maps:google-maps-services:(insert latest version)'
compile 'org.slf4j:slf4j-nop:1.7.25'
我创建了一个简单的例子来演示它是如何工作的。看看我在代码中的评论
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
private String TAG = "so47492459";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
LatLng barcelona = new LatLng(41.385064,2.173403);
mMap.addMarker(new MarkerOptions().position(barcelona).title("Marker in Barcelona"));
LatLng madrid = new LatLng(40.416775,-3.70379);
mMap.addMarker(new MarkerOptions().position(madrid).title("Marker in Madrid"));
LatLng zaragoza = new LatLng(41.648823,-0.889085);
//Define list to get all latlng for the route
List<LatLng> path = new ArrayList();
//Execute Directions API request
GeoApiContext context = new GeoApiContext.Builder()
.apiKey("YOUR_API_KEY")
.build();
DirectionsApiRequest req = DirectionsApi.getDirections(context, "41.385064,2.173403", "40.416775,-3.70379");
try {
DirectionsResult res = req.await();
//Loop through legs and steps to get encoded polylines of each step
if (res.routes != null && res.routes.length > 0) {
DirectionsRoute route = res.routes[0];
if (route.legs !=null) {
for(int i=0; i<route.legs.length; i++) {
DirectionsLeg leg = route.legs[i];
if (leg.steps != null) {
for (int j=0; j<leg.steps.length;j++){
DirectionsStep step = leg.steps[j];
if (step.steps != null && step.steps.length >0) {
for (int k=0; k<step.steps.length;k++){
DirectionsStep step1 = step.steps[k];
EncodedPolyline points1 = step1.polyline;
if (points1 != null) {
//Decode polyline and add points to list of route coordinates
List<com.google.maps.model.LatLng> coords1 = points1.decodePath();
for (com.google.maps.model.LatLng coord1 : coords1) {
path.add(new LatLng(coord1.lat, coord1.lng));
}
}
}
} else {
EncodedPolyline points = step.polyline;
if (points != null) {
//Decode polyline and add points to list of route coordinates
List<com.google.maps.model.LatLng> coords = points.decodePath();
for (com.google.maps.model.LatLng coord : coords) {
path.add(new LatLng(coord.lat, coord.lng));
}
}
}
}
}
}
}
}
} catch(Exception ex) {
Log.e(TAG, ex.getLocalizedMessage());
}
//Draw the polyline
if (path.size() > 0) {
PolylineOptions opts = new PolylineOptions().addAll(path).color(Color.BLUE).width(5);
mMap.addPolyline(opts);
}
mMap.getUiSettings().setZoomControlsEnabled(true);
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(zaragoza, 6));
}
}
请注意,对于网络服务,您必须创建单独的API密钥,具有Android应用限制的API密钥不会使用网络服务。
我的示例的结果显示在屏幕截图
中您也可以从
下载完整的示例项目https://github.com/xomena-so/so47492459
别忘了用你的API替换API密钥。
我希望这有帮助!
答案 1 :(得分:1)
您可以使用此library,它很简单,请检查example of usage:
Routing routing = new Routing.Builder()
.travelMode(AbstractRouting.TravelMode.DRIVING)
.withListener(this)
.alternativeRoutes(true)
.waypoints(start, end)
.build();
routing.execute();
@Override
public void onRoutingSuccess(List<Route> route, int shortestRouteIndex)
{
progressDialog.dismiss();
CameraUpdate center = CameraUpdateFactory.newLatLng(start);
CameraUpdate zoom = CameraUpdateFactory.zoomTo(16);
map.moveCamera(center);
if(polylines.size()>0) {
for (Polyline poly : polylines) {
poly.remove();
}
}
polylines = new ArrayList<>();
//add route(s) to the map.
for (int i = 0; i <route.size(); i++) {
//In case of more than 5 alternative routes
int colorIndex = i % COLORS.length;
PolylineOptions polyOptions = new PolylineOptions();
polyOptions.color(getResources().getColor(COLORS[colorIndex]));
polyOptions.width(10 + i * 3);
polyOptions.addAll(route.get(i).getPoints());
Polyline polyline = map.addPolyline(polyOptions);
polylines.add(polyline);
Toast.makeText(getApplicationContext(),"Route "+ (i+1) +": distance - "+ route.get(i).getDistanceValue()+": duration - "+ route.get(i).getDurationValue(),Toast.LENGTH_SHORT).show();
}
// Start marker
MarkerOptions options = new MarkerOptions();
options.position(start);
options.icon(BitmapDescriptorFactory.fromResource(R.drawable.start_blue));
map.addMarker(options);
// End marker
options = new MarkerOptions();
options.position(end);
options.icon(BitmapDescriptorFactory.fromResource(R.drawable.end_green));
map.addMarker(options);
}
如果您收到有关无密钥访问的警告(you should have billing account与密钥一起使用),请不要忘记使用示例中的构建器添加key
答案 2 :(得分:1)
对我来说,我使用OSM获取了geojson的路线,然后使用google maps utils在Google Map上绘制了路线。
首先
<style>
div {
outline: solid 1px red;
}
div:focus {
outline: solid 1px blue;
}
</style>
<div
id="close"
tabindex="0"
onfocusin="console.log('in div')">close
</div>
<button
onblur="console.log('in onblur'); document.getElementById('close').focus()">click me
</button>
有关更多信息,请检查此Google Maps Android GeoJson Utility。
快乐编码
答案 3 :(得分:0)
从Google控制台启用Direction API。
替换API_KEY
类中的GetPathFromLocation.java
import android.graphics.Color;
import android.os.AsyncTask;
import android.util.Log;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.PolylineOptions;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class GetPathFromLocation extends AsyncTask<String, Void, PolylineOptions> {
private String TAG = "GetPathFromLocation";
private String API_KEY = "Place_Your_API_Key";
private LatLng source, destination;
private DirectionPointListener resultCallback;
public GetPathFromLocation(LatLng source, LatLng destination, DirectionPointListener resultCallback) {
this.source = source;
this.destination = destination;
this.resultCallback = resultCallback;
}
public String getUrl(LatLng origin, LatLng dest) {
String str_origin = "origin=" + origin.latitude + "," + origin.longitude;
String str_dest = "destination=" + dest.latitude + "," + dest.longitude;
String sensor = "sensor=false";
String parameters = str_origin + "&" + str_dest + "&" + sensor;
String output = "json";
String url = "https://maps.googleapis.com/maps/api/directions/" + output + "?" + parameters + "&key=" + API_KEY;
return url;
}
@Override
protected PolylineOptions doInBackground(String... url) {
String data;
try {
InputStream inputStream = null;
HttpURLConnection connection = null;
try {
URL directionUrl = new URL(getUrl(source, destination));
connection = (HttpURLConnection) directionUrl.openConnection();
connection.connect();
inputStream = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer stringBuffer = new StringBuffer();
String line = "";
while ((line = bufferedReader.readLine()) != null) {
stringBuffer.append(line);
}
data = stringBuffer.toString();
bufferedReader.close();
} catch (Exception e) {
Log.e(TAG, "Exception : " + e.toString());
return null;
} finally {
inputStream.close();
connection.disconnect();
}
Log.e(TAG, "Background Task data : " + data);
JSONObject jsonObject;
List<List<HashMap<String, String>>> routes = null;
try {
jsonObject = new JSONObject(data);
// Starts parsing data
DirectionHelper helper = new DirectionHelper();
routes = helper.parse(jsonObject);
Log.e(TAG, "Executing Routes : "/*, routes.toString()*/);
ArrayList<LatLng> points;
PolylineOptions lineOptions = null;
// Traversing through all the routes
for (int i = 0; i < routes.size(); i++) {
points = new ArrayList<>();
lineOptions = new PolylineOptions();
// Fetching i-th route
List<HashMap<String, String>> path = routes.get(i);
// Fetching all the points in i-th route
for (int j = 0; j < path.size(); j++) {
HashMap<String, String> point = path.get(j);
double lat = Double.parseDouble(point.get("lat"));
double lng = Double.parseDouble(point.get("lng"));
LatLng position = new LatLng(lat, lng);
points.add(position);
}
// Adding all the points in the route to LineOptions
lineOptions.addAll(points);
lineOptions.width(10);
lineOptions.color(Color.BLUE);
Log.e(TAG, "PolylineOptions Decoded");
}
// Drawing polyline in the Google Map for the i-th route
if (lineOptions != null) {
return lineOptions;
} else {
return null;
}
} catch (Exception e) {
Log.e(TAG, "Exception in Executing Routes : " + e.toString());
return null;
}
} catch (Exception e) {
Log.e(TAG, "Background Task Exception : " + e.toString());
return null;
}
}
@Override
protected void onPostExecute(PolylineOptions polylineOptions) {
super.onPostExecute(polylineOptions);
if (resultCallback != null && polylineOptions != null)
resultCallback.onPath(polylineOptions);
}
}
DirectionHelper.java
import com.google.android.gms.maps.model.LatLng;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class DirectionHelper {
public List<List<HashMap<String, String>>> parse(JSONObject jObject) {
List<List<HashMap<String, String>>> routes = new ArrayList<>();
JSONArray jRoutes;
JSONArray jLegs;
JSONArray jSteps;
try {
jRoutes = jObject.getJSONArray("routes");
/** Traversing all routes */
for (int i = 0; i < jRoutes.length(); i++) {
jLegs = ((JSONObject) jRoutes.get(i)).getJSONArray("legs");
List path = new ArrayList<>();
/** Traversing all legs */
for (int j = 0; j < jLegs.length(); j++) {
jSteps = ((JSONObject) jLegs.get(j)).getJSONArray("steps");
/** Traversing all steps */
for (int k = 0; k < jSteps.length(); k++) {
String polyline = "";
polyline = (String) ((JSONObject) ((JSONObject) jSteps.get(k)).get("polyline")).get("points");
List<LatLng> list = decodePoly(polyline);
/** Traversing all points */
for (int l = 0; l < list.size(); l++) {
HashMap<String, String> hm = new HashMap<>();
hm.put("lat", Double.toString((list.get(l)).latitude));
hm.put("lng", Double.toString((list.get(l)).longitude));
path.add(hm);
}
}
routes.add(path);
}
}
} catch (JSONException e) {
e.printStackTrace();
} catch (Exception e) {
}
return routes;
}
//Method to decode polyline points
private List<LatLng> decodePoly(String encoded) {
List<LatLng> poly = new ArrayList<>();
int index = 0, len = encoded.length();
int lat = 0, lng = 0;
while (index < len) {
int b, shift = 0, result = 0;
do {
b = encoded.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lat += dlat;
shift = 0;
result = 0;
do {
b = encoded.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lng += dlng;
LatLng p = new LatLng((((double) lat / 1E5)),
(((double) lng / 1E5)));
poly.add(p);
}
return poly;
}
}
DirectionPointListener.java
import com.google.android.gms.maps.model.PolylineOptions;
public interface DirectionPointListener {
public void onPath(PolylineOptions polyLine);
}
用于活动或片段
LatLng source = new LatLng(xx.xxxx, yy.yyyy);
LatLng destination = new LatLng(xx.xxxx, yy.yyyy);
new GetPathFromLocation(source, destination, new DirectionPointListener() {
@Override
public void onPath(PolylineOptions polyLine) {
yourMap.addPolyline(polyLine);
}
}).execute();