Polygon Bowties

2676
2
12-19-2010 04:45 PM
JohnMcGlynn
Occasional Contributor
Hi,

I am creating a 'Graph' of an attribute of a polyline by taking each point on the line and offsetting it by the (scaled) value.

This all works fine except where there are bends (not curves) in the line. At these points I get bowties in the polygon.

I tried a convex hull on the polygon but that removed all the concave parts. I also tried unioning the polygon with itself but that didn't work either.

I have enclosed a picture of the problem because they may be called something other than bowties elsewhere.

Does anyone have an idea for what else I could try?

Thanks

NavJ

The Code is:

 Public Sub createGraph(ByRef pInLine As IGeometry, ByVal dOffset As Double, ByRef pOutPoly As IGeometry, ByRef sError As String)
        ' Create a graph polygon as an offset of the polyline pInLine
        Dim pBridge As IGeometryBridge2
        Dim pSC As ISegmentCollection
        Dim pNewGC As IGeometryCollection
        Dim pEnumSegs As IEnumSegment
        Dim pS As ISegment
        Dim pPoly As IPolygon = New Polygon
        Dim iPart As Integer
        Dim iSeg As Integer
        Dim pN As ILine
        Dim dX() As Double
        Dim dY() As Double
        Dim X As Double
        Dim Y As Double
        Dim i As Integer
        Dim j As Integer
        Dim iSegTot As Integer
        Dim pWKS() As WKSPoint
        Dim dDist As Double
        Dim pCol As New StringCollection
        Dim a() As String
        Dim s As String

        Try
            sError = ""
            pOutPoly = New Polygon
            pOutPoly.SpatialReference = pInLine.SpatialReference
            pPoly.SpatialReference = pInLine.SpatialReference

            If pInLine.IsEmpty Then Exit Sub

            pBridge = New ESRI.ArcGIS.Geometry.GeometryEnvironment
            ' Get the offset distance
            dDist = dOffset

            ' Set the input segment collection
            pSC = pInLine
            iSegTot = 1 + 2 * pSC.SegmentCount
            i = iSegTot + 1
            ReDim dX(i)
            ReDim dY(i)
            pNewGC = pOutPoly
            ' Enumerate over the input segments adding points at i and at iSegTot - i
            pEnumSegs = pSC.EnumSegments
            pEnumSegs.Next(pS, iPart, iSeg)
            i = 0
            While IsNothing(pS) = False
                X = pS.FromPoint.X
                Y = pS.FromPoint.Y
                dX(iSegTot - i) = X
                dY(iSegTot - i) = Y
                ' Get the offset point
                pN = New Line
                pS.QueryNormal(esriSegmentExtension.esriNoExtension, 0, False, dDist, pN)
                X = pN.ToPoint.X
                Y = pN.ToPoint.Y
                dX(i) = X
                dY(i) = Y
                pEnumSegs.Next(pS, iPart, iSeg)
                i = i + 1
            End While
            ' Add the last point
            dX(UBound(dX)) = dX(0)
            dY(UBound(dY)) = dY(0)
            ' Remove nulls
            For i = 0 To UBound(dX)
                If IsNothing(dX(i)) = False AndAlso IsNumeric(dX(i)) AndAlso dX(i) <> 0 Then
                    If IsNothing(dY(i)) = False AndAlso IsNumeric(dY(i)) AndAlso dY(i) <> 0 Then
                        pCol.Add(dX(i).ToString & "," & dY(i).ToString)
                    End If
                End If
            Next i
            ReDim dX(pCol.Count - 1)
            ReDim dY(pCol.Count - 1)
            For i = 0 To pCol.Count - 1
                s = pCol.Item(i)
                a = Split(s, ",")
                dX(i) = CType(a(0), Double)
                dY(i) = CType(a(1), Double)
            Next i

            ' Assemble the points into a polygon
            ReDim pWKS(UBound(dX))

            j = 0
            For i = 0 To UBound(dX)
                pWKS(i).X = dX(i)
                pWKS(i).Y = dY(i)
            Next i
            pBridge.AddWKSPoints(pOutPoly, pWKS)

            '' Simplify the polygon and union it with itself to get rid of any slivers and bowties
            'Dim topologicalOperator2 As ITopologicalOperator2 = CType(pOutPoly, ITopologicalOperator2)
            ''Simplify.
            'topologicalOperator2.IsKnownSimple_2 = False
            'topologicalOperator2.Simplify()
            'Dim pGeometry As IGeometry = topologicalOperator2.Union(pOutPoly)
            'pOutPoly = CType(pGeometry, IPolygon)



        Catch ex As Exception
            sError = "ERROR: createGraph; Line: " & Erl() & vbCrLf & ex.Message
        End Try

    End Sub
