Existe-t-il un outil ArcPy pour le redimensionnement des polygones comme l'outil Échelle de la barre d'outils Édition avancée dans ArcMap?

17

J'écris un script python pour ArcGIS 10.3. Je connais Scale tooldans l'interface ArcGIS mais je ne trouve pas une telle commande arcpy. Ça existe?

Comme vous pouvez le voir sur l'image, les Scale tooltravaux sont différents de Buffer tool- cela change la forme du polygone d'origine. La question est donc:

Puis-je utiliser Scale tool(disponible à partir de l'interface ArcGIS) en utilisant arcpy?

entrez la description de l'image ici

M. Che
la source
2
Que diriez-vous de tamponner et de supprimer l'ancien polygone!? le tampon peut être utilisé avec des valeurs positives et négatives!
Farid Cheraghi
La question porte sur l'outil ArcPy existant, pas sur la façon de redimensionner un polygone.
M. Che
Votre titre, votre question et votre commentaire semblent en contradiction. Si les questions en double fournies ne répondent pas à votre question, pourriez-vous modifier votre question pour clarifier ce que vous recherchez?
Aaron
1
@ Mr.Che Buffer tool peut être utilisé dans les scripts python via arcpy.Buffer_analysis (...)
Farid Cheraghi
C'est super! Comment puis-je mettre à jour chaque classe d'entités par un nombre dans une table plutôt que de mettre à l'échelle toutes les entités par 0,5 par exemple? Merci
user1655130

Réponses:

27

Je ne suis au courant de rien dans l'API arcpy qui fera la mise à l'échelle pour vous, mais écrire une fonction pour le faire serait relativement simple.

Le code ci-dessous effectue la mise à l'échelle des entités 2D et ne prend pas en compte les valeurs M ou Z:

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)

Vous pouvez l'appeler avec un objet géométrique, un facteur d'échelle (1 = même taille, 0,5 = demi-taille, 5 = 5 fois plus grand, etc.) et un point de référence facultatif:

scale_geom(some_geom, 1.5)

Utilisez-le conjointement avec des curseurs pour mettre à l'échelle une classe d'entités entière, en supposant que la classe d'entités de destination existe déjà:

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

edit: voici un exemple utilisant une approximation de votre géométrie de test, pour 0,5 et 5 fois: entrez la description de l'image ici

Également testé avec des polygones multi-anneaux (trous)! entrez la description de l'image ici

Une explication, comme demandé:

scale_geomprend un seul polygone et boucle à travers chaque sommet, mesurant la distance de celui-ci à un point de référence (par défaut, le centroïde du polygone).
Cette distance est ensuite mise à l'échelle par l'échelle donnée pour créer le nouveau sommet «mis à l'échelle».

La mise à l'échelle se fait en traçant essentiellement une ligne à la longueur mise à l'échelle du point de référence à travers le sommet d'origine, la fin de la ligne devenant le sommet mis à l'échelle.
L'angle et la rotation sont là parce qu'il est plus simple de calculer la position de la fin de la ligne le long d'un seul axe, puis de la faire pivoter «en place».

Génie du mal
la source
1
J'ai testé ce script et cela fonctionne très bien. Tu es un putain de génie! =) Thx beaucoup. Je laisserai cette question sans réponse afin que plus de gens la voient dans les "questions futures".
M. Che
1
J'ai trouvé que lorsque j'essaie de traiter un polygone avec un trou - cela entraîne un crash du script en ligne bdist = refgeom.distanceTo(apnt). Pouvez-vous tester et corriger cela?
M. Che
@ Mr.Che Oups, j'ai oublié qu'ArcPy renvoie tous les anneaux d'une partie polygonale dans le même tableau. Les anneaux sont séparés par des points nuls. C'est une solution facile, veuillez voir la modification.
Evil Genius
Bonjour. Est-il possible d'obtenir une petite explication sur le fonctionnement du script, je suis mauvais en codage et je n'obtiens pas toutes les lignes, donc cela ne fonctionne pas pour moi, s'il vous plaît?
Peter
@peter Bien sûr, j'ai ajouté une courte explication de ce qui se passe. Ce n'est pas censé être un script autonome, mais quelque chose à intégrer dans votre propre script. L'extrait de code inférieur montre un exemple de la façon dont il pourrait être utilisé.
Evil Genius