android google-maps google-maps-android-api-2

android - ¿Cómo dibujo una ruta, a lo largo de una carretera existente, entre dos puntos?



google-maps google-maps-android-api-2 (3)

Quiero mostrar la ruta de conducción entre dos ubicaciones en mi aplicación de Android. Quiero dibujar la ruta solo en la parte superior de los segmentos de la carretera.

Hay varias respuestas sobre el desbordamiento de la pila, y todas ellas estaban usando el mismo método. Obtenga las indicaciones desde el punto de inicio hasta el destino utilizando la API de indicaciones de Google y dibuje una polilínea a través de los puntos devueltos. Las siguientes son algunas de las respuestas que utilizan este método.

https://stackoverflow.com/a/17007360/1015678

https://stackoverflow.com/a/40563930/1015678

Pero, el problema con el método anterior es que, cuando las carreteras no son rectas, la memorización del amanecer no siempre está en la parte superior de las carreteras, porque las direcciones API solo devuelven puntos en los que debe girar de una carretera a otra (en los cruces). No da detalles de puntos en las curvas del mismo segmento de carretera. Entonces, cuando uso el método anterior en un área donde las carreteras tienen muchas curvas, la ruta trazada casi siempre no está en la parte superior de los segmentos de la carretera.

Encontré this respuesta, que hace lo que tengo que hacer, usando la API de JavaScript. En esta solución, la ruta dibujada sigue muy bien los caminos, de manera similar a la aplicación de Android google maps. ¿Alguien sabe si esto se puede lograr en una aplicación de Android?

La aplicación de Android Google Maps puede dibujar una ruta de un punto a otro, manteniendo la ruta en las carreteras. ¿Alguien sabe cómo Google Maps está haciendo esto? ¿Utiliza alguna otra API que no esté expuesta públicamente?


De hecho, puede dibujar una ruta precisa en la API de Android de Google Maps utilizando los resultados proporcionados por el servicio web de API de Direcciones. Si lee la documentación de la API de indicaciones , verá que la respuesta contiene información sobre pasos y pasos de la ruta. Cada paso tiene una polyline campo que se describe en la documentación como

La polilínea contiene un objeto de puntos únicos que contiene una representación polilínea codificada del paso. Esta polilínea es una ruta aproximada (suavizada) del paso.

Por lo tanto, la idea principal para resolver su problema es obtener una respuesta de Instrucciones API, recorrer las etapas y pasos de la ruta, para cada paso obtener una polilínea codificada y decodificarla en la lista de coordenadas. Una vez hecho esto, tendrá una lista de todas las coordenadas que componen la ruta, no solo el punto inicial y final de cada paso.

Para simplificar, recomiendo usar la biblioteca de cliente Java para los servicios web de Google Maps:

https://github.com/googlemaps/google-maps-services-java

Con esta biblioteca puede evitar implementar sus propias tareas asíncronas y la función de decodificación para polilíneas. Lea la documentación para descubrir cómo agregar la biblioteca del cliente en su proyecto.

En Gradle debería ser algo similar a

compile ''com.google.maps:google-maps-services:(insert latest version)'' compile ''org.slf4j:slf4j-nop:1.7.25''

He creado un ejemplo simple para demostrar cómo funciona. Mira mis comentarios en el código

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)); } }

Tenga en cuenta que para el servicio web debe crear una clave API separada, la clave API con restricción de la aplicación de Android no funcionará con el servicio web.

El resultado de mi ejemplo se muestra en la captura de pantalla

También puede descargar un proyecto de muestra completo desde

https://github.com/xomena-so/so47492459

No olvides reemplazar la clave API con la tuya.

¡Espero que esto ayude!


Habilite Direction API desde Google Console. Reemplace API_KEY en la clase 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); }

Uso en actividad o fragmento

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();


Puede usar esta library , y es simple, verifique el ejemplo de uso :

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); }

Y no olvide agregar la key usando el generador del ejemplo, si recibe advertencias sobre el acceso sin clave ( debe tener una cuenta de facturación para usarla con la clave)