Antipodaler Abstand (oder Polygonabruf oder Polygondurchmesser) für konkave Polygone


8

Ich arbeite an einigen Klassenbeispielen in Python, die in ArcMap implementiert sind, um den antipodalen Abstand innerhalb eines Polygons zu berechnen. Dies ist für konvexe Polygone ziemlich routinemäßig. Für konkave Polygone möchte ich jedoch Lösungen ausschließen (die durch einen Strahl gebildet werden, der die Grenzpunkte verbindet), die nicht vollständig innerhalb des Polygons und nicht an der Polygongrenze liegen oder diese schneiden. Habe ich die Definition falsch interpretiert oder hat dieses Biest einen anderen Namen?

Betrachten Sie diese beiden Polygone

pnts = [[0,0], [0,1], [1,4], [3,5], [5,4], [4,1], [0,0]] # eine geschlossene Schleife konvex

pnts = [[0,0], [2,1], [1,4], [3,5], [5,4], [4,1], [0,0]] # eine geschlossene Schleife konkaves Polygon

In meiner Interpretation sollte dem Punkt 0,0 kein antipodaler Abstand zugeordnet sein, da der Vektor, der ihn mit den anderen Punkten verbindet, das Polygon entweder selbst schneidet oder sich an der Polygongrenze befindet.

Wenn jemand Klarheit über die Definition oder mögliche Lösungen hat, würde ich es begrüßen.

Eine visuelle Darstellung des konvexen Polygons und der gewünschten Linien (rot dargestellt) ist beigefügt (Beispielvektoren von Punkt 0 werden nur angezeigt).

Beispiel für einen antipodalen Innenabstand

Im konvexen Beispiel hat der erste Punkt keine antipodalen Vektoren, der zweite Punkt jedoch.

Konkaves antipodales Beispiel

BEARBEITEN Ich hatte einige Erfolge bei der Suche mit "Polygonabruf" oder "Polygondurchmesser" im Web. Ich vermute, dass dies das ist, wonach ich suche.


1
Hallo Dan. Welche Definition von "antipodaler Entfernung" verwenden Sie? Eine Möglichkeit wäre der am weitesten entfernte Punkt, gemessen durch Reisen entlang der Polygongrenze, aber das scheint nicht mit Ihrer Beschreibung übereinzustimmen. Eine andere Definition ist der am weitesten entfernte Punkt, an dem eine Bewegung innerhalb oder außerhalb des Polygons stattfinden kann. Ein dritter Punkt ist jedoch der am weitesten entfernte Punkt, an dem Reisen nur innerhalb des Inneren und der Grenze des Polygons zulässig sind.
whuber

1
@whuber, ich suchte nach einer Lösung, die nur innerhalb des Polygons mit Ausnahme von Liniensegmenten wanderte, die die Polygongrenze bilden. In dem konvexen Beispiel, das ich gegeben habe, wäre eine Bewegung von den Punkten p0 nach p1 oder p0 nach p5 nicht zulässig, da sie Teil der Polygonkante sind, jedoch wären p0 nach p2, p3, p4. Daher meine Sorge, dass "antipodal" möglicherweise nicht der richtige Begriff ist. Beachten Sie, dass ich mich momentan nur für einteilige konvexe Polygone ohne Löcher interessiere. Wenn ich mit Kantensegmenten in der Lösung stecken bleibe, kann ich sie später jederzeit entfernen.

1
Hier gibt es ein heikles Problem, Dan: Obwohl solche Segmente möglicherweise ausgeschlossen sind, sagen sie Ihnen dennoch, wie hoch das Infimum aller möglichen Entfernungen sein wird (sie verhindern lediglich, dass dieses Infimum tatsächlich realisiert wird). Praktische Lösungen würden im Inneren dieser Segmente bleiben, aber unendlich nahe bei ihnen bleiben. Für konvexe Polygone ist der Algorithmus daher einfach: Finden Sie einen Scheitelpunkt, der am weitesten vom Startpunkt entfernt ist (es kann viele davon geben: Stellen Sie sich einen Halbkreis vor und beginnen Sie in der Mitte des ursprünglichen Kreises).
whuber

