Thank you for your script...huge help for someone that has never used Python! As Melanie mentioned below, the script mostly works but random polygons are only completely within my defined study area polygon about 30-40% of the time. For those that aren't, at least some part of the random polygon is within the study area. I could generate a large sample of the random polygons and simply discard those that aren't completely contained, but do you see anything obvious in your original script that would explain what's going on. Again, many thanks!
This is so much easier to do using FME...
Random rotation of polygons from a set of anchor points. - FME Knowledge Center
Hey all,
Thanks @Darren Wiens for this really cool code. I was helping my friend figuring out this code. The biggest issue to me is that the random polygon would not fall in the study area polygon. So I added a try/except and if/else statement to check and return until every polygon fall in the study area. Again, I have a simply polygons for test so it might not working if you have huge amount of polygon, and these polygons should be singlepart and no hollow inside. Also, I remove the "disjoint" function for checking if the result polygon is overlap or not. I hope this help
import arcpy
from arcpy import da
import random
import math
#set-up environment
workspace = r"...:/.../..." #location of your files
pa = workspace + '\\' + "polygons.shp" # polygons to randomly rotate and move
sr = arcpy.Describe(pa).spatialReference # spatial reference
sa = workspace + '\\' + "study_area.shp" # study area
extent = arcpy.Describe(sa).extent # study area extent
sa_geom = [row[0] for row in arcpy.da.SearchCursor(sa,'SHAPE@',spatial_reference=sr)][0] # study area geometry
new_polys = [] # placeholder
# magic function
def rotate(poly,centroid):
#ang = random.random() * 2 * math.pi # random rotation angle in radians if you want to have unified angle to result polygons
new_array = arcpy.Array() # placeholder
rnd_x = random.uniform(extent.XMin,extent.XMax) # random x coordinate for new centroid
rnd_y = random.uniform(extent.YMin,extent.YMax) # random y coordinate for new centroid
rnd_centroid = arcpy.Point(rnd_x,rnd_y) # random centroid point
while True:
try:
for part in poly:#for each polygon part
ang = random.random() * 2 * math.pi # random rotation angle in radians # set as 0 if you don't need to rotate polygon
for pnt in part: # for each vertex
x_trans = pnt.X - centroid.X # normalize to zero
y_trans = pnt.Y - centroid.Y # normalize to zero
x_transprime = (math.cos(ang) * x_trans) - (math.sin(ang) * y_trans) # new x coord, from zero
y_transprime = (math.sin(ang) * x_trans) + (math.cos(ang) * y_trans) # new y coord, from zero
x_prime = x_transprime + rnd_centroid.X # move to new centroid x
y_prime = y_transprime + rnd_centroid.Y # move to new centroid y
new_array.add(arcpy.Point(x_prime, y_prime)) # add to array
temp_poly = arcpy.Polygon(new_array,sr) # make a temporarily polygon for test
if sa_geom.contains(temp_poly) is True:
print 'inside'
break
else:
raise Exception
except:
print 'outside sa, start over...'
return rotate(poly,centroid) #here you start over and run the loop again... until all the polygon fall in study area.
break
good_poly = arcpy.Polygon(new_array,sr) # make all good polygons (indside study area) from array
return (good_poly) # return the final good polygon
print "start processing..."
with arcpy.da.SearchCursor(pa,'SHAPE@',spatial_reference=sr) as cursor: # for each polygon
for row in cursor:
centroid = row[0].centroid # calculate centroid
new_polys.append(rotate(row[0],centroid)) # add returned good polygon to list
arcpy.CopyFeatures_management(new_polys, workspace + '\\' + "TheResult.shp") # write to disk
print "done"
Thanks both Yin-hsuen Chen and @Darren for the code.
I am using this same code for a very large amount of polygons, and it is still working great. However, as expected, it is taking very long. Do you have any suggestions that might speed up the execution?
Thank you!