it-swarm.com.de

Wie kann ich feststellen, ob sich ein 2D-Punkt innerhalb eines Polygons befindet?

Ich versuche, einen fast 2D-Punkt innerhalb eines Polygonalgorithmus zu erstellen, der beim Hit-Testing (z. B. Polygon.contains(p:Point)) verwendet wird. Vorschläge für effektive Techniken werden gebeten.

442
Scott Evernden

Für Grafiken möchte ich lieber Ganzzahlen nicht bevorzugen. Viele Systeme verwenden Ganzzahlen für das UI-Painting (Pixel sind immerhin Ints), aber macOS verwendet beispielsweise Float für alles. macOS kennt nur Punkte, und ein Punkt kann in ein Pixel umgewandelt werden. Abhängig von der Monitorauflösung kann dies jedoch zu etwas anderem führen. Auf Retina-Bildschirmen ist ein halber Punkt (0,5/0,5) Pixel. Trotzdem habe ich nie bemerkt, dass macOS-Benutzeroberflächen wesentlich langsamer sind als andere Benutzeroberflächen. Schließlich funktionieren alle 3D-APIs (OpenGL oder Direct3D) auch mit Floats und moderne Grafikbibliotheken nutzen häufig die GPU-Beschleunigung.

Nun, Sie sagten, Geschwindigkeit ist Ihr wichtigstes Anliegen, okay, lassen Sie uns auf Geschwindigkeit gehen. Bevor Sie einen ausgeklügelten Algorithmus ausführen, führen Sie zunächst einen einfachen Test durch. Erstellen Sie um Ihr Polygon einen mit Achse ausgerichteten Begrenzungsrahmen. Das ist sehr einfach, schnell und kann Ihnen schon viele Berechnungen ersparen. Wie funktioniert das? Iteriere über alle Punkte des Polygons und finde die Min/Max-Werte von X und Y. 

Z.B. Sie haben die Punkte (9/1), (4/3), (2/7), (8/2), (3/6). Das heißt, Xmin ist 2, Xmax ist 9, Ymin ist 1 und Ymax ist 7. Ein Punkt außerhalb des Rechtecks ​​mit den beiden Kanten (2/1) und (9/7) darf nicht innerhalb des Polygons liegen.

// p is your point, p.x is the x coord, p.y is the y coord
if (p.x < Xmin || p.x > Xmax || p.y < Ymin || p.y > Ymax) {
    // Definitely not within the polygon!
}

Dies ist der erste Test, der für jeden Punkt ausgeführt wird. Wie Sie sehen, ist dieser Test ultraschnell, aber auch sehr grob. Um Punkte zu behandeln, die sich innerhalb des Begrenzungsrechtecks ​​befinden, benötigen wir einen komplexeren Algorithmus. Es gibt mehrere Möglichkeiten, wie dies berechnet werden kann. Welche Methode funktioniert, hängt auch davon ab, ob das Polygon Löcher haben kann oder immer solide ist. Hier sind Beispiele für feste (eine konvexe, eine konkave):

Polygon without hole

Und hier ist einer mit einem Loch:

Polygon with hole

Der grüne hat ein Loch in der Mitte!

Der einfachste Algorithmus, der alle drei oben genannten Fälle beherrscht und immer noch ziemlich schnell ist, heißt ray casting . Die Idee des Algorithmus ist ziemlich einfach: Zeichnen Sie einen virtuellen Strahl von einem beliebigen Punkt außerhalb des Polygons zu Ihrem Punkt und zählen Sie, wie oft er auf eine Seite des Polygons trifft. Wenn die Anzahl der Treffer gerade ist, liegt sie außerhalb des Polygons. Wenn sie ungerade ist, ist sie im Innern.

Demonstrating how the ray cuts through a polygon

Der Windungsnummernalgorithmus wäre eine Alternative, er ist genauer für Punkte, die sich sehr nahe an einer Polygonlinie befinden, ist aber auch viel langsamer. Ray Casting kann aufgrund von begrenzter Fließkomma-Genauigkeit und Rundungsproblemen bei Punkten zu nahe an einer Polygonseite fehlschlagen. In Wirklichkeit ist dies jedoch kaum ein Problem, da ein Punkt so nahe an einer Seite liegt Betrachter zu erkennen, ob er bereits drinnen oder noch draußen ist.

Sie haben immer noch die Begrenzungsbox von oben, erinnerst du dich? Wählen Sie einfach einen Punkt außerhalb des Begrenzungsrahmens und verwenden Sie ihn als Ausgangspunkt für Ihren Strahl. Z.B. der Punkt (Xmin - e/p.y) liegt mit Sicherheit außerhalb des Polygons. 

Aber was ist e? Nun, e (eigentlich epsilon) gibt der Begrenzungsbox etwas padding. Wie gesagt, das Raytracing schlägt fehl, wenn wir zu nahe an einer Polygonlinie beginnen. Da der Begrenzungsrahmen möglicherweise dem Polygon entspricht (wenn das Polygon ein rechteckiges Rechteck ist, entspricht der Begrenzungsrahmen dem Polygon selbst!), Wir brauchen etwas Abstand, um dies sicher zu machen, das ist alles. Wie groß sollten Sie e wählen? Nicht zu groß. Dies hängt von dem Koordinatensystem-Maßstab ab, den Sie zum Zeichnen verwenden. Wenn Ihre Pixelschrittweite 1,0 ist, wählen Sie einfach 1,0 (0,1 hätte jedoch ebenfalls funktioniert)

Nachdem wir nun den Strahl mit den Start- und Endkoordinaten haben, verschiebt sich das Problem von "ist der Punkt innerhalb des Polygons" nach "wie oft schneidet der Strahl eine Polygonseite". Daher können wir nicht mehr wie zuvor mit den Polygonpunkten arbeiten, jetzt brauchen wir die tatsächlichen Seiten. Eine Seite wird immer durch zwei Punkte definiert.

side 1: (X1/Y1)-(X2/Y2)
side 2: (X2/Y2)-(X3/Y3)
side 3: (X3/Y3)-(X4/Y4)
:

Sie müssen den Strahl gegen alle Seiten testen. Betrachten Sie den Strahl als einen Vektor und jede Seite als einen Vektor. Der Strahl muss jede Seite genau oder nie treffen. Es kann nicht dieselbe Seite zweimal treffen. Zwei Linien im 2D-Raum schneiden sich immer genau einmal, sofern sie nicht parallel sind. In diesem Fall schneiden sie niemals. Da Vektoren jedoch eine begrenzte Länge haben, sind zwei Vektoren möglicherweise nicht parallel und schneiden sich trotzdem nie, weil sie zu kurz sind, um sich jemals zu treffen.// Test the ray against all sides int intersections = 0; for (side = 0; side < numberOfSides; side++) { // Test if current side intersects with ray. // If yes, intersections++; } if ((intersections & 1) == 1) { // Inside of polygon } else { // Outside of polygon }