1
Ich verstehe Ihre Definition immer noch nicht, Dan, weil es in keinem Polygon einen "längsten Pfad" gibt: Sie können sich herumschlängeln, um beliebig lange Pfade zu erstellen. Möglicherweise beabsichtigen Sie Folgendes: Definieren Sie den Abstand zwischen den Punkten P und Q in einem (verbundenen) Polygon als das Infimum der Längen aller Pfade von P nach Q, die vollständig innerhalb des Polygons liegen. Dann wäre ein plausibler "Antipode" für ein kompakt verbundenes Polygon P ein beliebiger Punkt Q im maximalen Abstand von P. (Wenn P ein Scheitelpunkt eines konvexen Polygons ist, sind seine Antipoden wieder Eckpunkte im maximalen euklidischen Abstand von P.)
whuber

2
Der am weitesten entfernte Punkt wird unter Verwendung der "plausiblen" Definition in meinem vorhergehenden Kommentar rigoros charakterisiert. Beachten Sie, dass es bei der Suche nach Sie dürfen davon ausgehen , dass Sie kann entlang der Kanten reisen. In Ihrer zweiten Figur ist E antipodal zu A und B; A ist antipodal zu C, D und E; und D und A sind beide antipodal zu F. Unter Verwendung der Kanu-Analogie, bei der das Innere des Polygons ein See ist, ist ein Punkt P antipodal zu Ihrem Startpunkt Q, wenn Sie in einem Kanurennen von Q gegen einen Gegner antreten, der P erreichen möchte Bevor Sie einen Punkt P 'erreichen, haben sie keinen Vorteil gegenüber Ihnen, egal wo P' ist.
whuber

Antworten:


4

Wenn ich einen Algorithmus schreiben würde, würde ich einfach prüfen, ob eine Linie zwischen zwei Eckpunkten des Polygons eine Linie schneidet, die eine der Kanten bildet. Hier ist mein Pseudocode:

  1. Identifizieren Sie alle Eckpunkte und speichern Sie sie in einer Liste
  2. Identifizieren Sie alle Kanten und speichern Sie sie in einer Liste (möglicherweise als Scheitelpunktpaare von 1).
  3. Ermitteln Sie für jeden Scheitelpunkt den Abstand zu allen anderen Scheitelpunkten, außer:
    1. benachbarte Scheitelpunkte ausschließen (diejenigen, die eine Paarung mit diesem Scheitelpunkt in 2 teilen, vielleicht)
    2. Schließen Sie jede Linie aus, die eine Linie in 2. schneidet, indem Sie etwas von hier verwenden .
  4. Speichern Sie alle Validabstände in Bezug auf die Eckpunkte in 1.

  5. Machen Sie mit den Ergebnissen, was immer Sie wollen, schreiben Sie neue Zeilen aus, speichern Sie die längste für jedes Polygon ...

Ich bin mir nicht sicher, ob Sie danach suchen, aber Sie können die oben genannten Schritte in ArcPy ausführen.

EDIT: Code für Schritt 2.2:

E = B-A = ( Bx-Ax, By-Ay )
F = D-C = ( Dx-Cx, Dy-Cy ) 
P = ( -Ey, Ex )
h = ( (A-C) * P ) / ( F * P )

Wenn h zwischen 0 und 1 liegt, schneiden sich die Linien, andernfalls nicht. Wenn F * P Null ist, können Sie die Berechnung natürlich nicht durchführen, aber in diesem Fall sind die Linien parallel und schneiden sich daher nur in den offensichtlichen Fällen. Wenn h 1 ist, enden die Linien am selben Punkt. Behandle das so wie du willst! (Ich würde sagen, sie kreuzen sich, es macht mich einfacher.)

Ein weiteres Beispiel für Schritt 2.2 von hier: http://en.wikipedia.org/wiki/Line-line_intersection Geben Sie hier die Bildbeschreibung ein

Überprüfen Sie zunächst, ob der Nenner nicht gleich 0 ist, was bedeutet, dass die Linien parallel sind.

Stellen Sie dann sicher, dass die oben angegebenen Koordinaten nicht außerhalb des Begrenzungsrahmens der beiden Linien liegen.

Weitere Informationen: http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf


Der Code in meinem Blog macht alles außer 3.2 und verweist das Ergebnis für weitere Berechnungen, die für konvexe Polygone gut funktionieren, an das aufrufende Programm zurück. Ich möchte nach Möglichkeit einige Referenzen und ob die Wicklungsnummer effizient wäre, um Linienkreuzungen zu bestimmen, oder ob ich eine andere Route gehen sollte.

