3

I have a polyline dataset I'm trying to merge / dissolve segments based on an attribute into continuous single lines. The problem is that some of those segments cross each other but need to be represented as a single-part feature in the end result. The segments share, at most, one endpoint with another segment of the same value and should all snap together nicely since they were digitized that way. (The data will eventually be used in a geometric network and we don't want net junctions diverting traces.)

Has anyone managed to automate this process with my criteria? I've tried various combinations of the Dissolve, Unsplit Line and Merge tools in ArcGIS Desktop 10.3, all to failing to create the continuous lines. Non-ArcGIS based solutions are welcome if that will get the job done.

Update:

So the end goal is to connect separated features and produce continuous feature that loops over itself without the inclusion of the a vertex where the lines cross.

Features before dissolve Features before dissolve Post dissolve - No multipart, Unsplit (per Michael's suggestion). Three features are created as a result (The selected feature, plus features just north and west from the denoted intersection.) Dissolve - No multipart, Unsplit (per Michael's suggestion)

13
  • Do the same attribute segments have common sink? Would you like a single line from very top to sink? Any actual diversions, i.e. 2 segments running out from single node?
    – FelixIP
    Commented Jul 23, 2015 at 1:16
  • 1
    Dissolve resources.arcgis.com/en/help/main/10.2/index.html#//… - no multipart, unsplit lines. That will sort the geometries out but as for the fields/values what's up with them? are individual attributes required?... if you have an advanced license you can find them using Feature Vertices to Points (both ends, Dangle) connected endpoints are select all BOTH_ENDS then unselect near DANGLE. That will find the likely places lines will dissolve. Commented Jul 23, 2015 at 3:38
  • I'm not really following the cross but can't be multipart thing. Are you saying the tools are introducing new vertices where they shouldn't? Can you provide a screenshot of a problem area?
    – Chris W
    Commented Jul 23, 2015 at 4:23
  • 1
    @MichaelMiles-Stimson I got that part; what I'm getting at is the planarize lines issue. If two lines cross and the allow multipart option isn't checked with dissolve, they should not become a single entity and no vertex should be introduced where they cross even if they have the same dissolve attribute. OP says merge/dissolve isn't working and that singlepart/multipart is an issue. I just think a graphic example of what the problem is would make it a lot clearer. Your first comment may be the answer, but the question isn't clear on exactly what is being done (and possibly done wrong).
    – Chris W
    Commented Jul 23, 2015 at 17:12
  • 1
    Ok, I've just done some testing and I guess what I say should not happen is what actually does happen. Dissolve, and both Union and Merge on the Editor toolbar are planarizing the lines when run. This means a new vertex is introduced at the point of crossing, No matter what combination of options or methods I use, I can't seem to stop it from doing this. It's hard to see in OP's images (because that's showing vertex editing mode rather than selections), but the loop becomes a closed polyline with the two feeders as separate lines. Wonder if @Vince can help - if that notification even works.
    – Chris W
    Commented Jul 23, 2015 at 19:39

2 Answers 2

1

This is VB.net code for merging lines that share a common end point, the function to call is UnsplitAllLayer0 with utility functions fFlipPC and fJoinPC. First step is to find all the locations where two lines share an endpoint then using those locations go back through and merge both features (extend one and delete the other)... a line may be extended multiple times, this I've found is the simplest way of not loosing track of multiple dissolution operations:

Private Function fFlipPC(ByRef InPointColl As IPointCollection) As IPointCollection
    Dim pOutPC As IPointCollection
    Dim cnt As Long

    pOutPC = New Path
    'pOutPc.RemovePoints 0, InPointColl.PointCount - 1

    For cnt = InPointColl.PointCount - 1 To 0 Step -1
        pOutPC.AddPoint(InPointColl.Point(cnt))
    Next cnt
    fFlipPC = pOutPC
End Function
Private Function fJoinPC(ByRef FirstPointColl As IPointCollection, ByRef SecondPointColl As IPointCollection) As IPointCollection
    Dim pOutPC As IPointCollection
    Dim cnt As Long

    pOutPC = FirstPointColl
    For cnt = 1 To SecondPointColl.PointCount - 1
        pOutPC.AddPoint(SecondPointColl.Point(cnt))
    Next cnt
    fJoinPC = pOutPC
End Function
Private Sub UnsplitAllLayer0()
    Dim pLayer As ILayer = My.ArcMap.Document.FocusMap.Layer(0)
    If Not (TypeOf pLayer Is IFeatureLayer) Then Exit Sub ' must be a feature layer

    Dim pFtLayer As IFeatureLayer = CType(pLayer, IFeatureLayer)
    ' step one: find the likely locations..
    Dim pLikelyLocations As IMultipoint = New MultipointClass()
    Dim pLikelyPC As IPointCollection = CType(pLikelyLocations, IPointCollection)

    ' get a cursor on all your lines
    Dim pFtCur As IFeatureCursor = pFtLayer.Search(Nothing, True)
    Dim pFeat As IFeature = pFtCur.NextFeature()

    Dim pTopOp As ITopologicalOperator
    Dim pBoundary As IGeometry
    Dim pBoundaryPC As IPointCollection
    Dim pSpatFlt As ISpatialFilter = New SpatialFilterClass()
    pSpatFlt.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects
    Dim pSrchGeom As IGeometry

    Do Until pFeat Is Nothing
        pTopOp = pFeat.ShapeCopy
        pBoundary = pTopOp.Boundary ' both ends
        pBoundaryPC = CType(pBoundary, IPointCollection)

        For pPntCnt As Integer = 0 To pBoundaryPC.PointCount - 1
            pTopOp = pBoundaryPC.Point(pPntCnt)
            pSpatFlt.Geometry = pTopOp.Buffer(0.01) ' use a suitable small number here
            If pFtLayer.FeatureClass.FeatureCount(pSpatFlt) = 2 Then
                pLikelyPC.AddPoint(pBoundaryPC.Point(pPntCnt)) ' store this point to go back later
                ' add some code here for checking attributes
                ' if that's important to your network
            End If
        Next

        pFeat = pFtCur.NextFeature() ' go next..
    Loop

    Dim pEd As IEditor = pApplication.FindExtensionByName("ESRI Object Editor") 'http://help.arcgis.com/en/sdk/10.0/arcobjects_net/conceptualhelp/index.html#//0001000004nn000000
    pEd.StartOperation()

    ' now go through the likely locations and dissolve
    For pLikelyCnt As Integer = 0 To pLikelyPC.PointCount - 1
        pTopOp = pLikelyPC.Point(pLikelyCnt)
        pSpatFlt.Geometry = pTopOp.Buffer(0.01) ' use a suitable small number here
        Dim pUpCur As IFeatureCursor = pFtLayer.FeatureClass.Update(pSpatFlt, False) ' must be false here
        Dim pFtInto As IFeature = pUpCur.NextFeature() ' the one that will get the shape
        Dim pFtRem As IFeature = pUpCur.NextFeature()    ' the one that will be removed

        Dim pIntoPC As IPointCollection = pFtInto.ShapeCopy
        Dim pRemPC As IPointCollection = pFtRem.ShapeCopy

        ' there are 4 possibilites for line orientation:
        Dim pProxOp As IProximityOperator = pIntoPC.Point(0) ' start point
        Dim pFromFromDist = pProxOp.ReturnDistance(pRemPC.Point(0)) 'distance start-start
        Dim pFromToDist = pProxOp.ReturnDistance(pRemPC.Point(pRemPC.PointCount - 1))   ' distance start-end
        pProxOp = pIntoPC.Point(pIntoPC.PointCount - 1)
        Dim pToFromDist = pProxOp.ReturnDistance(pRemPC.Point(0))   'distance end-start
        Dim pToToDist = pProxOp.ReturnDistance(pRemPC.Point(pRemPC.PointCount - 1)) ' distance end-end

        Dim pOutPolyLine As IGeometryCollection = New PolylineClass()
        Dim pOutPath As IPointCollection = New PathClass()
        Dim pHasUpdated As Boolean = False
        If pToFromDist < 0.01 Then
            ' simplest case just extend
            pOutPath.AddPointCollection(fJoinPC(pIntoPC, pRemPC))
            pHasUpdated = True
        ElseIf pFromFromDist < 0.01 Then
            ' flip first then join
            pOutPath.AddPointCollection(fJoinPC(fFlipPC(pRemPC), pIntoPC))
            pHasUpdated = True
        ElseIf pFromToDist < 0.01 Then
            ' Extend first
            pOutPath.AddPointCollection(fJoinPC(pRemPC, pIntoPC))
            pHasUpdated = True
        ElseIf pToToDist < 0.01 Then
            ' flip second then join
            pOutPath.AddPointCollection(fJoinPC(pIntoPC, fFlipPC(pRemPC)))
            pHasUpdated = True
        End If
        If pHasUpdated Then

            pOutPolyLine.AddGeometry(pOutPath)
            Dim pOutShape As IGeometry = pOutPolyLine
            pOutShape.SpatialReference = pFtInto.ShapeCopy.SpatialReference

            ' update one and remove the other
            pFtInto.Shape = pOutShape
            pUpCur.UpdateFeature(pFtInto)
            pFtRem.Delete()
        End If
    Next

    pEd.StopOperation("Unsplit")
End Sub

Disclaimer: this is very old code, I haven't written anything in VB.net for a few years now and may or may not be the current working version... I had to dredge this one up off a backup.

2
  • Thanks for digging this out for me. Upon reviewing the code, I believe this is a viable solution. Will update when I can get it to run.
    – Eok
    Commented Jul 28, 2015 at 16:47
  • After some testing regarding editing sessions, I managed to get the bulk of the code to run almost as is in an Add-In. Many thanks Michael!
    – Eok
    Commented Jul 28, 2015 at 22:59
1

This is my practice of removing 'parasite' nodes.

Original network with links direction shown. Links labelled by FID: enter image description here

Decide where the sink is going to be and flip relevant link if necessary. In this case I picked No 2.

enter image description here

Next step I call 'create network'. Script takes selected link, traces all upstream and flips them on the go if necessary. In case shown it flips only 1 link, that is No 5.

Assign node types based on combination of in/out links - another script. For simple no-diversions network "Source" node is the one where inCount=0 and outCount=1, redundant node (transition) has inCount=1 and outCount=1:

enter image description here

Trace all links downstream from Source (script). The list of FIDs in this case [3,0,5,1,4,2]. Build a single polyline by merging geometries in this order (script).

I don't think that scripting free approach will do here at all.

2
  • Thanks for the thorough answer, though if I'm reading this correctly, it still requires user intervention. Also, as mentioned my comment/response, the geometric network does not have sources or sinks.
    – Eok
    Commented Jul 23, 2015 at 22:21
  • Well, make them. My original network didn't have them either. Or to be more precise it had 2 sinks, 2 sources, 1 diversion and 2 transit nodes.
    – FelixIP
    Commented Jul 23, 2015 at 23:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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