#define NO 0 #define YES 1 #define COLLINEAR 2 int areIntersecting( float v1x1, float v1y1, float v1x2, float v1y2, float v2x1, float v2y1, float v2x2, float v2y2 ) { float d1, d2; float a1, a2, b1, b2, c1, c2; // Convert vector 1 to a line (line 1) of infinite length. // We want the line in linear equation standard form: A*x + B*y + C = 0 // See: http://en.wikipedia.org/wiki/Linear_equation a1 = v1y2 - v1y1; b1 = v1x1 - v1x2; c1 = (v1x2 * v1y1) - (v1x1 * v1y2); // Every point (x,y), that solves the equation above, is on the line, // every point that does not solve it, is not. The equation will have a // positive result if it is on one side of the line and a negative one // if is on the other side of it. We insert (x1,y1) and (x2,y2) of vector // 2 into the equation above. d1 = (a1 * v2x1) + (b1 * v2y1) + c1; d2 = (a1 * v2x2) + (b1 * v2y2) + c1; // If d1 and d2 both have the same sign, they are both on the same side // of our line 1 and in that case no intersection is possible. Careful, // 0 is a special case, that's why we don't test ">=" and "<=", // but "<" and ">". if (d1 > 0 && d2 > 0) return NO; if (d1 < 0 && d2 < 0) return NO; // The fact that vector 2 intersected the infinite line 1 above doesn't // mean it also intersects the vector 1. Vector 1 is only a subset of that // infinite line 1, so it may have intersected that line before the vector // started or after it ended. To know for sure, we have to repeat the // the same test the other way round. We start by calculating the // infinite line 2 in linear equation standard form. a2 = v2y2 - v2y1; b2 = v2x1 - v2x2; c2 = (v2x2 * v2y1) - (v2x1 * v2y2); // Calculate d1 and d2 again, this time using points of vector 1. d1 = (a2 * v1x1) + (b2 * v1y1) + c2; d2 = (a2 * v1x2) + (b2 * v1y2) + c2; // Again, if both have the same sign (and neither one is 0), // no intersection is possible. if (d1 > 0 && d2 > 0) return NO; if (d1 < 0 && d2 < 0) return NO; // If we get here, only two possibilities are left. Either the two // vectors intersect in exactly one point or they are collinear, which // means they intersect in any number of points from zero to infinite. if ((a1 * b2) - (a2 * b1) == 0.0f) return COLLINEAR; // If they are not collinear, they must intersect in exactly one point. return YES; }

Was ist mit COLLINEAR? Dies bedeutet, dass beide Vektoren auf derselben unendlichen Linie liegen, abhängig von Position und Länge, sie schneiden sich überhaupt nicht oder sie schneiden sich in einer unendlichen Anzahl von Punkten. Ich bin nicht absolut sicher, wie ich mit diesem Fall umgehen sollte, ich würde ihn auch nicht als Schnittpunkt betrachten. Nun, dieser Fall ist in der Praxis ohnehin aufgrund von Rundungsfehlern mit Gleitkommazahlen eher selten. Besserer Code würde wahrscheinlich nicht auf == 0.0f testen, sondern auf etwas wie < epsilon, wobei epsilon eine eher kleine Zahl ist. 

Wenn Sie eine größere Anzahl von Punkten testen müssen, können Sie das Ganze sicherlich etwas beschleunigen, indem Sie die linearen Gleichungsstandardformen der Polygonseiten im Speicher belassen, sodass Sie diese nicht jedes Mal neu berechnen müssen. Dadurch sparen Sie bei jedem Test zwei Gleitkomma-Multiplikationen und drei Gleitkomma-Subtraktionen, um drei Gleitkomma-Werte pro Polygonseite im Speicher abzulegen. Es ist ein typischer Kompromiss zwischen Speicher und Rechenzeit.

Last but not least: Wenn Sie 3D-Hardware verwenden, um das Problem zu lösen, gibt es eine interessante Alternative. Lassen Sie die GPU die ganze Arbeit für Sie erledigen. Erstellen Sie eine Malfläche, die sich außerhalb des Bildschirms befindet. Füllen Sie es vollständig mit der Farbe Schwarz. Nun lassen Sie OpenGL oder Direct3D Paint Ihr Polygon (oder sogar alle Polygone, wenn Sie nur testen möchten, ob sich der Punkt in einem von ihnen befindet, aber Sie interessieren sich nicht für welches) und füllen die Polygone mit einem anderen Farbe, z Weiß. Um zu überprüfen, ob sich ein Punkt innerhalb des Polygons befindet, erhalten Sie die Farbe dieses Punkts von der Zeichenoberfläche. Dies ist nur ein O(1) Speicherabruf.

Natürlich ist diese Methode nur anwendbar, wenn Ihre Zeichenfläche nicht groß sein muss. Wenn es nicht in den GPU-Speicher passt, ist diese Methode langsamer als auf der CPU. Wenn dies sehr groß sein muss und Ihre GPU moderne Shader unterstützt, können Sie die GPU weiterhin verwenden, indem Sie den oben gezeigten Ray Casting als GPU-Shader implementieren, was absolut möglich ist. Für eine größere Anzahl von Polygonen oder eine große Anzahl von zu testenden Punkten wird sich dies auszahlen. Einige GPUs werden in der Lage sein, 64 bis 256 Punkte parallel zu testen. Beachten Sie jedoch, dass das Übertragen von Daten von der CPU zur GPU und zurück immer teuer ist. Wenn Sie also nur ein paar Punkte gegen ein paar einfache Polygone testen, bei denen entweder die Punkte oder die Polygone dynamisch sind und sich häufig ändern, zahlt sich ein GPU-Ansatz selten aus aus.

Of course this method is only usable if your drawing surface doesn't have to be huge. If it cannot fit into the GPU memory, this method is slower than doing it on the CPU. If it would have to be huge and your GPU supports modern shaders, you can still use the GPU by implementing the ray casting shown above as a GPU shader, which absolutely is possible. For a larger number of polygons or a large number of points to test, this will pay off, consider some GPUs will be able to test 64 to 256 points in parallel. Note however that transferring data from CPU to GPU and back is always expensive, so for just testing a couple of points against a couple of simple polygons, where either the points or the polygons are dynamic and will change frequently, a GPU approach will rarely pay off.

661
Mecki

Ich denke, der folgende Code ist die beste Lösung (aus hier ):