2
Ich habe ein Beispiel für die Schnittpunktberechnung aus dem Artikel hinzugefügt, auf den ich verlinkt habe. Ich denke, ich würde mir keine Sorgen um die Effizienz machen, bis es ein Problem war! Lass etwas funktionieren und repariere es, wenn es nicht gut genug ist!
Alex Leith

Danke Alex, ich werde es überprüfen ... Effizienz ist kein Problem, da dies nur ein Beispiel ist, das nicht auf Tausenden von Polygonen ausgeführt werden soll

Obwohl es schwer zu sagen ist, was diese Antwort beschreibt, scheint es eine Überprüfung zu sein, ob ein Polygon konvex ist. Es ist besonders verwirrend, dass "Linie" anstelle von "Liniensegment" verwendet wird. Der angegebene Code scheint keine gültige Prüfung für den Schnittpunkt von Liniensegmenten zu sein.
whuber

Hallo whuber, dies prüft Liniensegmente. Das zweite Beispiel, das ich hinzugefügt habe, macht es vielleicht klarer, indem die Mathematik den Schnittpunkt für unendliche Linien berechnet und Sie dann überprüfen müssen, ob sich dieser Schnittpunkt innerhalb des Begrenzungsrahmens einer der Linien befindet. Wie bei jeder Vektormathematik gibt es sicherlich eine Bibliothek, die dies tut, aber es ist nicht so komplex, und ich denke, es ist notwendig für das, was OP tun möchte.
Alex Leith

3

Ich wäre versucht, dies mit Engeln zu tun, fast wie eine Sichtlinie. Wenn beim Iterieren der Scheitelpunkte in der Form die Winkel zwischen dem Ursprungsscheitelpunkt und dem Zielscheitelpunkt in einer konsistenten Richtung fortgesetzt werden, sind alle Punkte Kandidaten für das Antipodal. Wenn ein Winkel die Richtung wechselt, wird dieser Punkt durch den vorherigen Punkt ausgeblendet oder ausgeblendet. Wenn es durch den vorherigen Punkt ausgeblendet wird, muss der Punkt übersprungen werden. Wenn der vorherige Punkt ausgeblendet wird, müssen die vorherigen Punkte aus der Kandidatenliste entfernt werden.

  1. Erstellen Sie eine PolygonCandidates-Liste
  2. Für jeden Scheitelpunkt (Punkt k)
    1. Neue Liste für Kandidaten erstellen (Punkt, Winkel)
    2. Fügen Sie den aktuellen Scheitelpunkt zur Kandidatenliste hinzu (Punkt k)
    3. Iterieren Sie im Uhrzeigersinn um das Polygon für jeden verbleibenden Scheitelpunkt (Punkt i).
      1. Wenn sich der Winkel zum aktuellen Punkt (von Punkt k zu Punkt i) im Uhrzeigersinn fortsetzt, fügen Sie den Punkt hinzu
      2. Wenn sich der Winkel zum aktuellen Punkt gegen den Uhrzeigersinn fortsetzt
      3. Wenn die beiden vorherigen Kandidatenpunkte plus der aktuelle Punkt eine Rechtskurve bilden.
      4. Entfernen Sie den letzten Punkt in der Liste, bis der aktuelle Winkel und der letzte Kandidatenlistenwinkel gegen den Uhrzeigersinn liegen.
      5. Fügen Sie den aktuellen Punkt zur Kandidatenliste hinzu
    4. Fügen Sie alle bis auf die ersten beiden und letzten Kandidatenpunkte zu einer PolygonCandidates-Liste hinzu
  3. Suchen Sie den am weitesten entfernten Punkt in der Liste der PolygonCandidates.

Ich bin mir nicht sicher, was ich mit Fällen tun soll, in denen der Ursprung und zwei andere Eckpunkte alle entlang derselben Linie liegen. In diesem Fall wäre der Winkel der gleiche. Wenn Sie ein Polygon mit Löchern hatten, konnten Sie den minimalen / maximalen Winkel jedes Lochs ermitteln und alle Kandidatenpunkte entfernen, die innerhalb dieses Bereichs liegen.

Der Hauptvorteil dieses Ansatzes besteht darin, dass Sie nicht auf den Linienschnittpunkt zwischen dem aktuellen Liniensegment und allen Polygonkanten testen müssen.

Das funktioniert ... denke ich. Ich habe den obigen Pseudocode und die Python aktualisiert, um das Lesen zu erleichtern.


