Gibt es ein ArcPy-Werkzeug für die Größenänderung von Polygonen wie das Skalierungswerkzeug der Symbolleiste "Erweiterte Bearbeitung" in ArcMap?

17

Ich schreibe ein Python-Skript für ArcGIS 10.3. Ich kenne die Scale toolArcGIS-Benutzeroberfläche, kann aber einen solchen arcpy-Befehl nicht finden. Es existiert?

Wie Sie auf dem Bild sehen können, Scale toolfunktioniert das anders als Buffer tool- es ändert die Form des ursprünglichen Polygons. Die Frage ist also:

Kann ich Scale toolarcpy verwenden (über die ArcGIS-Benutzeroberfläche verfügbar)?

Bildbeschreibung hier eingeben

Mr. Che
quelle
2
Wie wäre es mit dem Puffern und Entfernen des alten Polygons? puffer kann mit positiven und negativen werten verwendet werden!
Farid Cheraghi
Die Frage bezieht sich auf das vorhandene Arcpy-Tool und nicht darauf, wie die Größe eines Polygons geändert werden kann.
Herr Che
Ihr Titel, Ihre Frage und Ihr Kommentar scheinen sich zu widersprechen. Wenn die bereitgestellten doppelten Fragen Ihre Frage nicht beantworten, können Sie Ihre Frage bitte bearbeiten, um zu verdeutlichen, wonach Sie suchen?
Aaron
1
@ Mr.Che Buffer-Tool kann in Python- Skripten
Farid Cheraghi
Das ist super! Wie kann ich jede Feature-Class anhand einer Nummer in einer Tabelle aktualisieren, anstatt beispielsweise alle Features auf 0,5 zu skalieren? Thanks
user1655130

Antworten:

27

Mir ist nichts in der arcpy-API bekannt, das die Skalierung für Sie übernimmt, aber das Schreiben einer entsprechenden Funktion wäre relativ einfach.

Der folgende Code führt die Skalierung für 2D-Features durch und berücksichtigt keine M- oder Z-Werte:

import arcpy
import math

def scale_geom(geom, scale, reference=None):
    """Returns geom scaled to scale %"""
    if geom is None: return None
    if reference is None:
        # we'll use the centroid if no reference point is given
        reference = geom.centroid

    refgeom = arcpy.PointGeometry(reference)
    newparts = []
    for pind in range(geom.partCount):
        part = geom.getPart(pind)
        newpart = []
        for ptind in range(part.count):
            apnt = part.getObject(ptind)
            if apnt is None:
                # polygon boundaries and holes are all returned in the same part.
                # A null point separates each ring, so just pass it on to
                # preserve the holes.
                newpart.append(apnt)
                continue
            bdist = refgeom.distanceTo(apnt)

            bpnt = arcpy.Point(reference.X + bdist, reference.Y)
            adist = refgeom.distanceTo(bpnt)
            cdist = arcpy.PointGeometry(apnt).distanceTo(bpnt)

            # Law of Cosines, angle of C given lengths of a, b and c
            angle = math.acos((adist**2 + bdist**2 - cdist**2) / (2 * adist * bdist))

            scaledist = bdist * scale

            # If the point is below the reference point then our angle
            # is actually negative
            if apnt.Y < reference.Y: angle = angle * -1

            # Create a new point that is scaledist from the origin 
            # along the x axis. Rotate that point the same amount 
            # as the original then translate it to the reference point
            scalex = scaledist * math.cos(angle) + reference.X
            scaley = scaledist * math.sin(angle) + reference.Y

            newpart.append(arcpy.Point(scalex, scaley))
        newparts.append(newpart)

    return arcpy.Geometry(geom.type, arcpy.Array(newparts), geom.spatialReference)

Sie können es mit einem Geometrieobjekt, einem Skalierungsfaktor (1 = gleiche Größe, 0,5 = halbe Größe, 5 = 5-mal so groß usw.) und einem optionalen Referenzpunkt aufrufen:

scale_geom(some_geom, 1.5)

Verwenden Sie dies in Verbindung mit Cursorn, um eine gesamte Feature-Class zu skalieren, sofern die Ziel-Feature-Class bereits vorhanden ist:

incur = arcpy.da.SearchCursor('some_folder/a_fgdb.gdb/orig_fc', ['OID@','SHAPE@'])
outcur = arcpy.da.InsertCursor('some_folder/a_fgdb.gdb/dest_fc', ['SHAPE@'])

for row in incur:
    # Scale each feature by 0.5 and insert into dest_fc
    outcur.insertRow([scale_geom(row[1], 0.5)])
del incur
del outcur

Bearbeiten: Hier ist ein Beispiel mit einer Näherung Ihrer Testgeometrie für das 0,5- und das 5-fache: Bildbeschreibung hier eingeben

Auch mit Mehrringpolygonen (Löchern) getestet! Bildbeschreibung hier eingeben

Eine Erklärung, wie gewünscht:

scale_geomNimmt ein einzelnes Polygon und durchläuft jeden Scheitelpunkt mit einer Schleife, wobei der Abstand zu einem Referenzpunkt gemessen wird (standardmäßig der Schwerpunkt des Polygons).
Dieser Abstand wird dann mit dem angegebenen Maßstab skaliert, um den neuen "skalierten" Scheitelpunkt zu erstellen.

Die Skalierung erfolgt im Wesentlichen durch Zeichnen einer Linie mit der skalierten Länge vom Referenzpunkt durch den ursprünglichen Scheitelpunkt, wobei das Ende der Linie zum skalierten Scheitelpunkt wird.
Das Winkel- und Rotationsmaterial ist da, weil es einfacher ist, die Position des Linienendes entlang einer einzelnen Achse zu berechnen und dann zu drehen.

Böses Genie
quelle
1
Ich habe dieses Skript getestet und es funktioniert einwandfrei. Du bist ein verdammtes Genie! =) Vielen Dank. Ich werde diese Frage unbeachtet lassen, damit mehr Menschen sie in den "Zukunftsfragen" sehen.
Mr. Che
1
Wenn ich versuche, ein Polygon mit einer Lücke zu verarbeiten, stürzt das Skript in der Zeile ab bdist = refgeom.distanceTo(apnt). Können Sie das testen und beheben?
Mr. Che
@ Mr.Che Hoppla, ich habe vergessen, dass ArcPy alle Ringe eines Polygon-Teils im selben Array zurückgibt. Die Ringe sind durch Nullpunkte getrennt. Es ist eine einfache Lösung, siehe Bearbeiten.
Evil Genius
Hallo. Ist es möglich, eine kleine Erklärung zu bekommen, wie ein Skript funktioniert, ich kann schlecht codieren und bekomme nicht alle Zeilen, daher funktioniert es bitte nicht für mich?
Peter
@ Peter Klar, ich habe eine kurze Erklärung hinzugefügt, was los ist. Es soll kein eigenständiges Skript sein, sondern in ein eigenes Skript integriert werden. Das untere Codefragment zeigt ein Beispiel für die Verwendung.
Evil Genius