int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
  int i, j, c = 0;
  for (i = 0, j = nvert-1; i < nvert; j = i++) {
    if ( ((verty[i]>testy) != (verty[j]>testy)) &&
     (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
       c = !c;
  }
  return c;
}

Argumente

  • nvert: Anzahl der Scheitelpunkte im Polygon. In dem oben genannten Artikel wurde diskutiert, ob der erste Scheitelpunkt am Ende wiederholt werden soll.
  • vertx, verty: Arrays, die die x- und y-Koordinaten der Scheitelpunkte des Polygons enthalten.
  • testx, testy: X- und Y-Koordinate des Testpunkts.

Es ist sowohl kurz als auch effizient und funktioniert sowohl für konvexe als auch für konkave Polygone. Wie zuvor vorgeschlagen, sollten Sie zuerst das Begrenzungsrechteck prüfen und die Polygonlöcher separat behandeln. 

Die Idee dahinter ist ziemlich einfach. Der Autor beschreibt es wie folgt:

Ich führe einen semi-unendlichen Strahl horizontal aus (ansteigend x, festes y) und zähle, wie viele Kanten er kreuzt. Bei jeder Kreuzung wechselt der Strahl zwischen innen und außen. Dies wird als Jordan-Kurventheorem bezeichnet.

Die Variable c wechselt jedes Mal von 0 auf 1 und von 1 auf 0, wenn der horizontale Strahl eine Kante kreuzt. Im Grunde ist es also wichtig zu verfolgen, ob die Anzahl der gekreuzten Kanten gerade oder ungerade ist. 0 bedeutet gerade und 1 ungerade.

549
nirg

Hier ist eine C # -Version der Antwort von nirg , die von diesem RPI-Professor stammt. Beachten Sie, dass die Verwendung des Codes aus dieser RPI-Quelle eine Zuordnung erfordert.

Am oberen Rand wurde ein Häkchen gesetzt. Wie James Brown hervorhebt, ist der Hauptcode jedoch fast so schnell wie das Markierungsfeld für das Begrenzungsfeld selbst. Daher kann das Markierungsfeld für das Begrenzungsfeld den gesamten Vorgang tatsächlich verlangsamen, falls sich die meisten Punkte, die Sie überprüfen, im Begrenzungsrahmen befinden . Sie können also das Begrenzungsfeld auschecken lassen, oder Sie können die Begrenzungsrahmen Ihrer Polygone vorberechnen, wenn sie ihre Form nicht allzu oft ändern.

public bool IsPointInPolygon( Point p, Point[] polygon )
{
    double minX = polygon[ 0 ].X;
    double maxX = polygon[ 0 ].X;
    double minY = polygon[ 0 ].Y;
    double maxY = polygon[ 0 ].Y;
    for ( int i = 1 ; i < polygon.Length ; i++ )
    {
        Point q = polygon[ i ];
        minX = Math.Min( q.X, minX );
        maxX = Math.Max( q.X, maxX );
        minY = Math.Min( q.Y, minY );
        maxY = Math.Max( q.Y, maxY );
    }

    if ( p.X < minX || p.X > maxX || p.Y < minY || p.Y > maxY )
    {
        return false;
    }

    // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
    bool inside = false;
    for ( int i = 0, j = polygon.Length - 1 ; i < polygon.Length ; j = i++ )
    {
        if ( ( polygon[ i ].Y > p.Y ) != ( polygon[ j ].Y > p.Y ) &&
             p.X < ( polygon[ j ].X - polygon[ i ].X ) * ( p.Y - polygon[ i ].Y ) / ( polygon[ j ].Y - polygon[ i ].Y ) + polygon[ i ].X )
        {
            inside = !inside;
        }
    }

    return inside;
}
60
M Katz

Hier ist eine JavaScript-Variante der Antwort von M. Katz, die auf Nirgs Ansatz basiert:

function pointIsInPoly(p, polygon) {
    var isInside = false;
    var minX = polygon[0].x, maxX = polygon[0].x;
    var minY = polygon[0].y, maxY = polygon[0].y;
    for (var n = 1; n < polygon.length; n++) {
        var q = polygon[n];
        minX = Math.min(q.x, minX);
        maxX = Math.max(q.x, maxX);
        minY = Math.min(q.y, minY);
        maxY = Math.max(q.y, maxY);
    }

    if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) {
        return false;
    }

    var i = 0, j = polygon.length - 1;
    for (i, j; i < polygon.length; j = i++) {
        if ( (polygon[i].y > p.y) != (polygon[j].y > p.y) &&
                p.x < (polygon[j].x - polygon[i].x) * (p.y - polygon[i].y) / (polygon[j].y - polygon[i].y) + polygon[i].x ) {
            isInside = !isInside;
        }
    }

    return isInside;
}
43
Philipp Lenssen

Berechnen Sie die orientierte Winkelsumme zwischen dem Punkt p und jeder der Polygonspitzen. Wenn der Gesamtorientierungswinkel 360 Grad beträgt, befindet sich der Punkt innerhalb. Wenn die Summe 0 ist, ist der Punkt außerhalb.

Diese Methode gefällt mir besser, weil sie robuster ist und weniger von der numerischen Genauigkeit abhängig ist.

Methoden, die die Gleichheit der Anzahl von Schnittpunkten berechnen, sind begrenzt, da Sie während der Berechnung der Schnittpunktanzahl einen Scheitelpunkt treffen können.

BEARBEITEN: Übrigens, diese Methode arbeitet mit konkaven und konvexen Polygonen.

EDIT: Ich habe kürzlich einen ganzen Wikipedia-Artikel zum Thema gefunden.

28
David S.

Der von Bobobobo zitierte Eric Haines-Artikel ist wirklich ausgezeichnet. Besonders interessant sind die Tabellen, in denen die Leistung der Algorithmen verglichen wird. Die Winkelsummenmethode ist im Vergleich zu den anderen wirklich schlecht. Interessant ist auch, dass Optimierungen wie die Verwendung eines Lookup-Gitters zur weiteren Unterteilung des Polygons in "In" - und "Out" -Sektoren den Test selbst bei Polygonen mit mehr als 1000 Seiten unglaublich schnell machen können.

Wie auch immer, es ist noch früh, aber ich stimme für die Crossings-Methode, was Mecki meiner Meinung nach so ziemlich beschreibt. Ich fand es jedoch am besten beschrieben und kodifiziert von David Bourke . Ich finde es toll, dass keine echte Trigonometrie erforderlich ist, und dass sie für konvexe und konkave Formen geeignet ist.

Übrigens, hier ist eine der Performance-Tabellen aus dem Artikel von Eric Haines, in der Interesse an zufälligen Polygonen getestet wird.

                       number of edges per polygon
                         3       4      10      100    1000
MacMartin               2.9     3.2     5.9     50.6    485
Crossings               3.1     3.4     6.8     60.0    624
Triangle Fan+Edge sort  1.1     1.8     6.5     77.6    787
Triangle Fan            1.2     2.1     7.3     85.4    865
Barycentric             2.1     3.8    13.8    160.7   1665
Angle Summation        56.2    70.4   153.6   1403.8  14693

