Thursday, 15 November 2018

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