1

I'm trying to draw a smooth polyline between two locations that are some distance apart (say over 100 miles) on a Google Map in Android. I've been following this guide and also this guide in terms of utilising the Directions and Snap to Roads API, yet due to the limit of 100 coordinates from the Snap to Roads API, it appears it is virtually impossible to draw a smooth polyline from one location to another that follows the smooth contours of the road.

I've managed to extract all coordinates of the directions to plot a polyline using the returned overview_points and have decoded this using the decode method from the PolyUtil API, yet the polyline drawn on the map is not snapped to the road for the vast majority of the time. Instead, I've tried using the Snap to Roads API and set a limit of 100 coordinates (maximum GPS points allowed) that all seem to be very accurately snapped to the road from destination A to B (only covering some distance between the two locations if far apart).

Basically, is there something I am missing entirely, or is it a case of coming up with some resolution to spread the 100 coordinates allocation of the Snap to Roads API using the GPS points retrieved from the overview_points from the Directions API, i.e. plot a coordinate every XXX meters.

Here is the bulk of the code I've implemented through a Volley request minus the Snap to Roads request, which the later is fairly straightforward to implement:

StringRequest stringRequest = new StringRequest(Request.Method.GET, 
"https://maps.googleapis.com/maps/api/directions/json?
origin=START_LOCATION_HERE&destination=END_LOCATION_HERE&key=API_KEY_HERE",
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {

                    JSONObject directions;
                    try {
                        directions = new JSONObject(response);

                        JSONArray routes = directions.getJSONArray("routes");

                        mCoordinates = new ArrayList<>();
                        for (int i = 0; i < routes.length(); i++) {
                            JSONObject routesObject = routes.getJSONObject(i);

                            JSONObject overviewPolyline = routesObject.getJSONObject("overview_polyline");
                            String points = overviewPolyline.getString("points");

                            List<LatLng> coordinates = new ArrayList<>();
                            coordinates.addAll(PolyUtil.decode(points));

                            PolylineOptions routeCoordinates = new PolylineOptions();
                            for (LatLng latLng : coordinates) {
                                routeCoordinates.add(new LatLng(latLng.latitude, latLng.longitude));
                            }
                            routeCoordinates.width(5);
                            routeCoordinates.color(Color.BLUE);

                            Polyline route = mGoogleMap.addPolyline(routeCoordinates);
                            for (LatLng latLng : coordinates) {
                                mGoogleMap.addMarker(new MarkerOptions().position(new LatLng(latLng.latitude, latLng.longitude)));
                            }

                        }

                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            // TODO handle error
        }

    });
Richard Ansell
  • 916
  • 1
  • 12
  • 28
  • Why are you doing this? `String formattedResponse = points.replaceAll("'\\'", "'\'");` – Daniel Nugent Sep 28 '17 at 22:33
  • Hi Daniel, as per this link https://developers.google.com/maps/documentation/utilities/polylinealgorithm: 'Note that the backslash is interpreted as an escape character within string literals. Any output of this utility should convert backslash characters to double-backslashes within string literals'. I tried a few combinations and this was one of the only ways to get it to work unless I'm completely misreading this. – Richard Ansell Sep 28 '17 at 22:37
  • Sorry, my mistake I've removed this line now and it seems to work without it. However, the problem still remains in terms of the coordinates not following the contours of the road. – Richard Ansell Sep 28 '17 at 22:46

2 Answers2

6

Okay, if anyone experiences this issue or similar, I've managed to fix this and it seems the way to correctly plot a polyline on a map that follows the smooth contours of the road is to use each individual polyline > point encoded string within each steps object. It also seems I misread the documentation that clearly states the polyline encoded string in each steps object provides an approximate smoothed path of each step, which I'd imagine others may also have missed.

Basically, the overview_polyline encoded string will only provide an overview of the route and thus not provide a smooth path between locations. It may be perfect for routes that consist of largely straight lines (not great in the UK obviously), so each individual polyline encoded string is what is needed. I've just tested this and it works perfectly, without the need at all for the Snap to Roads API.

My altered code to fix the problem in my use case is as follows:

    StringRequest stringRequest = new StringRequest(Request.Method.GET, "https://maps.googleapis.com/maps/api/directions/json?origin=START_LOCATION&destination=END_LOCATION&key=API_KEY",
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {

                    JSONObject directions;
                    try {
                        directions = new JSONObject(response);

                        JSONArray routes = directions.getJSONArray("routes");

                        mCoordinates = new ArrayList<>();
                        for (int a = 0; a < routes.length(); a++) {
                            JSONObject routesObject = routes.getJSONObject(a);

                            JSONArray legsArray = routesObject.getJSONArray("legs");
                            for (int b = 0; b < legsArray.length(); b++) {
                                JSONObject legsObject = legsArray.getJSONObject(b);
                                JSONArray steps = legsObject.getJSONArray("steps");
                                for (int c = 0; c < steps.length(); c++) {
                                    JSONObject stepsObject = steps.getJSONObject(c);
                                    JSONObject polyLineObject = stepsObject.getJSONObject("polyline");
                                    mCoordinates.addAll(PolyUtil.decode(polyLineObject.getString("points")));
                                }
                            }

                            PolylineOptions routeCoordinates = new PolylineOptions();
                            for (LatLng latLng : mCoordinates) {
                                routeCoordinates.add(new LatLng(latLng.latitude, latLng.longitude));
                            }
                            routeCoordinates.width(5);
                            routeCoordinates.color(Color.BLUE);

                            Polyline route  = mGoogleMap.addPolyline(routeCoordinates);

                        }

                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            // TODO handle error
        }

    });
Richard Ansell
  • 916
  • 1
  • 12
  • 28
0

The encoded points from the directions api are already "snapped" to roads, so no need to use any additional API.

Using this code to parse/decode the points in the route:

List<LatLng> pointsList = null;
JSONArray routeArray = obj.getJSONArray("routes");
JSONObject routes = routeArray.getJSONObject(0);
JSONObject overviewPolylines = routes.getJSONObject("overview_polyline");
String encodedString = overviewPolylines.getString("points");
pointsList = PolyUtil.decode(encodedString);

Then drawing the Polyline:

PolylineOptions options = new PolylineOptions().width(5).color(Color.MAGENTA).geodesic(true);
for (int i = 0; i < pointsList.size(); i++) {
    LatLng point = pointsList.get(i);
    options.add(point);
}
line = mMap.addPolyline(options);

This is the result:

enter image description here

Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
  • Hi Daniel, thanks again for your input but I've just tried the code you suggested and the snap to roads issue still exists. I will attach a photo to my original post to give you an example using your code to illustrate the same problem I've been facing. – Richard Ansell Sep 28 '17 at 23:09
  • I can't seem to attach the image above, although here is a link to illustrate the problem I'm still facing: https://imgur.com/a/IM0bk. It seems to be when you zoom out from the polylines as from afar, they seem to be okay. It's just I've seen other examples whereby even close up, they are closely attached to the road. – Richard Ansell Sep 28 '17 at 23:16
  • @RichardAnsell Can you give me the start lat/lon and end lat/lon that you're using? I can try it to see if I get the same results. – Daniel Nugent Sep 28 '17 at 23:19
  • Sure thing. The start lat is 51.4615543 lng is -2.1196921. End lat is 53.4807512 lng is -2.2425604. – Richard Ansell Sep 28 '17 at 23:22
  • 1
    @RichardAnsell Hmm, you're right, it's not snapping to the roads correctly in some sections. Looks like a limitation of the directions api, it seems that their data for England is not as complete as their data for the bay area. – Daniel Nugent Sep 28 '17 at 23:33
  • 1
    Thought so and thank god you're seeing this also. It's frustrating as I honestly thought I've been doing something completely wrong. At least this way I know I can use what we've got in terms of the current API. I did try implementing a method to work within the 100 GPS points and attempt to use the Snap to Roads API, but that's going to take some more time to work on. Really appreciate your help either way Daniel, thank you. – Richard Ansell Sep 28 '17 at 23:37