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?
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.