I have some polygons and I want to calculate the inner angle of each polygon. Now, I have the geometry of every line of polygons in the same attribute table as shown in the following image.
I want to calculate the inner angle using the following equation:
inner_angle= the bearing angle of the previous line - the bearing angle of the following line +180 (in degrees).
How can I write this in the code block in the model builder and do this for each polygon respectively?
screen grab of your first 2 polygons? just to confirm something
and you are working with full circle angles and not angles from north I presume
Yes. The attached image just illustrates geometry table of points.
I just need code execute that :
_______________________________
for each polygon >>>>>>( Can the iterate feature in model builder do this loop ( for each polygon)?)
for i = 1: last line in the polygon
if i < the last line in the polygon
angle = the line bearing @ i - the line bearing @ (i+1) +180
if angle >360
angle= angle -360
end
if angle < 0
angle = angle +360
end
else
angle = the line bearing @ i - the line bearing @ first line
if angle >360
angle= angle -360
end
if angle < 0
angle = angle +360
end
end
end
_____________________________
The desired result as follow
I hope that I illustrated the problem clearly.
If you just want the sum of the interior angles of a polygon... convex, concave, whatever... it might be easier to
I guess I am not sure why you need the individual angles within the polygon and why you converted the polygon to its edges to get this
I would probably create a script tool that would be based on the following parameters:
The code would be something like this:
def main():
import arcpy
from collections import OrderedDict
# read parameters
fc = arcpy.GetParameterAsText(0) # r'C:\GeoNet\InnerAngle\data.gdb\angles'
fld_pol_id = arcpy.GetParameterAsText(1) # 'polygonID'
fld_bearing = arcpy.GetParameterAsText(2) # 'line_bearing'
fld_inner = arcpy.GetParameterAsText(3) # 'inner_angle'
# create a lst of the data
fld_oid = 'OID@'
flds = (fld_oid, fld_pol_id, fld_bearing)
dct_data = OrderedDict((r[0], [r[1], r[2]]) for r in arcpy.da.SearchCursor(fc, flds))
# create a list with polygon IDs
pol_ids = list(set([data[0] for oid, data in dct_data.items()]))
# process the polygons and store results in dct
dct_res = {}
for pol_id in pol_ids:
dct_bearing = OrderedDict((oid, data[1]) for oid, data in dct_data.items() if data[0] == pol_id)
lst_bearings = dct_bearing.values()
cnt = 0
for oid, bearing in dct_bearing.items():
if cnt == len(lst_bearings)-1:
inner_angle = lst_bearings[cnt] - lst_bearings[0] + 180
else:
inner_angle = lst_bearings[cnt] - lst_bearings[cnt+1] + 180
if inner_angle > 360:
inner_angle -= 360
if inner_angle < 0:
inner_angle += 360
dct_res[oid] = inner_angle
cnt += 1
# update the fc with the results
flds = (fld_oid, fld_inner)
with arcpy.da.UpdateCursor(fc, flds) as curs:
for row in curs:
oid = row[0]
if oid in dct_res:
row[1] = dct_res[oid]
curs.updateRow(row)
# set output
arcpy.SetParameterAsText(4, fc)
if __name__ == '__main__':
main()
This is what a test generated:
The reason that this will not be possible using a simple feature iteration is that when writing the current feature the line bearing of the next feature must be known.
So.. if you want to give this a shot and you are working with single part shapes since I have experimented a whole 5 minutes. I pulled this from elsewhere and cobbled a framework around the base code.
Actually it is more useful without the lines 30 and 31. It is designed to return all the angles of a poly* feature. If the feature is convex then it returns a boring 360 degrees for polygons. Concave closed-loop shapes should differ from 360
For polylines (or could be modified just to accept sequential point objects), you can get all the angles. The code can be modified ever so slightly to calculate the sequential differences in lengths and you can use those to derived 'weighted direction', but I will leave that out for this discussion.
Yes... to call it use the expression
angles_poly(!Shape!)
import numpy as np
import arcpy
def angles_poly(a, inside=True, in_deg=True):
"""Sequential angles from a poly* shape
"""
a = a.getPart()
a =np.asarray([[i.X, i.Y] for j in a for i in j])
if len(a) < 2:
return None
elif len(a) == 2: # **** check
ba = a[1] - a[0]
return np.arctan2(*ba[::-1])
else:
angles = []
if np.allclose(a[0], a[-1]): # closed loop
a = a[:-1]
r = (-1,) + tuple(range(len(a))) + (0,)
else:
r = tuple(range(len(a)))
for i in range(len(r)-2):
p0, p1, p2 = a[r[i]], a[r[i+1]], a[r[i+2]]
ba = p1 - p0
bc = p1 - p2
cr = np.cross(ba, bc)
dt = np.dot(ba, bc)
ang = np.arctan2(np.linalg.norm(cr), dt)
angles.append(ang)
if in_deg:
angles = np.degrees(angles)
angle = np.sum(angles)
return angle
hmmm, fun stuff ... modified hexagons, which followed the (n-2)*180 rule
Curious as to what you are using this for? A convexity index?
:----------------------------------------------------------------------
:Angle report....
....
.... snip ....
....
(2) number of angles... (7)
:array points...
[[ 300025.98 5000035. ]
[ 300034.96 5000033.03]
[ 300043.3 5000035. ]
[ 300043.3 5000025. ]
[ 300034.64 5000020. ]
[ 300025.98 5000025. ]
[ 300025.98 5000035. ]]
:interior angles [ 77.61 154.32 76.71 120. 120. 120. ]
:sum interior... 668.641029246