Grid (100x100)          1.5     1.5     1.6      2.1      9.8
Grid (20x20)            1.7     1.7     1.9      5.7     42.2
Bins (100)              1.8     1.9     2.7     15.1    117
Bins (20)               2.1     2.2     3.7     26.3    278
18
Gavin

Diese Frage ist so interessant. Ich habe eine andere praktikable Idee, die sich von den anderen Antworten dieses Beitrags unterscheidet.

Die Idee ist, die Winkelsumme zu verwenden, um zu entscheiden, ob sich das Ziel innerhalb oder außerhalb befindet. Wenn sich das Ziel innerhalb eines Bereichs befindet, ist die Summe der Winkelform durch das Ziel und alle zwei Grenzpunkte 360. Wenn sich das Ziel außerhalb befindet, ist die Summe nicht 360. Der Winkel hat die Richtung. Wenn der Winkel nach hinten geht, ist der Winkel negativ. Dies ist wie die Berechnung der Wicklungsnummer .

In diesem Bild erhalten Sie ein grundlegendes Verständnis der Idee:  enter image description here

Mein Algorithmus geht davon aus, dass der Uhrzeigersinn die positive Richtung ist. Hier ist ein möglicher Input:

[[-122.402015, 48.225216], [-117.032049, 48.999931], [-116.919132, 45.995175], [-124.079107, 46.267259], [-124.717175, 48.377557], [-122.92315, 47.047963], [-122.402015, 48.225216]]

Folgendes ist der Python-Code, der die Idee implementiert:

def isInside(self, border, target):
degree = 0
for i in range(len(border) - 1):
    a = border[i]
    b = border[i + 1]

    # calculate distance of vector
    A = getDistance(a[0], a[1], b[0], b[1]);
    B = getDistance(target[0], target[1], a[0], a[1])
    C = getDistance(target[0], target[1], b[0], b[1])

    # calculate direction of vector
    ta_x = a[0] - target[0]
    ta_y = a[1] - target[1]
    tb_x = b[0] - target[0]
    tb_y = b[1] - target[1]

    cross = tb_y * ta_x - tb_x * ta_y
    clockwise = cross < 0

    # calculate sum of angles
    if(clockwise):
        degree = degree + math.degrees(math.acos((B * B + C * C - A * A) / (2.0 * B * C)))
    else:
        degree = degree - math.degrees(math.acos((B * B + C * C - A * A) / (2.0 * B * C)))

if(abs(round(degree) - 360) <= 3):
    return True
return False
17
Junbang Huang

Ich habe daran gearbeitet, als ich als Forscher unter Michael Stonebraker - wissen Sie, der Professor, der Ingres , PostgreSQL , usw. entworfen hat.

Wir haben erkannt, dass der schnellste Weg war, zuerst eine Begrenzungsbox zu machen, weil sie SUPER-schnell ist. Wenn es außerhalb der Begrenzungsbox liegt, ist es außerhalb. Ansonsten erledigst du die härtere Arbeit ...

Wenn Sie einen hervorragenden Algorithmus wünschen, schauen Sie sich den Open Source-Projekt PostgreSQL-Quellcode für die Geo-Arbeit an ...

Ich möchte darauf hinweisen, dass wir nie einen Einblick in die Rechtshänder-Linkshändigkeit (auch als "Innen" -Verfahren außerhalb) haben ...


AKTUALISIEREN

Der Link von BKB lieferte eine gute Anzahl vernünftiger Algorithmen. Ich arbeitete an Problemen der Erdwissenschaft und brauchte daher eine Lösung, die in Längen- und Breitengraden funktioniert, und sie hat das eigentümliche Problem der Händigkeit - liegt das Gebiet innerhalb des kleineren Gebiets oder des größeren Gebiets? Die Antwort ist, dass die "Richtung" der Ecken wichtig ist - entweder Linkshänder oder Rechtshänder. Auf diese Weise können Sie einen Bereich als "innerhalb" eines gegebenen Polygons angeben. Daher verwendete meine Arbeit die auf dieser Seite aufgezählte Lösung 3. 

Darüber hinaus verwendete meine Arbeit separate Funktionen für Tests "on the line".

Seitdem jemand gefragt hat: Wir haben herausgefunden, dass der Test des Begrenzungskastens am besten war, wenn die Anzahl der Eckpunkte über eine bestimmte Anzahl hinausging. Führen Sie einen sehr kurzen Test durch, bevor Sie den längeren Test durchführen größtes x, kleinstes x, größtes y und kleinstes y und zusammenfügen aus vier Punkten einer Box ...

Ein weiterer Tipp für die folgenden: Wir haben unser gesamtes, komplexeres und "lichtverdunkelndes" Berechnen in einem Rasterfeld alle in positiven Punkten auf einer Ebene durchgeführt und dann wieder in "echte" Längen-/Breitengrade projiziert, um mögliche Fehler zu vermeiden Umwickeln, wenn eine Linie mit 180er Länge gekreuzt wird und wenn mit polaren Regionen umgegangen wird. Großartig gearbeitet

7
Richard T

Wirklich wie die von Nirg veröffentlichte und von Bobobobo bearbeitete Lösung. Ich habe es einfach Javascript-freundlicher gemacht und etwas lesbarer für meinen Gebrauch:

function insidePoly(poly, pointx, pointy) {
    var i, j;
    var inside = false;
    for (i = 0, j = poly.length - 1; i < poly.length; j = i++) {
        if(((poly[i].y > pointy) != (poly[j].y > pointy)) && (pointx < (poly[j].x-poly[i].x) * (pointy-poly[i].y) / (poly[j].y-poly[i].y) + poly[i].x) ) inside = !inside;
    }
    return inside;
}
7
Dave Seidman

Schnelle Version der Antwort von nirg :

extension CGPoint {
    func isInsidePolygon(vertices: [CGPoint]) -> Bool {
        guard !vertices.isEmpty else { return false }
        var j = vertices.last!, c = false
        for i in vertices {
            let a = (i.y > y) != (j.y > y)
            let b = (x < (j.x - i.x) * (y - i.y) / (j.y - i.y) + i.x)
            if a && b { c = !c }
            j = i
        }
        return c
    }
}
7
bzz

Die Antwort von David Segond ist so ziemlich die übliche Standardantwort, und die von Richard T. ist die häufigste Optimierung, obwohl es einige andere gibt. Andere starke Optimierungen basieren auf weniger allgemeinen Lösungen. Wenn Sie beispielsweise dasselbe Polygon mit vielen Punkten prüfen, kann das Triangulieren des Polygons die Dinge enorm beschleunigen, da es mehrere sehr schnelle TIN-Suchalgorithmen gibt. Wenn sich das Polygon und die Punkte auf einer begrenzten Ebene mit niedriger Auflösung befinden, beispielsweise eine Bildschirmanzeige, können Sie das Polygon in einen Speicherpuffer mit einer bestimmten Farbe malen und die Farbe eines bestimmten Pixels überprüfen, um festzustellen, ob es liegt in den Polygonen.

Wie bei vielen Optimierungen basieren diese auf spezifischen Fällen und nicht auf allgemeinen Fällen und bringen Nutzen auf Basis der Amortisationszeit und nicht der einmaligen Verwendung.

In diesem Bereich arbeitete ich mit Joeseph O'Rourkes 'Computation Geometry in C' ISBN 0-521-44034-3 als große Hilfe.

5
SmacL

Obj-C-Version von nirgs Antwort mit Mustermethode zum Testen von Punkten. Nirgs Antwort hat für mich gut funktioniert.

- (BOOL)isPointInPolygon:(NSArray *)vertices point:(CGPoint)test {
    NSUInteger nvert = [vertices count];
    NSInteger i, j, c = 0;
    CGPoint verti, vertj;

    for (i = 0, j = nvert-1; i < nvert; j = i++) {
        verti = [(NSValue *)[vertices objectAtIndex:i] CGPointValue];
        vertj = [(NSValue *)[vertices objectAtIndex:j] CGPointValue];
        if (( (verti.y > test.y) != (vertj.y > test.y) ) &&
        ( test.x < ( vertj.x - verti.x ) * ( test.y - verti.y ) / ( vertj.y - verti.y ) + verti.x) )
            c = !c;
    }

    return (c ? YES : NO);
}

- (void)testPoint {

    NSArray *polygonVertices = [NSArray arrayWithObjects:
        [NSValue valueWithCGPoint:CGPointMake(13.5, 41.5)],
        [NSValue valueWithCGPoint:CGPointMake(42.5, 56.5)],
        [NSValue valueWithCGPoint:CGPointMake(39.5, 69.5)],
        [NSValue valueWithCGPoint:CGPointMake(42.5, 84.5)],
        [NSValue valueWithCGPoint:CGPointMake(13.5, 100.0)],
        [NSValue valueWithCGPoint:CGPointMake(6.0, 70.5)],
        nil
    ];

    CGPoint tappedPoint = CGPointMake(23.0, 70.0);

    if ([self isPointInPolygon:polygonVertices point:tappedPoint]) {
        NSLog(@"YES");
    } else {
        NSLog(@"NO");
    }
}

sample polygon

3
Jon

Ich dachte auch, das 360-Summieren funktionierte nur für konvexe Polygone, aber das stimmt nicht.

Diese Seite hat ein schönes Diagramm, das genau dies zeigt, und eine gute Erklärung zum Treffertest: Gamasutra - Absturz ins neue Jahr: Kollisionserkennung

3
Dave

Ich weiß, das ist alt, aber hier ist ein in Cocoa implementierter Ray-Casting-Algorithmus, falls jemand interessiert ist. Nicht sicher, dass dies die effizienteste Art ist, Dinge zu tun, aber es kann jemandem helfen.

- (BOOL)shape:(NSBezierPath *)path containsPoint:(NSPoint)point
{
    NSBezierPath *currentPath = [path bezierPathByFlatteningPath];
    BOOL result;
    float aggregateX = 0; //I use these to calculate the centroid of the shape
    float aggregateY = 0;
    NSPoint firstPoint[1];
    [currentPath elementAtIndex:0 associatedPoints:firstPoint];
    float olderX = firstPoint[0].x;
    float olderY = firstPoint[0].y;
    NSPoint interPoint;
    int noOfIntersections = 0;

    for (int n = 0; n < [currentPath elementCount]; n++) {
        NSPoint points[1];
        [currentPath elementAtIndex:n associatedPoints:points];
        aggregateX += points[0].x;
        aggregateY += points[0].y;
    }

    for (int n = 0; n < [currentPath elementCount]; n++) {
        NSPoint points[1];

        [currentPath elementAtIndex:n associatedPoints:points];
        //line equations in Ax + By = C form
        float _A_FOO = (aggregateY/[currentPath elementCount]) - point.y;  
        float _B_FOO = point.x - (aggregateX/[currentPath elementCount]);
        float _C_FOO = (_A_FOO * point.x) + (_B_FOO * point.y);

        float _A_BAR = olderY - points[0].y;
        float _B_BAR = points[0].x - olderX;
        float _C_BAR = (_A_BAR * olderX) + (_B_BAR * olderY);

        float det = (_A_FOO * _B_BAR) - (_A_BAR * _B_FOO);
        if (det != 0) {
            //intersection points with the edges
            float xIntersectionPoint = ((_B_BAR * _C_FOO) - (_B_FOO * _C_BAR)) / det;
            float yIntersectionPoint = ((_A_FOO * _C_BAR) - (_A_BAR * _C_FOO)) / det;
            interPoint = NSMakePoint(xIntersectionPoint, yIntersectionPoint);
            if (olderX <= points[0].x) {
                //doesn't matter in which direction the ray goes, so I send it right-ward.
                if ((interPoint.x >= olderX && interPoint.x <= points[0].x) && (interPoint.x > point.x)) {  
                    noOfIntersections++;
                }
            } else {
                if ((interPoint.x >= points[0].x && interPoint.x <= olderX) && (interPoint.x > point.x)) {
                     noOfIntersections++;
                } 
            }
        }
        olderX = points[0].x;
        olderY = points[0].y;
    }
    if (noOfIntersections % 2 == 0) {
        result = FALSE;
    } else {
        result = TRUE;
    }
    return result;
}
3
diatrevolo

Die triviale Lösung wäre, das Polygon in Dreiecke zu unterteilen und die Dreiecke zu testen, wie erklärt hier

Wenn Ihr Polygon CONVEX ist, könnte es jedoch eine bessere Annäherung geben. Betrachten Sie das Polygon als eine Sammlung unendlicher Linien. Jede Zeile teilt den Raum in zwei Teile. Für jeden Punkt ist es leicht zu sagen, ob er auf der einen oder der anderen Seite der Linie liegt. Befindet sich ein Punkt auf derselben Seite aller Linien, befindet er sich innerhalb des Polygons. 

3
shoosh

C # -Version von Nirgs Antwort ist hier: Ich werde einfach den Code teilen. Es kann jemandem etwas Zeit sparen.

public static bool IsPointInPolygon(IList<Point> polygon, Point testPoint) {
            bool result = false;
            int j = polygon.Count() - 1;
            for (int i = 0; i < polygon.Count(); i++) {
                if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y) {
                    if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X) {
                        result = !result;
                    }
                }
                j = i;
            }
            return result;
        }
2

Es gibt nichts Schöneres als eine induktive Definition eines Problems. Der Vollständigkeit halber haben Sie hier eine Prolog-Version, die möglicherweise auch die Hintergedanken hinter ray casting verdeutlicht:

Basierend auf der Simulation des Einfachheitsalgorithmus in http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

Einige Helferprädikate:

exor(A,B):- \+A,B;A,\+B.
in_range(Coordinate,CA,CB) :- exor((CA>Coordinate),(CB>Coordinate)).

inside(false).
inside(_,[_|[]]).
inside(X:Y, [X1:Y1,X2:Y2|R]) :- in_range(Y,Y1,Y2), X > ( ((X2-X1)*(Y-Y1))/(Y2-Y1) +      X1),toggle_ray, inside(X:Y, [X2:Y2|R]); inside(X:Y, [X2:Y2|R]).

get_line(_,_,[]).
get_line([XA:YA,XB:YB],[X1:Y1,X2:Y2|R]):- [XA:YA,XB:YB]=[X1:Y1,X2:Y2]; get_line([XA:YA,XB:YB],[X2:Y2|R]).

Die Gleichung einer Linie mit zwei Punkten A und B (Linie (A, B)) lautet: 

                    (YB-YA)
           Y - YA = ------- * (X - XA) 
                    (XB-YB) 

Es ist wichtig, dass die Drehrichtung für die Linie auf Grenzen eingestellt ist Für Löcher wird im Uhrzeigersinn und für Löcher im Uhrzeigersinn festgelegt. Wir werden prüfen, ob der Punkt (X, Y), dh der Der getestete Punkt befindet sich auf der linken Halbebene unserer Linie (es ist Geschmackssache, es könnte auch die rechte Seite sein, aber auch die Richtung der Begrenzungslinien muss geändert werden.) Fall) ist es, den Strahl von rechts nach rechts (oder links) zu projizieren und den Schnittpunkt mit der Linie zu bestätigen. Wir haben uns entschieden, den Strahl in horizontaler Richtung zu projizieren (wiederum ist es eine Frage des Geschmacks; er könnte auch mit ähnlichen Einschränkungen vertikal ausgeführt werden), also haben wir:

               (XB-XA)
           X < ------- * (Y - YA) + XA
               (YB-YA) 

Jetzt müssen wir wissen, ob sich der Punkt auf der linken (oder rechten) Seite von Des Liniensegments und nicht der gesamten Ebene befindet. Daher müssen wir die Suche nur auf dieses Segment beschränken. Dies ist jedoch einfach da um innerhalb des Segments zu sein, kann nur ein Punkt in der Linie höher liegen als __ in der vertikalen Achse. Da dies eine stärkere Einschränkung ist, muss er zuerst überprüft werden. Daher nehmen wir zunächst nur die Zeilen vor, die diese Anforderung erfüllen, und prüfen dann seine Möglichkeiten. Nach dem Jordan-Kurvensatz muss sich jeder auf ein Polygon projizierte Strahl in einer geraden Anzahl von Linien schneiden. Damit sind wir fertig, wir werden den Strahl nach rechts werfen und dann jedes Mal, wenn er eine Linie schneidet, seinen Zustand umschalten. In unserer Implementierung müssen wir jedoch die Länge von Des Lösungsbeutels überprüfen die gegebenen Beschränkungen einhalten und die Innerschaft darüber entscheiden. Für jede Zeile im Polygon muss dies ausgeführt werden.

is_left_half_plane(_,[],[],_).
is_left_half_plane(X:Y,[XA:YA,XB:YB], [[X1:Y1,X2:Y2]|R], Test) :- [XA:YA, XB:YB] =  [X1:Y1, X2:Y2], call(Test, X , (((XB - XA) * (Y - YA)) / (YB - YA) + XA)); 
                                                        is_left_half_plane(X:Y, [XA:YA, XB:YB], R, Test).

in_y_range_at_poly(Y,[XA:YA,XB:YB],Polygon) :- get_line([XA:YA,XB:YB],Polygon),  in_range(Y,YA,YB).
all_in_range(Coordinate,Polygon,Lines) :- aggregate(bag(Line),    in_y_range_at_poly(Coordinate,Line,Polygon), Lines).

traverses_ray(X:Y, Lines, Count) :- aggregate(bag(Line), is_left_half_plane(X:Y, Line, Lines, <), IntersectingLines), length(IntersectingLines, Count).

% This is the entry point predicate
inside_poly(X:Y,Polygon,Answer) :- all_in_range(Y,Polygon,Lines), traverses_ray(X:Y, Lines, Count), (1 is mod(Count,2)->Answer=inside;Answer=outside).
2
jdavid_1385

Java-Version:

public class Geocode {
    private float latitude;
    private float longitude;

    public Geocode() {
    }

    public Geocode(float latitude, float longitude) {
        this.latitude = latitude;
        this.longitude = longitude;
    }

    public float getLatitude() {
        return latitude;
    }

    public void setLatitude(float latitude) {
        this.latitude = latitude;
    }

    public float getLongitude() {
        return longitude;
    }

    public void setLongitude(float longitude) {
        this.longitude = longitude;
    }
}

public class GeoPolygon {
    private ArrayList<Geocode> points;

    public GeoPolygon() {
        this.points = new ArrayList<Geocode>();
    }

    public GeoPolygon(ArrayList<Geocode> points) {
        this.points = points;
    }

    public GeoPolygon add(Geocode geo) {
        points.add(geo);
        return this;
    }

    public boolean inside(Geocode geo) {
        int i, j;
        boolean c = false;
        for (i = 0, j = points.size() - 1; i < points.size(); j = i++) {
            if (((points.get(i).getLongitude() > geo.getLongitude()) != (points.get(j).getLongitude() > geo.getLongitude())) &&
                    (geo.getLatitude() < (points.get(j).getLatitude() - points.get(i).getLatitude()) * (geo.getLongitude() - points.get(i).getLongitude()) / (points.get(j).getLongitude() - points.get(i).getLongitude()) + points.get(i).getLatitude()))
                c = !c;
        }
        return c;
    }

}
2
YongJiang Zhang

VBA-VERSION:

Hinweis: Wenn Ihr Polygon ein Bereich innerhalb einer Karte ist, sind Latitude/Longitude Y/X-Werte im Gegensatz zu X/Y (Latitude = Y, Longitude = X) Längengrad war keine Messung.

KLASSENMODUL: CPoint

Private pXValue As Double
Private pYValue As Double

'''''X Value Property'''''

Public Property Get X() As Double
    X = pXValue
End Property

Public Property Let X(Value As Double)
    pXValue = Value
End Property

'''''Y Value Property'''''

Public Property Get Y() As Double
    Y = pYValue
End Property

Public Property Let Y(Value As Double)
    pYValue = Value
End Property

MODUL:

Public Function isPointInPolygon(p As CPoint, polygon() As CPoint) As Boolean

    Dim i As Integer
    Dim j As Integer
    Dim q As Object
    Dim minX As Double
    Dim maxX As Double
    Dim minY As Double
    Dim maxY As Double
    minX = polygon(0).X
    maxX = polygon(0).X
    minY = polygon(0).Y
    maxY = polygon(0).Y

    For i = 1 To UBound(polygon)
        Set q = polygon(i)
        minX = vbMin(q.X, minX)
        maxX = vbMax(q.X, maxX)
        minY = vbMin(q.Y, minY)
        maxY = vbMax(q.Y, maxY)
    Next i

    If p.X < minX Or p.X > maxX Or p.Y < minY Or p.Y > maxY Then
        isPointInPolygon = False
        Exit Function
    End If


    ' SOURCE: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

    isPointInPolygon = False
    i = 0
    j = UBound(polygon)

    Do While i < UBound(polygon) + 1
        If (polygon(i).Y > p.Y) Then
            If (polygon(j).Y < p.Y) Then
                If p.X < (polygon(j).X - polygon(i).X) * (p.Y - polygon(i).Y) / (polygon(j).Y - polygon(i).Y) + polygon(i).X Then
                    isPointInPolygon = True
                    Exit Function
                End If
            End If
        ElseIf (polygon(i).Y < p.Y) Then
            If (polygon(j).Y > p.Y) Then
                If p.X < (polygon(j).X - polygon(i).X) * (p.Y - polygon(i).Y) / (polygon(j).Y - polygon(i).Y) + polygon(i).X Then
                    isPointInPolygon = True
                    Exit Function
                End If
            End If
        End If
        j = i
        i = i + 1
    Loop   
End Function

Function vbMax(n1, n2) As Double
    vbMax = IIf(n1 > n2, n1, n2)
End Function

Function vbMin(n1, n2) As Double
    vbMin = IIf(n1 > n2, n2, n1)
End Function


Sub TestPointInPolygon()

    Dim i As Integer
    Dim InPolygon As Boolean

'   MARKER Object
    Dim p As CPoint
    Set p = New CPoint
    p.X = <ENTER X VALUE HERE>
    p.Y = <ENTER Y VALUE HERE>

'   POLYGON OBJECT
    Dim polygon() As CPoint
    ReDim polygon(<ENTER VALUE HERE>) 'Amount of vertices in polygon - 1
    For i = 0 To <ENTER VALUE HERE> 'Same value as above
       Set polygon(i) = New CPoint
       polygon(i).X = <ASSIGN X VALUE HERE> 'Source a list of values that can be looped through
       polgyon(i).Y = <ASSIGN Y VALUE HERE> 'Source a list of values that can be looped through
    Next i

    InPolygon = isPointInPolygon(p, polygon)
    MsgBox InPolygon

End Sub
1
Colin Stadig

