Detección de polígono táctil Google Map API V2

Estoy tratando de encontrar la mejor manera de hacerlo, tengo un mapa con un Polygon dibujado en él. Dado que no parece que Google Maps API V2 tenga una detección táctil en un polígono. Me preguntaba si es posible detectar si el punto de contacto está dentro del Polígono. Si es así, entonces, ¿cómo ?, mi principal objective es delinear un estado en un mapa y cuando el usuario toque ese estado, mostrará más detalles dentro de una vista personalizada. Por el momento, puedo capturar MapOnClick del mapa, pero cuando el usuario toca dentro del Polygon , quiero que se polygon.getID() el polygon.getID() en el Toast . Soy un novato, así que me disculpo si no soy lo suficientemente claro.

 googleMap.setOnMapClickListener(new OnMapClickListener() { public void onMapClick(LatLng point) { boolean checkPoly = true; Toast.makeText(MainActivity.this,"The Location is outside of the Area", Toast.LENGTH_LONG).show(); } }); } } catch (Exception e) { Log.e("APP","Failed", e); } 

Ok esto es lo que tengo semi-trabajo hasta el momento

  private 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 > bY) { aX = vertB.longitude; aY = vertB.latitude; bX = vertA.longitude; bX = vertA.latitude; } System.out.println("aY: "+aY+" aX : "+aX); System.out.println("bY: "+bY+" bX : "+bX); if (pX < 0) pX += 360; if (aX < 0) aX += 360; if (bX  bY || pY  Math.max(aX, bX))) return false; if (pX  pX; } 

}

El problema que tengo es que el toque es verdadero a la izquierda de cada polígono hasta que alcanza otro. ¿Qué problema hay en mi algoritmo que podría causar este problema? Cualquier ayuda sería apreciada.

El problema que estás tratando de resolver es la prueba Point in Polygon .

Para ayudar a visualizar el concepto de Ray Casting:

Dibuja un polígono en una hoja de papel. Luego, comenzando en cualquier punto al azar, dibuje una línea recta a la derecha de la página. Si su línea se cruzó con su polígono un número impar de veces, esto significa que su punto de partida estaba dentro del Polígono.

Entonces, ¿cómo haces eso en el código?

