Saturday 20 July 2019

arcobjects - Measure each angle of a selected polygon


This post is also a continuation of a previous one. I have the code (in VBA and ARcObjects) shown below to measure each angle of a selected polygon in degrees. I have the following problems: 1. Some angles are negative and I do not wish this since I measure the interior angles of a polygon. 2. Some angles are measured wrong i.e. I get the reflex angle instead of the normal angle. e.g. 270 instead of 90 degrees.


Could somebody see the code and help me for the neccessary modifications please? There is a procedure that calls a function shown also below. Thanks.


    'This procedure gives the angles between the vertices of a polygon


' ----------------------------------------


Dim pMxDoc As IMxDocument
Dim pGeoFtrLyr As IFeatureLayer
Dim pFtrCls As IFeatureClass
Dim pFtrCsr As IFeatureCursor
Dim pFtr As IFeature
Dim pGeomColl As IGeometryCollection
Dim pSegColl As ISegmentCollection
Dim pCurve As ICurve
Dim pLine1 As ILine

Dim pLine2 As ILine
Dim dVtxAng As Double
Dim i As Long, j As Long
Dim PI As Double

' ' Calc PI
PI = Atn(1) * 4

' Get a cursor on the features in the first layer
Set pMxDoc = ThisDocument

Set pGeoFtrLyr = pMxDoc.FocusMap.Layer(12)
Set pFtrCsr = pGeoFtrLyr.Search(Nothing, False)


'Select a polygon

Dim pEnumFeature As IEnumFeature
Set pEnumFeature = pMxDoc.FocusMap.FeatureSelection
pEnumFeature.Reset
Set pFtr = pEnumFeature.Next

If pFtr Is Nothing Then
MsgBox "Please select a valid feature", vbInformation
Exit Sub
End If
If Not TypeOf pFtr.ShapeCopy Is IPointCollection Then
MsgBox "Not a valid feature", vbInformation
Exit Sub
End If



' Get all disjoint paths
Set pGeomColl = pFtr.ShapeCopy

' Loop thru the paths
For i = 0 To pGeomColl.GeometryCount - 1
' Get all segments that make up this path
Set pSegColl = pGeomColl.Geometry(i)
Set pCurve = pSegColl

' Loop thru the segments

For j = 0 To pSegColl.SegmentCount - 2

' Get the next two lines
Set pLine1 = pSegColl.Segment(j)
Set pLine2 = pSegColl.Segment(j + 1)

' Calculate the left side angle (in degrees)
dVtxAng = CalcAngleBetweenLines(pLine1, pLine2, True)
MsgBox Abs(dVtxAng * 57.2957795)


Next j

' Check for a closed polylin

If pCurve.IsClosed Then
Debug.Print pFtr.OID; " is closed!"
Set pLine1 = pSegColl.Segment(j)
Set pLine2 = pSegColl.Segment(0)
dVtxAng = CalcAngleBetweenLines(pLine1, pLine2, True)


'get each angle of the polygon in degrees
MsgBox dVtxAng * 57.2957795

End If

Next i



End Sub



Private Function CalcAngleBetweenLines(pLine1 As ILine, pLine2 As ILine, AsDegrees as Boolean) As Double


Dim dAng1 As Double, dAng2 As Double, dVtxAng As Double, PI As Double

dVtxAng = pLine1.Angle - pLine2.Angle



' Get the reversed angle of the first line
pLine1.ReverseOrientation
dAng1 = pLine1.Angle
If dAng1 < 0 Then dAng1 = 2 * PI + dAng1
pLine1.ReverseOrientation

' Get the angle fo the second line
dAng2 = pLine2.Angle
If dAng2 < 0 Then dAng2 = 2 * PI + dAng2


' subtract 2 from 1 - that's the smallest angle between the two lines
dVtxAng = dAng1 - dAng2

' Ensure we have the angle to the left of the lines

If dVtxAng >= 2 * PI Then
dVtxAng = dVtxAng - 2 * PI
ElseIf dVtxAng < 0 Then
dVtxAng = dVtxAng + 2 * PI
End If



CalcAngleBetweenLines = dVtxAng
End Function

Answer



The angle by which the direction deviates as you move from one segment (AB) to the next (BC) across their common vertex (B) is the difference in angles, dAng = pLine2.Angle - pLine1.Angle.


ABC with angles shown


The angle to the left of the polyline ...ABC... is the supplement of this deviation, Pi - dAng. The angle to the right of the polyline is Pi + dAng. Which of those sides (right or left) is the interior depends on your convention for orienting polygon boundaries. To make sure the one you use is in the range [0, 2*Pi), reduce your answer modulo 2*Pi. In languages where the modulo function only works correctly with positive values--or just to be sure--add another 2*PI before applying it.


In pseudocode the left hand angle therefore is


dAng = Mod(3*PI - (pLine1.Angle - pLine2.Angle), 2*PI)


Because VBA does not have a floating point modulus function (AFAIK), implement one yourself:


Private Function fmod(x As Double, b As Double) As Double
fmod = x - Int(x / b) * b
End Function

Private Function CalcAngleBetweenLines(pLine1 As ILine, pLine2 As ILine) As Double
Const pi = 3.14159265358979
CalcAngleBetweenLines = fmod(3*pi - (pLine1.Angle - pLine2.Angle), 2*pi)
End Function


That's all you need.


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...