Search code examples
androidgoogle-mapsgoogle-maps-api-3polylinegoogle-polyline

Find out the sections where polylines overlap GoogleMap


I want to display several polylines on a GoogleMap and some of them could overlap like in the picture below. (It is possible they don't overlap perfectly).

enter image description here

My goal is to obtain a list of polylines that go over the same roads as the initial set of polylines but with the constraint that they do not overlap.

For the previous case, I could end up with this:

enter image description here

Some ideas on how I can achieve this?


Solution

  • Easiest way (in case you have not so many polylines) is to use Google Maps Roads API part Snap to Road which

    returns the best-fit road geometry for a given set of GPS coordinates. This service takes up to 100 GPS points collected along a route, and returns a similar set of data with the points snapped to the most likely roads the vehicle was traveling along.

    like in this example:

    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(<COORDINATES_OF_POLYLINE>));
            ...
            sourcePoints.add(new LatLng(<COORDINATES_OF_POLYLINE>));
    
            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));
    
            }
        }
    
    }
    

    (for this you need to add Google Maps Roads API support for your project in Google Cloud Console)

    After you process all of the "initial" polylines with Snap To Road all of them should overlaps perfectly and you can use Z-Index property of Polyline to manage Z-order of polylines.

    Other way is to detect overlapping of polylines "manually". In that case you should:

    1. decide how to determine "main" polyline (longest or having largest amount of points or something else) that should be shown on top of others;
    2. for each point of "non-main" polyline check is it on "main" polyline or not (you can use isLocationOnPath() method of PolyUtil library) and choose the color for the segment depending on check result: if point on the "main" path (within a specified tolerance in meters) you should not to show segment of "non-main" polyline or show it with selected color otherwise.