Su polígono está compuesto por una lista de vértices: vértices ArrayList vertices . Necesita ver cada Line Segment individualmente, y ver si su Ray cruza

 private boolean isPointInPolygon(Geopoint tap, ArrayList vertices) { int intersectCount = 0; for(int j=0; jpY && bY>pY) || (aY pX; } 

La biblioteca de soporte de Google Maps ahora tiene un método estático que hace esta verificación por usted:

 PolyUtil.containsLocation(LatLng point, Listpolygon, boolean geodesic); 

Aunque los documentos no lo mencionan explícitamente en la guía, el método está ahí

Documentos de la Biblioteca de soporte de mapas

Con el lanzamiento de Google Play Services 8.4.0 , la API de Maps ha incluido soporte para agregar OnPolygonClickListener a OnPolygonClickListener . Ambos polígonos , polilíneas y superposiciones admiten eventos similares.

Solo necesita llamar a GoogleMap.setOnPolygonClickListener(OnPolygonClickListener listener) para configurarlo, y de forma correspondiente para los otros oyentes ( setOnPolylineClickListener , & c):

 map.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() { @Override public void onPolygonClick(Polygon polygon) { // Handle click ... } }); 

Aunque es un poco tarde, resuelve este caso de uso bastante bien.

Aquí hay un ejemplo completo de trabajo para saber si un toque ocurrió en un polígono. Algunas de las respuestas son más complicadas de lo que deben ser. Esta solución usa los “android-maps-utils”

 // compile 'com.google.maps.android:android-maps-utils:0.3.4' private ArrayList polygonList = new ArrayList<>(); private void addMyPolygons() { PolygonOptions options = new PolygonOptions(); // TODO: make your polygon's however you want Polygon polygon = googleMap.addPolygon(options); polygonList.add(polygon); } @Override public void onMapClick(LatLng point) { boolean contains = false; for (Polygon p : polygonList) { contains = PolyUtil.containsLocation(point, p.getPoints(), false); if (contains) break; } Toast.makeText(getActivity(), "Click in polygon? " + contains, Toast.LENGTH_SHORT).show(); } @Override protected void onMapReady(View view, Bundle savedInstanceState) { googleMap.setOnMapClickListener(this); addMyPolygons(); } 

Aunque user1504495 ha respondido en breve como lo he usado. Pero en lugar de utilizar toda la biblioteca de Map Utility, use estos métodos.

De su clase de actividad, pase los parámetros en consecuencia:

 if (area.containsLocation(Touchablelatlong, listLatlong, true)) isMarkerINSide = true; else isMarkerINSide = false; 

y poner a los siguientes en una clase separada:

 /** * Computes whether the given point lies inside the specified polygon. * The polygon is always cosidered 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. */ public static boolean containsLocation(LatLng point, List polygon, boolean geodesic) { final int size = polygon.size(); if (size == 0) { return false; } double lat3 = toRadians(point.latitude); double lng3 = toRadians(point.longitude); LatLng prev = polygon.get(size - 1); double lat1 = toRadians(prev.latitude); double lng1 = toRadians(prev.longitude); int nIntersect = 0; for (LatLng point2 : polygon) { double dLng3 = wrap(lng3 - lng1, -PI, PI); // Special case: point equal to vertex is inside. if (lat3 == lat1 && dLng3 == 0) { return true; } double lat2 = toRadians(point2.latitude); double lng2 = toRadians(point2.longitude); // Offset longitudes by -lng1. if (intersects(lat1, lat2, wrap(lng2 - lng1, -PI, PI), lat3, dLng3, geodesic)) { ++nIntersect; } lat1 = lat2; lng1 = lng2; } return (nIntersect & 1) != 0; } /** * Wraps the given value into the inclusive-exclusive interval between min and max. * @param n The value to wrap. * @param min The minimum. * @param max The maximum. */ static double wrap(double n, double min, double max) { return (n >= min && n < max) ? n : (mod(n - min, max - min) + min); } /** * Returns the non-negative remainder of x / m. * @param x The operand. * @param m The modulus. */ static double mod(double x, double m) { return ((x % m) + m) % m; } /** * Computes whether the vertical segment (lat3, lng3) to South Pole intersects the segment * (lat1, lng1) to (lat2, lng2). * Longitudes are offset by -lng1; the implicit lng1 becomes 0. */ private static boolean intersects(double lat1, double lat2, double lng2, double lat3, double lng3, boolean geodesic) { // Both ends on the same side of lng3. if ((lng3 >= 0 && lng3 >= lng2) || (lng3 < 0 && lng3 < lng2)) { return false; } // Point is South Pole. if (lat3 <= -PI/2) { return false; } // Any segment end is a pole. if (lat1 <= -PI/2 || lat2 <= -PI/2 || lat1 >= PI/2 || lat2 >= PI/2) { return false; } if (lng2 <= -PI) { return false; } double linearLat = (lat1 * (lng2 - lng3) + lat2 * lng3) / lng2; // Northern hemisphere and point under lat-lng line. if (lat1 >= 0 && lat2 >= 0 && lat3 < linearLat) { return false; } // Southern hemisphere and point above lat-lng line. if (lat1 <= 0 && lat2 <= 0 && lat3 >= linearLat) { return true; } // North Pole. if (lat3 >= PI/2) { return true; } // Compare lat3 with latitude on the GC/Rhumb segment corresponding to lng3. // Compare through a strictly-increasing function (tan() or mercator()) as convenient. return geodesic ? tan(lat3) >= tanLatGC(lat1, lat2, lng2, lng3) : mercator(lat3) >= mercatorLatRhumb(lat1, lat2, lng2, lng3); } /** * Returns tan(latitude-at-lng3) on the great circle (lat1, lng1) to (lat2, lng2). lng1==0. * See http://williams.best.vwh.net/avform.htm . */ private static double tanLatGC(double lat1, double lat2, double lng2, double lng3) { return (tan(lat1) * sin(lng2 - lng3) + tan(lat2) * sin(lng3)) / sin(lng2); } /** * Returns mercator Y corresponding to latitude. * See http://en.wikipedia.org/wiki/Mercator_projection . */ static double mercator(double lat) { return log(tan(lat * 0.5 + PI/4)); } /** * Returns mercator(latitude-at-lng3) on the Rhumb line (lat1, lng1) to (lat2, lng2). lng1==0. */ private static double mercatorLatRhumb(double lat1, double lat2, double lng2, double lng3) { return (mercator(lat1) * (lng2 - lng3) + mercator(lat2) * lng3) / lng2; } 

Solo por coherencia: en onCapClick no se invoca cuando el usuario toca un polígono (u otra superposición), y se menciona en javadoc.

Hice una solución para interceptar los eventos de los grifos antes de que MapFragment los maneje, y proyectar el punto para mapear las coordenadas y verificar si el punto está dentro de cualquier polígono, como se sugiere en otra respuesta.

Ver más detalles aquí