7

I am trying to dissolve multiple lines for stream networks into individual features. The dataset contains many stream networks so a complete dissolve of the whole dataset wont do as I would like to only dissolve streams that are in the same network (illustrated in the pictures below).

I've tried using the Unsplit Line tool and the Dissolve tool in ArcGIS 10. The tool will only join two lines together, i.e. lines that share a common end point. This is illustrated in the screenshots below.

enter image description here

Here is the orignal dataset with the lines split, a single line feature is highlighted

enter image description here

This is the same feature highlighted after using the unsplit/dissolve tool. As you can see it has only joined two lines together rather than all of the lines that are touching.

The features in the same network do not have a common attribute either.

1
  • No that's something else I should have mentioned they do not. I was hoping there was something that dissolved based on lines that were touching within the network. But as far as I can tell dissolve/unsplit only works on lines with shared end points. Commented Feb 2, 2015 at 14:49

4 Answers 4

9

I've devised a way of doing it with little bits from all of the other answers.

The scripts that @GeoJohn and @FelixIP have created may well work (if you have access to ArcGIS 10.1 and the database access functions), so if someone else had this problem please check those options out as well.

I did the following in the end:

  1. Created a very small buffer polygon around the lines (0.1 metres) to make sure there was crossover between the lines that were connected.

  2. Use the dissolve tool with the Unsplit option checked. This created individual polygons for each of the connected line groups.

  3. Create a unique ID for each polygon.

  4. Use the spatial join tool to add the unique polygon ids to each line that intersects or is within the polygons.

  5. Use the dissolve tool again to dissolve the lines based on the unique ID.

That has worked for me but like I said the scripting methods may be preferable for other people.

enter image description here

3

I think the best way to do this is in arcpy. The following is a complete script that will do what you need. It works by putting a small buffer around all of the stream features and indexing them(adds a field that is used later in the script to dissolve). The buffer allows for every part of a network, whether it is connected or not, to be considered part of it by using the buffer distance as a "tolerance". So... if the networks are really close together you can set the buffer distance lower.

import arcpy, os
from arcpy import env

arcpy.env.overwriteOutput = True

inFC = arcpy.GetParameterAsText(0)
outLoc = arcpy.GetParameterAsText(1)
outName = arcpy.GetParameterAsText(2)

newFC = os.path.join(outLoc, outName)

def buildWhereClause(table, field, value):
    fieldDelimited = arcpy.AddFieldDelimiters(table, field)
    fieldType = arcpy.ListFields(table, field)[0].type
    if str(fieldType) == 'String':
        value = "'%s'" % value
    whereClause = "%s = %s" % (fieldDelimited, value)
    return whereClause

bufferClass = os.path.join(outLoc, outName + "_Buffer")
singleClass = os.path.join(outLoc, outName + "_singlePart")

arcpy.Buffer_analysis(inFC, bufferClass, 500, "", "", "ALL")
arcpy.MultipartToSinglepart_management(bufferClass, singleClass)

arcpy.AddField_management(singleClass, "INDEX_ID", "LONG")

fields = arcpy.ListFields(singleClass)
fieldList = []

for nm in fields:
    fieldList.append(nm.name)

for field in fieldList:
    if field == "INDEX_ID":
        fieldCounter = 1
        with arcpy.da.UpdateCursor(singleClass, field) as cursor:
            for row in cursor:
                row[0] = fieldCounter
                cursor.updateRow(row)
                fieldCounter += 1
        del cursor
        del row

arcpy.CreateFeatureclass_management(outLoc, outName + "_Final", "POLYLINE")
finalFC = os.path.join(outLoc, outName + "_Final")

try:
    arcpy.AddField_management(finalFC, "ID_Match", "LONG")
except:
    pass

tempFC = os.path.join(outLoc, "tempFC_SE")
tempFC_2 = os.path.join(outLoc, "tempFC_SE_2")

arcpy.AddField_management(inFC, "ID_Match", "LONG")

fieldMatch = ["INDEX_ID", "OBJECTID"]
inFCMatch = ["ID_Match"]

arcpy.MakeFeatureLayer_management(singleClass, "singleClass_lyr")
arcpy.MakeFeatureLayer_management(inFC, "inFC_lyr")

with arcpy.da.SearchCursor("singleClass_lyr", fieldMatch) as cursor:
    for row in cursor:
        arcpy.SelectLayerByAttribute_management("singleClass_lyr", "CLEAR_SELECTION")
        arcpy.SelectLayerByAttribute_management("inFC_lyr", "CLEAR_SELECTION")
        where = buildWhereClause(singleClass, "OBJECTID", str(row[1]))
        arcpy.SelectLayerByAttribute_management("singleClass_lyr", "NEW_SELECTION", where)
        arcpy.CopyFeatures_management("singleClass_lyr", tempFC_2)
        arcpy.SelectLayerByLocation_management("inFC_lyr", "INTERSECT", tempFC_2)
        arcpy.CopyFeatures_management("inFC_lyr", tempFC)
        with arcpy.da.UpdateCursor(tempFC, inFCMatch) as cursor2:
            for row2 in cursor2:
                row2[0] = row[0]
                cursor2.updateRow(row2)
        del cursor2
        del row2
        arcpy.Append_management(tempFC, finalFC, "NO_TEST")
        arcpy.Delete_management(tempFC)
        arcpy.Delete_management(tempFC_2)
del cursor
del row

arcpy.Dissolve_management(finalFC, newFC, "ID_Match")

arcpy.Delete_management(finalFC)
arcpy.Delete_management(bufferClass)
arcpy.Delete_management(singleClass)

The results will not preserve fields from the original FC but with a little tweaking that is also possible.

3

If I understand question correctly and this is what you'd like to see enter image description here the script below will do

# Import arcpy module
import arcpy, traceback, os, sys
from arcpy import env
infc = r'D:\Scratch\river_cl.shp'
joined = r'D:\Scratch\s_joined.shp'
groupField="GROUP"
arcpy.overwriteoutput=1
try:
    def showPyMessage():
        arcpy.AddMessage(str(time.ctime()) + " - " + message)
    arcpy.SpatialJoin_analysis(infc, infc, joined, "JOIN_ONE_TO_MANY","KEEP_ALL", "", "BOUNDARY_TOUCHES")
    vFT=list(arcpy.da.TableToNumPyArray(joined,("TARGET_FID","JOIN_FID")))
    bigList=[]
    while (True):
        if len(vFT)==0:
            break
        first=list(vFT.pop(0))
        while (True):
            if len(vFT)==0:
                break
            if -1 not in first:
                m=0;toRemove=[]
                for ent in vFT:
                    f,t = ent
                    if f in first or t in first:
                        small = filter(lambda x: x not in first, ent)
                        first+=small
                        toRemove.append(m)
                    m+=1
                if len(toRemove)==0:
                    break
                toRemove.reverse()
                for m in toRemove:
                    vFT.pop(m)
            else:
                break
        bigList.append(first)

    result=arcpy.GetCount_management(infc)
    nStreams=int(result.getOutput(0))
    groups=[-1]*nStreams
    m=0
    for first in bigList:
        small = tuple(filter(lambda x: x not in [-1], first))
        arcpy.AddMessage('Group %i Contains FID(s) in %s' %(m,small))
        for n in small:
            groups[n]=m
        m+=1
    with arcpy.da.UpdateCursor(infc,groupField) as cursor:
        m=0
        for row in cursor:
            row[0]=groups[m]
            cursor.updateRow(row)
            m+=1
except:
    message = "\n*** PYTHON ERRORS *** "; showPyMessage()
    message = "Python Traceback Info: " + traceback.format_tb(sys.exc_info()[2])[0]; showPyMessage()
    message = "Python Error Info: " +  str(sys.exc_type)+ ": " + str(sys.exc_value) + "\n"; showPyMessage()            

Just replace 3 lines at the beginning, starting with infc=... by your shapefile, intermediate output and field to store Group No. Add this field to infc manually. Script will populate this field with unique group numbers. It can be used for dissolve

0
0

I found another method described in an ArcGIS Discussion Forum posting that I think would work and may be simpler:

creating a route from the line shapefile (unticking ignore spatial gaps) and then exploding the resulting route.

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.