Sunday, 6 October 2019

arcgis desktop - Is there ArcPy tool for polygon resizing like Scale tool of Advanced Editing toolbar in ArcMap?


I am writing a python script for ArcGIS 10.3. I know about Scale tool in ArcGIS interface but I can't find such arcpy command. It exists?


As you can see on the picture the Scale tool works different than Buffer tool - it changes the form of original polygon. So the question is:


Can I use Scale tool (available from ArcGIS interface) using arcpy?


enter image description here



Answer



I'm not aware of anything in the arcpy API that will do the scaling for you, but writing a function to do so would be relatively simple.



The code below does the scaling for 2D features, and doesn't take into account M or Z values:


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)


You can call it with a geometry object, a scale factor (1 = same size, 0.5 = half size, 5 = 5 times as large, etc.), and an optional reference point:


scale_geom(some_geom, 1.5)

Use this in conjunction with cursors to scale an entire feature class, assuming the destination feature class already exists:


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: here's an example using an approximation of your test geometry, for 0.5 and 5 times: enter image description here


Also tested with multi-ring polygons (holes)! enter image description here


An explanation, as requested:


scale_geom takes a single polygon and loops through each vertex, measuring the distance from it to a reference point (by default, the centroid of the polygon).
That distance is then scaled by the scale given to create the new 'scaled' vertex.


The scaling is done by essentially drawing a line at the scaled length from the reference point through the original vertex, with the end of the line becoming the scaled vertex.
The angle and rotation stuff is there because it's more straight forward to calculate the position of the end of the line along a single axis and then rotate it 'into place.'



No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...