Polygon Touch-Erkennung Google Map API V2

Lesezeit: 9 Minuten

Polygon Touch Erkennung Google Map API V2
Dwill

Ich versuche herauszufinden, wie das am besten geht, ich habe eine Karte mit einer Polygon darauf gezeichnet. Da scheint es nicht so, als ob die Google Maps API V2 eine Berührungserkennung auf einem Polygon hat. Ich habe mich gefragt, ob es möglich ist zu erkennen, ob sich der Berührungspunkt innerhalb des Polygons befindet? Wenn ja, wie dann ist mein Hauptziel, einen Zustand auf einer Karte zu skizzieren. Wenn der Benutzer auf diesen Zustand tippt, werden weitere Details in einer benutzerdefinierten Ansicht angezeigt. Ab sofort kann ich das einfangen MapOnClick der Karte, aber wenn der Benutzer in die Karte tippt Polygon Ich will das polygon.getID() auf die setzen Toast. Ich bin ein Neuling und entschuldige mich, wenn ich nicht klar genug bin.

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, das ist, was ich bisher halbbeschäftigt habe

    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 < 0) bX += 360;

        if (pY == aY || pY == bY) pY += 0.00000001;
        if ((pY > bY || pY < aY) || (pX > Math.max(aX, bX))) return false;
        if (pX < Math.min(aX, bX))

            return true;
//  }

    double m = (aX != bX) ? ((bY - aY) / (bX - aX)) : aX;
    double bee = (aX != pX) ? ((pY - aY) / (pX - aX)) : aX;
    double x = (pY - bee) / m;

    return x > pX;
}

}

Das Problem, das ich habe, ist, dass die Berührung links von jedem Polygon wahr ist, bis es ein anderes erreicht. Was ist mit meinem Algorithmus falsch, der dieses Problem verursachen würde? Jede Hilfe wäre dankbar.

  • Versuchen Sie also zur Verdeutlichung herauszufinden, ob Ihre Berührung innerhalb der geografischen Grenzen des Polygons stattgefunden hat oder nicht?

    – Matt

    18. Januar ’13 um 18:53

1641716763 190 Polygon Touch Erkennung Google Map API V2
Matt

Das Problem, das Sie zu lösen versuchen, ist das Punkt im Polygon Prüfung.

Um das Konzept von Ray Casting zu visualisieren:

Zeichnen Sie ein Polygon auf ein Blatt Papier. Zeichnen Sie dann von einem beliebigen Punkt aus eine gerade Linie rechts auf der Seite. Wenn Ihre Linie Ihr Polygon ungerade oft schneidet, bedeutet dies, dass Ihr Startpunkt innerhalb des Polygons lag.

Also, wie macht man das im Code?

Ihr Polygon besteht aus einer Liste von Scheitelpunkten: ArrayList<Geopoint> vertices. Du musst dir jeden anschauen Line Segment individuell, und sehen Sie, ob Ihre Ray schneidet es

private boolean isPointInPolygon(Geopoint tap, ArrayList<Geopoint> vertices) {
    int intersectCount = 0;
    for(int j=0; j<vertices.size()-1; j++) {
        if( rayCastIntersect(tap, vertices.get(j), vertices.get(j+1)) ) {
            intersectCount++;
        }
    }

    return (intersectCount%2) == 1); // odd = inside, even = outside;
}

private boolean rayCastIntersect(Geopoint tap, Geopoint vertA, Geopoint vertB) {

    double aY = vertA.getLatitude();
    double bY = vertB.getLatitude();
    double aX = vertA.getLongitude();
    double bX = vertB.getLongitude();
    double pY = tap.getLatitude();
    double pX = tap.getLongitude();

    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;
}

  • Ok Matt, ich habe es zum Laufen gebracht. Mein einziges Problem ist, dass wenn zwei Polygone auf demselben x-Pfad liegen, sie als eins behandelt werden und dieselbe ID-Nummer für sie verwendet wird. Irgendwelche Gedanken?

    – Dwill

    22. Januar ’13 um 22:54

  • Hmm, kannst du das näher ausführen? Was ich mir vorstelle, ist, dass die App sie als ein Polygon behandelt, wenn sich zwei Polygone eine Kante teilen?

    – Matt

    23. Januar ’13 um 17:22

  • Ich bin mir nicht sicher, was das Problem ist. Diese Funktion sollte true zurückgeben, wenn Sie sich links von dem zu testenden Liniensegment befanden. Sie müssen diese Funktion für jeden Satz benachbarter Punkte in Ihrem Polygon ausführen und ermitteln, wie oft diese Funktion “true” zurückgegeben hat.

    – Matt

    26. Februar ’13 um 20:00

  • Wie zu tun für Polyline?

    – Muhammad Babar

    19. Okt ’14 um 17:28

  • @MuhammadBabar Ich würde das in einem separaten Thread fragen. Es gibt mehrere Möglichkeiten, dieses Problem anzugehen. Es ist schwierig, da ein antippbarer Bereichspuffer erforderlich ist (die Wahrscheinlichkeit, dass der Benutzer genau auf die Leitung tippt, ist sehr gering).

    – Matt

    21. Okt ’14 um 18:15