Dies sollte die letzte Bearbeitung sein. Im folgenden Beispiel sollte der größte Anitpole für eine bestimmte Geometrie gefunden werden. Ich habe den Scrip so geändert, dass Punkte und Vektoren verwendet werden, um das Lesen zu erleichtern.

import math
from collections import namedtuple


Point = namedtuple("Point", "position x y")
Vector = namedtuple("Vector", "source dest angle")

def isClockwise(angle1, angle2):
    diff = angle2 - angle1
    #print("         angle1:%s angle2:%s diff: %s" % (angle1, angle2, diff))
    if(diff > math.pi/2):
        diff = diff - math.pi/2
    elif (diff < -math.pi/2):
        diff = diff + math.pi/2
    #print("         diff:%s" % (diff)) 
    if(diff > 0):
        return False
    return True

def getAngle(origin, point):
    return math.atan2(point.y - origin.y, point.x-origin.x)

#returns a list of candidate vertcies.  This will include the first, second, and second to last points 
#the first and last points in the polygon must be the same
#k is the starting position, only vertices after this position will be evaluated
def getCandidates (k, polygon):

    origin = polygon[k]
    candidates = [Vector(k,k,0)]
    prevAngle = 0;
    currentAngle = 0;
    for i in range(k + 1, len(polygon) - 1):

        current = polygon[i]
        #print("vertex i:%s x:%s y:%s  " % (i, current.x, current.y))

        if(i == k+1):
            prevAngle = getAngle(origin, current)
            candidates.append(Vector(k,i,prevAngle))
        else:   
            currentAngle = getAngle(origin, current)
            #print("     prevAngle:%s currentAngle:%s  " % (prevAngle, currentAngle))
            if isClockwise(prevAngle, currentAngle):
                #print("     append")
                candidates.append(Vector(k,i,currentAngle))
                prevAngle = currentAngle
            else:
                #look at the angle between current, candidate-1 and candidate-2
                if(i >= 2):
                    lastCandinate = polygon[candidates[len(candidates) - 1].dest]
                    secondLastCandidate = polygon[candidates[len(candidates) - 2].dest]
                    isleft = ((lastCandinate.x - secondLastCandidate.x)*(current.y - secondLastCandidate.y) - (lastCandinate.y - secondLastCandidate.y)*(current.x - secondLastCandidate.x)) > 0
                    #print("     test for what side of polygon %s" % (isleft))
                    if(i-k >= 2 and not isleft):
                        while isClockwise(currentAngle, candidates[len(candidates) - 1].angle):
                            #print("     remove %s" % (len(candidates) - 1))
                            candidates.pop()
                        #print("     append (after remove)")
                        candidates.append(Vector(k,i,currentAngle))
                        prevAngle = currentAngle

        #for i in range(len(candidates)):
        #   print("candidate i:%s x:%s y:%s a:%s " % (candidates[i][0], candidates[i][1], candidates[i][2], candidates[i][3]))

    return candidates

def calcDistance(point1, point2):
    return math.sqrt(math.pow(point2.x - point1.x, 2) + math.pow(point2.y - point1.y, 2))

def findMaxDistance(polygon, candidates):
    #ignore the first 2 and last result
    maxDistance = 0
    maxVector = Vector(0,0,0);
    for i in range(len(candidates)):
        currentDistance = calcDistance(polygon[candidates[i].source], polygon[candidates[i].dest])
        if(currentDistance > maxDistance):
            maxDistance = currentDistance
            maxVector = candidates[i];
    if(maxDistance > 0):
        print ("The Antipodal distance is %s from %s to %s" % (maxDistance, polygon[candidates[i].source], polygon[candidates[i].dest]))
    else:
        print ("There is no Antipodal distance")

def getAntipodalDist(polygon):
    polygonCandidates = []
    for j in range(0, len(polygon) - 1):
        candidates = getCandidates(j, polygon)
        for i in range(2, len(candidates) - 1):
            #print("candidate i:%s->%s x:%s y:%s  " % (candidates[i].source, candidates[i].dest, candidates[i].x, candidates[i].y))
            polygonCandidates.append(candidates[i])

    for i in range(len(polygonCandidates)):
        print("candidate i:%s->%s" % (polygonCandidates[i].source, polygonCandidates[i].dest))
    findMaxDistance(polygon, polygonCandidates)


getAntipodalDist([Point(0,0,0),Point(1,-2,0),Point(2,-2,3),Point(3,2,2),Point(4,-1,1),Point(5,4,0),Point(6,0,0)])
getAntipodalDist([Point(0,0,0),Point(1,2,1),Point(2,1,4),Point(3,3,5),Point(4,5,4),Point(5,4,1),Point(6,0,0)])
getAntipodalDist([Point(0,0,0),Point(1,1,1),Point(2,2,1),Point(3,1,4),Point(4,3,5),Point(5,5,4),Point(6,4,1),Point(7,0,0)])
getAntipodalDist([Point(0,0,0),Point(1,-1,3),Point(2,1,4),Point(3,3,3),Point(4,2,0),Point(5,-2,-1),Point(6,0,0)])

Funktioniert dieser Algorithmus wirklich? Könnten Sie es vielleicht mit einem einfachen Beispiel veranschaulichen, wie dem Sechseck ((0,0), (- 2,0), (- 2,3), (2,2), (- 1,1), (4)? , 0), (0,0))? Was passiert, wenn Sie bei (0,0) beginnen? Und was soll dieser Algorithmus tun? Beispielsweise wird das längste Liniensegment im Polygon nicht gefunden (Länge 1,2 * sqrt (26)).
whuber

Vielen Dank für den Kommentar Travis, dies funktioniert jedoch nicht in allen Fällen (siehe Beispiel für einen konkaven Rumpf), da isRightTurn (A, B, C) falsch wäre und AC kein Kandidatensegment wäre. Wenn B weiter nördlich wäre, könnte es möglicherweise alles für ein Segment AE sein, so dass ich Punkt A nicht vollständig ausschließen möchte, bis alle anderen Punkte überprüft wurden.

@whuber, angesichts dieser Geometrie sehe ich nicht, wie das längste Liniensegment 1,2 * sqrt (26) ist. Es sei denn, ich habe völlig übersehen, worum es bei dieser Frage geht. Wäre es nicht sqrt (2), entweder von (0,0) -> (- 1,1) oder (-2,0) -> (- 1,1).
Travis

1
@ DanPatterson, ich habe möglicherweise verpasst, was Sie fragen. Mein Verständnis war: Was ist der größte Abstand zwischen einem bestimmten Scheitelpunkt und einem anderen Scheitelpunkt, der die Grenze des Polygons nicht schneidet? Ich habe mein Skript aktualisiert, um die maximale Entfernung des Polygons zu ermitteln.
Travis

Konvexe Polygone scheinen angesichts der simplen Beispiele, die man im Web und in Texten findet, kein Problem zu sein. Der Polygondurchmesser für konkave Rümpfe scheint verschiedene Interpretationen und Polygonabrufe zu haben. Ich beginne jetzt, einen weiteren Fischkessel zu erkennen. Auf jeden Fall ist es das, wonach ich strebe, wenn ich mich nicht überschneide. Mein Anliegen ist mein Mangel an klaren Definitionen und Beispielen mit realen Beispielen. Ich kann die konvexen behandeln, aber die konkaven erweisen sich als problematisch und gehen über meine mathematischen / rechnerischen Kenntnisse hinaus, wie dies durch einige von Bills Vorschlägen unterstützt / hervorgehoben wird.

1

Ziehen Sie möglicherweise in Betracht, den Datensatz zu triangulieren. Welche Linien, die Polygonkanten gemeinsam haben, lassen sich leicht feststellen, und die übrigen können verglichen werden, um die längsten zu finden? Die Frage ist dann, welchen Triangulationsalgorithmus Sie benötigen.

Es ist nur eine Vermutung, aber ich vermute (ironischerweise), dass die Triangulation "niedrigster Qualität", die man erstellen kann, die gesuchte Linie enthalten muss, z. B. Abb. 1 in https://www.google.co.uk/url?sa=t&rct= j & q = & ESCR = s & source = web & cd = 6 & ved = 0CEoQFjAF & url = http% 3A% 2F% 2Fhrcak.srce.hr% 2Ffile% 2F69457 & ei = alIcUsb6HsLnswbfnYHoDw & USG = AFQjCNHIaykVRBAvv9hlaFJIBlfPLGHKtQ


Mein Code in meinem Blog ist effektiver als die Terminologie, die ich klären muss, sowie was bei einem konkaven Rumpf zu tun ist.

Eine Triangulation behandelt von Natur aus eine konkave Hülle (sofern keine Dreieckskanten entstehen, die eine Polygongrenze überschreiten)
AnserGIS
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.