1

I'm using the Roads Google API passing a list of positions and I want to paint lines in the most probable road driven by a car by these points.

Something is going wrong because some points are not being crossed by the lines and the lines are not using only roads, some of them pass through edifices or the sea with straight lines. I added markers to show the problem.

Is something wrong in the code?

enter image description here

Some sample code to get this error:

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {

private GoogleMap mGoogleMap;
private MapFragment mapFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mapFragment = (MapFragment) getFragmentManager()
            .findFragmentById(R.id.map_fragment);
    mapFragment.getMapAsync(this);
}

@Override
public void onMapReady(GoogleMap googleMap) {
    mGoogleMap = googleMap;

    List<LatLng> sourcePoints = new ArrayList<>();
    sourcePoints.add(new LatLng(39.4321055669415, -0.343169529033198));
    sourcePoints.add(new LatLng(39.4279737815806, -0.334743742804801));
    sourcePoints.add(new LatLng(39.4235262880062, -0.341102620562518));
    sourcePoints.add(new LatLng(39.4216973481355, -0.340624944612178));
    sourcePoints.add(new LatLng(39.4194951574233, -0.335974058847626));
    sourcePoints.add(new LatLng(39.4216760915054, -0.340342003540913));
    sourcePoints.add(new LatLng(39.4235646246302, -0.340901154018858));
    sourcePoints.add(new LatLng(39.4321131753486, -0.342995147300383));

    PolylineOptions polyLineOptions = new PolylineOptions();
    polyLineOptions.addAll(sourcePoints);
    polyLineOptions.width(5);
    polyLineOptions.color(Color.BLUE);
    mGoogleMap.addPolyline(polyLineOptions);

    mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(sourcePoints.get(0), 15));

    List<LatLng> snappedPoints = new ArrayList<>();
    new GetSnappedPointsAsyncTask().execute(sourcePoints, null, snappedPoints);
}


private String buildRequestUrl(List<LatLng> trackPoints) {
    StringBuilder url = new StringBuilder();
    url.append("https://roads.googleapis.com/v1/snapToRoads?path=");

    for (LatLng trackPoint : trackPoints) {
        url.append(String.format("%8.5f", trackPoint.latitude));
        url.append(",");
        url.append(String.format("%8.5f", trackPoint.longitude));
        url.append("|");
    }
    url.delete(url.length() - 1, url.length());
    url.append("&interpolate=true");
    url.append(String.format("&key=%s", <your_Google_Maps_API_key>);

    return url.toString();
}


private class GetSnappedPointsAsyncTask extends AsyncTask<List<LatLng>, Void, List<LatLng>> {

    protected void onPreExecute() {
        super.onPreExecute();
    }

    protected List<LatLng> doInBackground(List<LatLng>... params) {

        List<LatLng> snappedPoints = new ArrayList<>();

        HttpURLConnection connection = null;
        BufferedReader reader = null;

        try {
            URL url = new URL(buildRequestUrl(params[0]));
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.connect();

            InputStream stream = connection.getInputStream();

            reader = new BufferedReader(new InputStreamReader(stream));
            StringBuilder jsonStringBuilder = new StringBuilder();

            StringBuffer buffer = new StringBuffer();
            String line = "";

            while ((line = reader.readLine()) != null) {
                buffer.append(line+"\n");
                jsonStringBuilder.append(line);
                jsonStringBuilder.append("\n");
            }

            JSONObject jsonObject = new JSONObject(jsonStringBuilder.toString());
            JSONArray snappedPointsArr = jsonObject.getJSONArray("snappedPoints");

            for (int i = 0; i < snappedPointsArr.length(); i++) {
                JSONObject snappedPointLocation = ((JSONObject) (snappedPointsArr.get(i))).getJSONObject("location");
                double lattitude = snappedPointLocation.getDouble("latitude");
                double longitude = snappedPointLocation.getDouble("longitude");
                snappedPoints.add(new LatLng(lattitude, longitude));
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return snappedPoints;
    }

    @Override
    protected void onPostExecute(List<LatLng> result) {
        super.onPostExecute(result);

        PolylineOptions polyLineOptions = new PolylineOptions();
        polyLineOptions.addAll(result);
        polyLineOptions.width(5);
        polyLineOptions.color(Color.RED);
        mGoogleMap.addPolyline(polyLineOptions);

        LatLngBounds.Builder builder = new LatLngBounds.Builder();
        builder.include(result.get(0));
        builder.include(result.get(result.size()-1));
        LatLngBounds bounds = builder.build();
        mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 10));

    }
}

}

1 Answer 1

1

It's everything ok with your code: there are not enough points for Snap to Roads. You should use Google Maps Directions API instead of Snap to Roads API to get lacking points: format route request, receive and parse response - you need overview_polyline tag points String value - it's encoded polyline of route part For your source points (blue path on figure):

List<LatLng> sourcePoints = new ArrayList<>();
sourcePoints.add(new LatLng(39.4321055669415, -0.343169529033198));
sourcePoints.add(new LatLng(39.4279737815806, -0.334743742804801));
sourcePoints.add(new LatLng(39.4235262880062, -0.341102620562518));
sourcePoints.add(new LatLng(39.4216973481355, -0.340624944612178));
sourcePoints.add(new LatLng(39.4194951574233, -0.335974058847626));
sourcePoints.add(new LatLng(39.4216760915054, -0.340342003540913));
sourcePoints.add(new LatLng(39.4235646246302, -0.340901154018858));
sourcePoints.add(new LatLng(39.4321131753486, -0.342995147300383));