0 Kudos
2 Replies
deleted-user-Ce5BdyPCP7b5
New Contributor
One thing you could do is to keep track of the lines between the points on your pInLine and the new offset points. Let's say you have point 1 that has X1Y1 coordinates and the offset to this point is point 1' that has X1'Y1' coordinates. The two points can form a line (which we'll call offset line 1). The next point on your pInLine is point 2 that has X2Y2 coordinates and its offset with coordinates X2'Y2'. This again can form a line, offset line 2. You can see if these two offset lines cross each other. If they intersect, you'll get a bow tie or something screwy. If they don't, you're good and can place that line. It'll slow down your code a lot to check all previous offset lines, but it should help. The shapes might not always be great, so you might want to play with the polygon a bit to get a better shape (maybe densifying it, or adding vertices at turns).
0 Kudos
JohnMcGlynn
Occasional Contributor
I had a go at finding the intersections but the problem is deciding what to do when they occur. There are so many combinations.

The method I finally used was to buffer the line and then chop off the ends and the bottom.

The code is (it could use some tidying):

 Public Sub createGraph(ByRef pInLine As IGeometry, ByVal dOffset As Double, ByRef pOutPoly As IGeometry, ByRef sError As String)
        ' Create the graph by creating a buffer around the line and then cutting it along the Input line and trimming the ends
        Dim pTopo1 As ITopologicalOperator2
        Dim pTopo2 As ITopologicalOperator2
        Dim pTopo3 As ITopologicalOperator2
        Dim pTopo4 As ITopologicalOperator2
        Dim pGeomBuff As IGeometry
        Dim pLineL As IPolyline
        Dim pLineR As IPolyline
        Dim pLine As ILine ' The line connecting the start and end points of pInLine
        Dim pLineTmp As ILine
        Dim pInPoly As IPolyline
        Dim pGeomL1 As IGeometry
        Dim pGeomR1 As IGeometry
        Dim pGeomL2 As IGeometry
        Dim pGeomR2 As IGeometry
        Dim pGeomUP As IGeometry
        Dim pGeomDOWN As IGeometry
        Dim pPtFrom As IPoint
        Dim pPtTo As IPoint
        Dim dDX As Double
        Dim dDY As Double
        Dim bRLL As Boolean ' The cut sequence flag; RLL is Right-Left-Left otherwise LRR

        Try
            sError = ""
            pOutPoly = New Polygon

            If pInLine.IsEmpty Then Exit Sub

            pOutPoly.SpatialReference = pInLine.SpatialReference

            ' Set the input polyline
            pInPoly = New Polyline
            pInPoly.SpatialReference = pInLine.SpatialReference
            pInPoly = CType(pInLine, IPolyline)
            ' Set the Cut sequence flag
            dDX = pInPoly.ToPoint.X - pInPoly.FromPoint.X
            If dDX = 0 Then
                ' Vertical line; Use Y axis
                dDX = pInPoly.ToPoint.Y - pInPoly.FromPoint.Y
            End If
            If dDX > 0 Then
                bRLL = True
            Else
                bRLL = False
            End If

            ' Create the buffer on the input line
            pTopo1 = pInLine
            pGeomBuff = pTopo1.Buffer(dOffset)

            ' Create the end cutting lines
            ' 1. Get the line between the polyline end-points
            pLine = New Line
            pLine.SpatialReference = pInLine.SpatialReference

            pLine.FromPoint = pInPoly.FromPoint
            pLine.ToPoint = pInPoly.ToPoint
            '##TEST
            'g_wLog.WriteLine("BASE From: " & pLine.FromPoint.X & ", " & pLine.FromPoint.Y & " To: " & pLine.ToPoint.X & ", " & pLine.ToPoint.Y)
            ' 2. Get the left and right end lines perpendicular to the line between the ends
            pLineTmp = New Line
            pLineTmp.SpatialReference = pInLine.SpatialReference
            pLine.QueryNormal(esriSegmentExtension.esriExtendAtFrom, 0, False, dOffset * 2, pLineTmp)
            ' Extend the end lines to go right across the buffer
            pPtTo = New ESRI.ArcGIS.Geometry.Point
            pPtTo = pLineTmp.ToPoint
            dDX = pLineTmp.ToPoint.X - pLineTmp.FromPoint.X
            dDY = pLineTmp.ToPoint.Y - pLineTmp.FromPoint.Y
            pPtFrom = New ESRI.ArcGIS.Geometry.Point
            pPtFrom.X = pLineTmp.FromPoint.X - dDX
            pPtFrom.Y = pLineTmp.FromPoint.Y - dDY

            pLineL = New Polyline
            pLineL.SpatialReference = pInLine.SpatialReference
            pLineL.FromPoint = pPtFrom
            pLineL.ToPoint = pPtTo

            ' Set the orientation of this line
            dDY = pLineL.ToPoint.Y - pLineL.FromPoint.Y
            If dDY = 0 Then
                dDY = pLineL.ToPoint.X - pLineL.FromPoint.X
            End If
            If dDY < 0 Then
                pLineL.ReverseOrientation()
            End If
            '##TEST
            'g_wLog.WriteLine("CUT_L From: " & pLineL.FromPoint.X & ", " & pLineL.FromPoint.Y & " To: " & pLineL.ToPoint.X & ", " & pLineL.ToPoint.Y)
            ' Other End
            pLineTmp = New Line
            pLineTmp.SpatialReference = pInLine.SpatialReference
            pLine.QueryNormal(esriSegmentExtension.esriExtendAtFrom, pLine.Length, False, dOffset * 2, pLineTmp)
            pPtTo = New ESRI.ArcGIS.Geometry.Point
            pPtTo = pLineTmp.ToPoint
            dDX = pLineTmp.ToPoint.X - pLineTmp.FromPoint.X
            dDY = pLineTmp.ToPoint.Y - pLineTmp.FromPoint.Y
            pPtFrom = New ESRI.ArcGIS.Geometry.Point
            pPtFrom.X = pLineTmp.FromPoint.X - dDX
            pPtFrom.Y = pLineTmp.FromPoint.Y - dDY

            pLineR = New Polyline
            pLineR.SpatialReference = pInLine.SpatialReference
            pLineR.FromPoint = pPtFrom
            pLineR.ToPoint = pPtTo
            ' Get the orientation of this line
            dDY = pLineR.ToPoint.Y - pLineR.FromPoint.Y
            If dDY = 0 Then
                dDY = pLineR.ToPoint.X - pLineR.FromPoint.X
            End If
            If dDY < 0 Then
                pLineR.ReverseOrientation()
            End If
            '##TEST
            'g_wLog.WriteLine("CUT_R From: " & pLineR.FromPoint.X & ", " & pLineR.FromPoint.Y & " To: " & pLineR.ToPoint.X & ", " & pLineR.ToPoint.Y)

            ' Chop the Left end off
            Try
                pTopo2 = pGeomBuff
                pTopo2.Cut(pLineL, pGeomL1, pGeomR1)
                ' Get the resulting geometry depending on the flag
                If bRLL = True Then
                    pTopo3 = pGeomR1
                Else
                    pTopo3 = pGeomL1
                End If
            Catch ex1 As Exception
                pOutPoly = CType(pGeomBuff, IPolygon)
                g_wLog.WriteLine("CUT_L ERROR: " & ex1.Message)
                Exit Sub
            End Try

            ' And the Right end
            Try
                pTopo3.Cut(pLineR, pGeomL2, pGeomR2)
                ' Get the resulting geometry
                If bRLL = True Then
                    pTopo4 = pGeomL2
                Else
                    pTopo4 = pGeomR2
                End If
            Catch ex2 As Exception

                If bRLL = True Then
                    pOutPoly = CType(pGeomR1, IPolygon)
                Else
                    pOutPoly = CType(pGeomL1, IPolygon)
                End If
                g_wLog.WriteLine("CUT_R ERROR: " & ex2.Message)
                Exit Sub
            End Try

            ' And the bottom
            Try
                pTopo4.Cut(pInLine, pGeomUP, pGeomDOWN)
            Catch ex3 As Exception
                If bRLL = True Then
                    pOutPoly = CType(pGeomL2, IPolygon)
                Else
                    pOutPoly = CType(pGeomR2, IPolygon)
                End If
                g_wLog.WriteLine("CUT_BOTTOM ERROR: " & ex3.Message)
                Exit Sub
            End Try
            ' Set the output
            If bRLL = True Then
                pOutPoly = CType(pGeomUP, IPolygon)
            Else
                pOutPoly = CType(pGeomDOWN, IPolygon)
            End If





        Catch ex As Exception
            sError = "ERROR: createGraph; Line: " & Erl() & vbCrLf & ex.Message
        Finally
            pTopo1 = Nothing
            pTopo2 = Nothing
            pTopo3 = Nothing
            pTopo4 = Nothing
            pLineR = Nothing
            pLineL = Nothing
        End Try

    End Sub
0 Kudos