Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

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
share|improve this question

2 Answers 2

up vote 4 down vote accepted

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.

share|improve this answer
    
Thank you very much for trying to help me. Although I fully understand your comments regarding the nice graph; I am not sure how to correct my code. Could you please tell me some more details regarding the neccessary corrections in relation to my code as shown above? Thanks –  Demetris Jun 10 '11 at 15:33
    
@Demetris I appended a VBA solution. –  whuber Jun 10 '11 at 16:38
    
Dear Whuber,thank you very much. The code now works very well!. I really appreciate your great help! –  Demetris Jun 11 '11 at 4:13

I've seen the answer above, however I would say that there exists a problem with this depending on what the actual question is.

If the question is "what is the minimum angle at the vertex of two lines?" then the solution above is correct.

If the question is "what is the angle inside a polygon at the vertex of two consecutive lines of the polygon?" the the solution also needs to know the "inner" versus the "outer" of the polygon. A complex polygon may have angles approaching zero or approaching 360degrees.

share|improve this answer
    
I think you misunderstand my answer, then: it does produce a result between 0 and 360 and it does require that the polygon be conventionally oriented so you can distinguish left from right. –  whuber Jun 13 '11 at 15:19
    
@Whuber: so the order of lines is important? This should be noted in the function though, no? –  jufemaiz Jun 14 '11 at 0:06
    
Just to clarfify that the above code including that in the answer measures the interior angles of a polygon and the result for each angle is something between 0 and 360. It works very well. The sum of angles always equals (n-2)*180 where n is the number of corners(points) of the polygon. –  Demetris Jun 14 '11 at 12:40
    
@Demetris Thank you for checking! (I only tested the code in a spreadsheet with random line segments, but not in an actual GIS application.) You point out an excellent way to make this solution more general (thereby addressing @jufemaiz' concerns): if, after computing all the angles for the outer ring of a polygon, you find they sum to (n+2)*180 instead of (n-2)*180, then you should just subtract each angle from 360 degrees for every ring in the polygon. –  whuber Jun 14 '11 at 13:26
    
Of course the order is important. For instance, if you turn 90 degrees to the left as you go around one vertex, then if you were to reverse your route, you would turn 270 degrees to the left (90 degrees to the right). This is apparent in the pseudocode, which exhibits the calculation as a subtraction modulo 2Pi. –  whuber Jun 14 '11 at 13:29

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.