with Google Maps Directions API implementation like that:

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {

    private static final String TAG = MainActivity.class.getSimpleName();
    private GoogleMap mGoogleMap;
    private MapFragment mapFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mapFragment = (MapFragment) getFragmentManager()
                .findFragmentById(R.id.map_fragment);
        mapFragment.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mGoogleMap = googleMap;

        List<LatLng> sourcePoints = new ArrayList<>();
        sourcePoints.add(new LatLng(39.4321055669415, -0.343169529033198));
        sourcePoints.add(new LatLng(39.4279737815806, -0.334743742804801));
        sourcePoints.add(new LatLng(39.4235262880062, -0.341102620562518));
        sourcePoints.add(new LatLng(39.4216973481355, -0.340624944612178));
        sourcePoints.add(new LatLng(39.4194951574233, -0.335974058847626));
        sourcePoints.add(new LatLng(39.4216760915054, -0.340342003540913));
        sourcePoints.add(new LatLng(39.4235646246302, -0.340901154018858));
        sourcePoints.add(new LatLng(39.4321131753486, -0.342995147300383));

        PolylineOptions polyLineOptions = new PolylineOptions();
        polyLineOptions.addAll(sourcePoints);
        polyLineOptions.width(5);
        polyLineOptions.color(Color.BLUE);
        mGoogleMap.addPolyline(polyLineOptions);

        mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(sourcePoints.get(0), 15));

        List<LatLng> directionsPoints = new ArrayList<>();
        new GetDirectionPointsAsyncTask().execute(sourcePoints, null, directionsPoints);
    }

    private String buildDirectionsUrl(List<LatLng> trackPoints) {

        if (trackPoints.size() < 2) {
            return null;
        }

        final LatLng origin = trackPoints.get(0);
        final LatLng dest = trackPoints.get(trackPoints.size() - 1);

        StringBuilder url = new StringBuilder();
        url.append("https://maps.googleapis.com/maps/api/directions/json?");
        url.append(String.format("origin=%8.5f,%8.5f", origin.latitude, origin.longitude));
        url.append(String.format("&destination=%8.5f,%8.5f", dest.latitude, dest.longitude));

        // add waypoints, if they exists
        if (trackPoints.size() > 2) {
            url.append("&waypoints=");
            LatLng wayPoint;
            for (int ixWaypoint = 1; ixWaypoint < trackPoints.size() - 2; ixWaypoint++) {
                wayPoint = trackPoints.get(ixWaypoint);
                url.append(String.format("%8.5f,%8.5f|", wayPoint.latitude, wayPoint.longitude));
            }
            url.delete(url.length() - 1, url.length());
        }

        url.append(String.format("&key=%s", getResources().getString(R.string.google_maps_key)));

        return url.toString();
    }


    private class GetDirectionPointsAsyncTask extends AsyncTask<List<LatLng>, Void, List<LatLng>> {

        protected void onPreExecute() {
            super.onPreExecute();
        }

        protected List<LatLng> doInBackground(List<LatLng>... params) {

            List<LatLng> routePoints = new ArrayList<>();

            HttpURLConnection connection = null;
            BufferedReader reader = null;

            try {
                URL url = new URL(buildDirectionsUrl(params[0]));
                connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.connect();

                int responseCode = connection.getResponseCode();
                InputStream stream = connection.getInputStream();

                reader = new BufferedReader(new InputStreamReader(stream));
                StringBuilder jsonStringBuilder = new StringBuilder();

                StringBuffer buffer = new StringBuffer();
                String line = "";

                while ((line = reader.readLine()) != null) {
                    buffer.append(line+"\n");
                    jsonStringBuilder.append(line);
                    jsonStringBuilder.append("\n");
                }

                JSONObject jsonRoot = new JSONObject(jsonStringBuilder.toString());
                JSONArray jsonRoutes = jsonRoot.getJSONArray("routes");

                if (jsonRoutes.length() < 1) {
                    return null;
                }

                JSONObject jsonRoute = jsonRoutes.getJSONObject(0);
                JSONObject overviewPolyline = jsonRoute.getJSONObject("overview_polyline");
                String overviewPolylineEncodedPoints = overviewPolyline.getString("points");
                routePoints = decodePoly(overviewPolylineEncodedPoints);

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            } finally {
                if (connection != null) {
                    connection.disconnect();
                }
                try {
                    if (reader != null) {
                        reader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            return routePoints;
        }

        @Override
        protected void onPostExecute(List<LatLng> result) {
            super.onPostExecute(result);

            PolylineOptions polyLineOptions = new PolylineOptions();
            polyLineOptions.addAll(result);
            polyLineOptions.width(5);
            polyLineOptions.color(Color.RED);
            mGoogleMap.addPolyline(polyLineOptions);

            LatLngBounds.Builder builder = new LatLngBounds.Builder();
            builder.include(result.get(0));
            builder.include(result.get(result.size()-1));
            LatLngBounds bounds = builder.build();
            mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 10));

        }
    }

    //
    // Method to decode polyline points
    // Courtesy : http://jeffreysambells.com/2010/05/27/decoding-polylines-from-google-maps-direction-api-with-java
    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;
    }
}

you get something like that (red path on figure):

Route

NB! Don't forget to allow Directions API on Google APIs Console

More details about Direction API here and in many online tutorials/examples.

Not the answer you're looking for? Browse other questions tagged or ask your own question.