Die Google Maps-Supportbibliothek verfügt jetzt über eine statische Methode, die diese Prüfung für Sie durchführt:

PolyUtil.containsLocation(LatLng point, List<LatLng>polygon, boolean geodesic);

Obwohl die Dokumente in der Anleitung nicht explizit darauf hinweisen, ist die Methode vorhanden

Dokumente zur Google Maps-Supportbibliothek

  • Dies ist eine genauere Methode. Und das funktioniert wirklich wie Charme. Schätzen Sie diese Antwort. Diese Methode sollte als vom Google Map Utility selbst angesehen werden.

    – Sagar Shah

    13. August ’15 um 12:27

1641716763 47 Polygon Touch Erkennung Google Map API V2
Matiasch

Mit dem Veröffentlichung der Google Play-Dienste 8.4.0, bietet die Maps-API Unterstützung für das Hinzufügen eines OnPolygonClickListener zu Polygonen. Beide Polygone, Polylinien und Überlagerungen unterstützen ähnliche Veranstaltungen.

Du musst nur anrufen GoogleMap.setOnPolygonClickListener(OnPolygonClickListener listener) einzurichten, und entsprechend für die anderen Hörer (setOnPolylineClickListener, &C):

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

Obwohl es etwas spät ist, löst es diesen Anwendungsfall recht gut.

  • Wie erkennt man, auf welches Polygon geklickt wird?

    – Samad

    28. Mai ’16 um 11:39

  • @Delta ist das Polygon, das Sie im Listener erhalten.

    – Matiasch

    28. Mai ’16 um 16:34

  • Ich habe eine Klasse für Polygone erstellt, die IDs enthält, aber ich kann keine ID für ein Polygon festlegen. Wie kann ich die ID des Polygons herausfinden?

    – Samad

    29. Mai ’16 um 4:02

  • @Delta7 Sie könnten einfach eine Map für die Referenzen erstellen und dort alle Polygone hinzufügen, die Sie der Karte hinzufügen. Auf diese Weise können Sie eine Suche durchführen, um die zugehörige ID zu finden.

    – Matiasch

    29. Mai ’16 um 5:09

  • Können Sie die angeklickte Koordinate aus Polygon abrufen?

    – Gereltod

    8. August ’16 um 3:49

Polygon Touch Erkennung Google Map API V2
Sagar Shah

Obwohl user1504495 kurz geantwortet hat, wie ich es benutzt habe. Aber anstatt das Ganze zu verwenden Bibliothek des Kartendienstprogramms Verwenden Sie diese Methoden.

Passen Sie die Parameter Ihrer Aktivitätsklasse entsprechend an:

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

und füge folgendes in eine separate Klasse ein:

/**
     * 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<LatLng> 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;
    } 

Hier ist ein vollständiges Arbeitsbeispiel, um zu wissen, ob eine Berührung auf einem Polygon stattgefunden hat. Manche Antworten sind komplizierter als nötig. Diese Lösung verwendet die “android-maps-utils”

// compile 'com.google.maps.android:android-maps-utils:0.3.4'
private ArrayList<Polygon> 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();
}

1641716764 952 Polygon Touch Erkennung Google Map API V2
Gemeinschaft

Nur aus Gründen der Konsistenz – onMapClick wird nicht aufgerufen, wenn der Benutzer auf ein Polygon (oder ein anderes Overlay) tippt, und es wird in Javadoc erwähnt.

Ich habe eine Problemumgehung vorgenommen, um Taps-Ereignisse abzufangen, bevor MapFragment sie verarbeitet, und Punkt-zu-Karten-Koordinaten zu projizieren und zu überprüfen, ob sich der Punkt innerhalb eines Polygons befindet, wie in einer anderen Antwort vorgeschlagen.

Weitere Details finden Sie hier

.

205840cookie-checkPolygon Touch-Erkennung Google Map API V2

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy