Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if point is in polygon with Google Maps API in Android

I am using the Google Maps API on Android to create a puzzle. This link contains the data I used to draw African countries: World countries coordinates.

When the user clicks on the map, a test is done to check whether it was in the right country or not.

  • point inside right country: the right country is colored in green

  • point inside another known country: the current country is colored in red

The code below iterates the list of African countries (a country may contain multiple polygons) to find the one that contains the clicked point and compare it with the right country.

mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
    @Override
    public void onMapClick(LatLng latLng) {

        if(isPointInPolygon(latLng,answerPolygon)) {
            // right answer
            Toast.makeText(MapsActivity.this, "point inside the right polygon", Toast.LENGTH_SHORT).show();
            Polygon p = mMap.addPolygon(new PolygonOptions().addAll(answerPolygon));
            p.setStrokeWidth(p.getStrokeWidth()/5);
            p.setFillColor(0x7F00FF00);
        }
        else {
            // wrong answer
            // search current polygon
            // color current polygon in red
            if (colorPolygonInRed(latLng)){
                Polygon p = mMap.addPolygon(new PolygonOptions().addAll(answerPolygon));
                p.setStrokeWidth(p.getStrokeWidth()/5);
                p.setFillColor(0x7F00FF00);
                Toast.makeText(MapsActivity.this, "point in known polygons", Toast.LENGTH_SHORT).show();
            }
            else {
                Toast.makeText(MapsActivity.this, "point in unknown polygons", Toast.LENGTH_SHORT).show();
            }
        }
    }
});

The function isPointInPolygon() works with only one polygon on the map because it's based on geometry (ray intersect). It doesn't work in my case. For example, when I click inside Chad the country (the blue spot in this picture), Egypt is getting colored with red (my list is sorted alphabetically, so Egypt is the first one on the right of the clicked point that verifies the ray intersect condition).

public boolean rayCastIntersect(LatLng tap, LatLng vertA, LatLng vertB) {
    double aY = vertA.latitude;
    double bY = vertB.latitude;
    double aX = vertA.longitude;
    double bX = vertB.longitude;
    double pY = tap.latitude;
    double pX = tap.longitude;

    if ( (aY>pY && bY>pY) || (aY<pY && bY<pY) || (aX<pX && bX<pX) ) {
        return false; // a and b can't both be above or below pt.y, and a or b must be east of pt.x
    }

    double m = (aY-bY) / (aX-bX);               // Rise over run
    double bee = (-aX) * m + aY;                // y = mx + b
    double x = (pY - bee) / m;                  // algebra is neat!

    return x > pX;
}
public boolean colorPolygonInRed(LatLng point){
    for (Country country:countryList){
        for (ArrayList<LatLng> polygon : country.getCoordinates()){
            if(isPointInPolygon(point,polygon)) {
                Polygon p = mMap.addPolygon(new PolygonOptions().addAll(polygon));
                p.setStrokeWidth(p.getStrokeWidth()/5);
                p.setFillColor(0x7FE00808);
                return true;
            }
        }
    }
    return false;
}

What's the right way to get the polygon which was clicked from my list?

like image 666
PhiloJunkie Avatar asked Dec 17 '16 21:12

PhiloJunkie


2 Answers

You can use the PolyUtil.containsLocation method from the Google Maps Android API Utility Library. From the documentation:

public static boolean containsLocation(LatLng point, java.util.List polygon, boolean geodesic)

Computes whether the given point lies inside the specified polygon. The polygon is always considered closed, regardless of whether the last point equals the first or not. Inside is defined as not containing the South Pole -- the South Pole is always outside. The polygon is formed of great circle segments if geodesic is true, and of rhumb (loxodromic) segments otherwise.

like image 78
antonio Avatar answered Nov 01 '22 11:11

antonio


A bit later response, but for future searches, you can also check if a location is inside the current visible map area by using:

// Kotlin code
var location = LatLng(-27.6016488, -48.5137219)    
val isInside = mGoogleMap.projection.visibleRegion.latLngBounds.contains(location)

Documentation link: contains (LatLng point)

like image 28
Lenoir Zamboni Avatar answered Nov 01 '22 13:11

Lenoir Zamboni