. NET-Port:

    static void Main(string[] args)
    {

        Console.Write("Hola");
        List<double> vertx = new List<double>();
        List<double> verty = new List<double>();

        int i, j, c = 0;

        vertx.Add(1);
        vertx.Add(2);
        vertx.Add(1);
        vertx.Add(4);
        vertx.Add(4);
        vertx.Add(1);

        verty.Add(1);
        verty.Add(2);
        verty.Add(4);
        verty.Add(4);
        verty.Add(1);
        verty.Add(1);

        int nvert = 6;  //Vértices del poligono

        double testx = 2;
        double testy = 5;


        for (i = 0, j = nvert - 1; i < nvert; j = i++)
        {
            if (((verty[i] > testy) != (verty[j] > testy)) &&
             (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
                c = 1;
        }
    }
1
Aladar

Ich habe eine Python-Implementierung von nirg's c ++ code gemacht:

Eingaben

  • bounding_points: Knoten, aus denen das Polygon besteht.
  • bounding_box_positions: zu filternde Kandidatenpunkte. (In meiner Implementierung aus dem Begrenzungsrahmen erstellt.

    (Die Eingaben sind Listen von Tupeln im Format: [(xcord, ycord), ...])

Kehrt zurück

  • Alle Punkte, die sich innerhalb des Polygons befinden. 
def polygon_ray_casting(self, bounding_points, bounding_box_positions):
    # Arrays containing the x- and y-coordinates of the polygon's vertices.
    vertx = [point[0] for point in bounding_points]
    verty = [point[1] for point in bounding_points]
    # Number of vertices in the polygon
    nvert = len(bounding_points)
    # Points that are inside
    points_inside = []

    # For every candidate position within the bounding box
    for idx, pos in enumerate(bounding_box_positions):
        testx, testy = (pos[0], pos[1])
        c = 0
        for i in range(0, nvert):
            j = i - 1 if i != 0 else nvert - 1
            if( ((verty[i] > testy ) != (verty[j] > testy))   and
                    (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]) ):
                c += 1
        # If odd, that means that we are inside the polygon
        if c % 2 == 1: 
            points_inside.append(pos)


    return points_inside

Wieder wird die Idee von hier übernommen.

1
Noresourses

Überrascht hat dies niemand früher angesprochen, aber für die Pragmatiker, die eine Datenbank benötigen: MongoDB bietet hervorragende Unterstützung für Geo-Abfragen einschließlich dieser.

Was Sie suchen, ist:

db.neighborhoods.findOne ({Geometrie: {$ geoIntersects: {$ Geometrie: { Typ: "Punkt", Koordinaten: ["Längengrad", "Breitengrad"]}} })

Neighborhoods ist die Sammlung, in der ein oder mehrere Polygone im Standard-GeoJson-Format gespeichert werden. Wenn die Abfrage null zurückgibt, wird sie nicht durchgeschnitten.

Sehr gut dokumentiert hier: https://docs.mongodb.com/manual/tutorial/geospatial-tutorial/

Die Leistung für mehr als 6.000 Punkte, die in einem 330 unregelmäßigen Polygonraster klassifiziert wurden, betrug weniger als eine Minute, ohne dass überhaupt eine Optimierung vorgenommen wurde, einschließlich der Zeit, um Dokumente mit ihrem jeweiligen Polygon zu aktualisieren. 

Um mit den folgenden speziellen Fällen im Ray Casting-Algorithmus fertigzuwerden:

  1. Der Strahl überlappt eine der Polygonseiten.
  2. Der Punkt befindet sich innerhalb des Polygons und der Strahl verläuft durch einen Scheitelpunkt des Polygons.
  3. Der Punkt liegt außerhalb des Polygons und der Strahl berührt nur einen Winkel des Polygons.

Prüfen Bestimmen, ob sich ein Punkt in einem komplexen Polygon befindet . Der Artikel bietet eine einfache Möglichkeit, sie zu lösen, sodass für die oben genannten Fälle keine besondere Behandlung erforderlich ist.

0
Justin

Die Scala-Version der Lösung von nirg (setzt voraus, dass die Vorprüfung des Begrenzungsrechtecks ​​separat durchgeführt wird):

def inside(p: Point, polygon: Array[Point], bounds: Bounds): Boolean = {

  val length = polygon.length

  @tailrec
  def oddIntersections(i: Int, j: Int, tracker: Boolean): Boolean = {
    if (i == length)
      tracker
    else {
      val intersects = (polygon(i).y > p.y) != (polygon(j).y > p.y) && p.x < (polygon(j).x - polygon(i).x) * (p.y - polygon(i).y) / (polygon(j).y - polygon(i).y) + polygon(i).x
      oddIntersections(i + 1, i, if (intersects) !tracker else tracker)
    }
  }

  oddIntersections(0, length - 1, tracker = false)
}
0
Michael-7

Die Antwort hängt davon ab, ob Sie einfache oder komplexe Polygone haben. Einfache Polygone dürfen keine Schnittpunkte von Liniensegmenten haben. Sie können also Löcher haben, aber Linien können sich nicht kreuzen. Komplexe Regionen können die Schnittpunkte der Linien haben. Sie können also überlappende Regionen oder Regionen haben, die sich nur durch einen einzigen Punkt berühren.

Für einfache Polygone ist der beste Algorithmus der Ray-Casting-Algorithmus. Bei komplexen Polygonen erkennt dieser Algorithmus keine Punkte, die sich in den überlappenden Bereichen befinden. Für komplexe Polygone müssen Sie daher den Algorithmus der Windungszahl verwenden.

Hier ist ein hervorragender Artikel mit C-Implementierung beider Algorithmen. Ich habe sie ausprobiert und sie funktionieren gut.

http://geomalgorithms.com/a03-_inclusion.html

0
Timmy_A

Um den Treffer auf Polygon zu ermitteln, müssen wir zwei Dinge testen:

  1. Wenn Punkt innerhalb des Polygonbereichs liegt. (kann durch Ray-Casting-Algorithmus erreicht werden)
  2. Wenn Punkt sich auf dem Polygonrand befindet (kann mit demselben Algorithmus durchgeführt werden, der für die Punkterkennung auf einer Polylinie (Linie) verwendet wird).
0
V.J.

Hier ist ein Punkt im Polygontest in C, bei dem kein Raycasting verwendet wird. Und es kann für überlappende Bereiche (Selbstschnittpunkte) funktionieren, siehe das Argument use_holes.

/* math lib (defined below) */
static float dot_v2v2(const float a[2], const float b[2]);
static float angle_signed_v2v2(const float v1[2], const float v2[2]);
static void copy_v2_v2(float r[2], const float a[2]);

/* intersection function */
bool isect_point_poly_v2(const float pt[2], const float verts[][2], const unsigned int nr,
                         const bool use_holes)
{
    /* we do the angle rule, define that all added angles should be about zero or (2 * PI) */
    float angletot = 0.0;
    float fp1[2], fp2[2];
    unsigned int i;
    const float *p1, *p2;

    p1 = verts[nr - 1];

    /* first vector */
    fp1[0] = p1[0] - pt[0];
    fp1[1] = p1[1] - pt[1];

    for (i = 0; i < nr; i++) {
        p2 = verts[i];

        /* second vector */
        fp2[0] = p2[0] - pt[0];
        fp2[1] = p2[1] - pt[1];

        /* dot and angle and cross */
        angletot += angle_signed_v2v2(fp1, fp2);

        /* circulate */
        copy_v2_v2(fp1, fp2);
        p1 = p2;
    }

    angletot = fabsf(angletot);
    if (use_holes) {
        const float nested = floorf((angletot / (float)(M_PI * 2.0)) + 0.00001f);
        angletot -= nested * (float)(M_PI * 2.0);
        return (angletot > 4.0f) != ((int)nested % 2);
    }
    else {
        return (angletot > 4.0f);
    }
}

/* math lib */

static float dot_v2v2(const float a[2], const float b[2])
{
    return a[0] * b[0] + a[1] * b[1];
}

static float angle_signed_v2v2(const float v1[2], const float v2[2])
{
    const float perp_dot = (v1[1] * v2[0]) - (v1[0] * v2[1]);
    return atan2f(perp_dot, dot_v2v2(v1, v2));
}

static void copy_v2_v2(float r[2], const float a[2])
{
    r[0] = a[0];
    r[1] = a[1];
}

Hinweis: Dies ist eine der weniger optimalen Methoden, da sie viele Aufrufe von atan2f enthält. Dies kann jedoch für Entwickler von Interesse sein, die diesen Thread lesen.

0
ideasman42

Wenn Sie nach einer Java-Skriptbibliothek suchen, gibt es für die Polygon-Klasse eine JavaScript-Erweiterung für Google Maps v3, mit der Sie feststellen können, ob sich ein Punkt darin befindet. 

var polygon = new google.maps.Polygon([], "#000000", 1, 1, "#336699", 0.3);
var isWithinPolygon = polygon.containsLatLng(40, -90);

Google Extention Github

0
shana

Hier ist die Golang-Version von @nirg answer (inspiriert durch C # -Code von @@ m-katz)

func isPointInPolygon(polygon []point, testp point) bool {
    minX := polygon[0].X
    maxX := polygon[0].X
    minY := polygon[0].Y
    maxY := polygon[0].Y

    for _, p := range polygon {
        minX = min(p.X, minX)
        maxX = max(p.X, maxX)
        minY = min(p.Y, minY)
        maxY = max(p.Y, maxY)
    }

    if testp.X < minX || testp.X > maxX || testp.Y < minY || testp.Y > maxY {
        return false
    }

    inside := false
    j := len(polygon) - 1
    for i := 0; i < len(polygon); i++ {
        if (polygon[i].Y > testp.Y) != (polygon[j].Y > testp.Y) && testp.X < (polygon[j].X-polygon[i].X)*(testp.Y-polygon[i].Y)/(polygon[j].Y-polygon[i].Y)+polygon[i].X {
            inside = !inside
        }
        j = i
    }

    return inside
}
0
SamTech

Wenn Sie qt (Qt 4.3+) verwenden, können Sie die Funktion von QPolygon containsPoint verwenden.

0
Peter

Sie können dies tun, indem Sie prüfen, ob der durch Verbinden des gewünschten Punkts mit den Scheitelpunkten Ihres Polygons gebildete Bereich mit dem Bereich des Polygons selbst übereinstimmt. 

Oder Sie können überprüfen, ob die Summe der Innenwinkel von Ihrem Punkt zu jedem Paar von zwei aufeinanderfolgenden Polygonscheitelpunkten zu Ihren Prüfpunktsummen bis 360, aber ich habe das Gefühl, dass die erste Option schneller ist, weil es keine Divisionen oder Berechnungen gibt der Umkehrung trigonometrischer Funktionen.

Ich weiß nicht, was passiert, wenn Ihr Polygon ein Loch enthält, aber es scheint mir, dass die Grundidee an diese Situation angepasst werden kann 

Sie können die Frage auch in einer Mathe-Community posten. Ich wette, sie haben eine Million Möglichkeiten, das zu tun

0
user5193682