I want to show the driving route between two locations in my android app. I want to draw the route only on top of road segments.
There are several answers on stack overflow itself, and all of them were using the same method. Get the directions from start point to destination using google directions API, and draw a polyline across the points returned. Following are some of the answers which uses this method.
https://stackoverflow.com/a/17007360/1015678
https://stackoverflow.com/a/40563930/1015678
But, problem with above method is, when the roads are not straight, the dawn rote is not always on top of the roads, because directions API only returns points where you need to turn from one road to another (at junctions). It doesn't give point details in the bends of the same road segment. So, when I use above method in an area where the roads have so many bends, the route drawn almost always is not on top of road segments.
I found this answer, which does what I need to do, using the javascript API. In this solution, the drawn route nicely follows the roads, similar to the google maps android app. Does someone know whether this is achievable in an android app?
Google Maps android app can nicely draw a route from one point to another, keeping the route on the roads. Does anyone know how Google Maps is doing this? Is it using any other API which is not publicly exposed?
Indeed, you can draw precise route in Google Maps Android API using results provided by Directions API web service. If you read the documentation for Directions API you will see that response contains information about route legs and steps. Each step has a field polyline
that is described in the documentation as
polyline contains a single points object that holds an encoded polyline representation of the step. This polyline is an approximate (smoothed) path of the step.
So, the main idea to solve your issue is to get response from Directions API, loop through route legs and steps, for each step get encoded polyline and decode it to the list of coordinates. Once done you will have a list of all coordinates that compound the route, not only begin and end point of each step.
For simplicity I recommend using the Java client library for Google Maps Web services:
https://github.com/googlemaps/google-maps-services-java
Using this library you can avoid implementing your own async tasks and decoding function for polylines. Read the documentation to figure out how to add the client library in your project.
In Gradle it should be something similar to
compile 'com.google.maps:google-maps-services:(insert latest version)'
compile 'org.slf4j:slf4j-nop:1.7.25'
I have created a simple example to demonstrate how it works. Have a look at my comments in the code
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));
}
}
Please note that for web service you have to create a separate API key, the API key with Android app restriction won't work with web service.
The result of my example is shown in screenshot
You can also download a complete sample project from
https://github.com/xomena-so/so47492459
Don't forget replace the API key with yours.
I hope this helps!