14

I have a large dataset consisting of lines/polylines where I would like to join/merge the lines that touch / intersect. Each line has an individual ID and there are no common attributes I can use for a join operation. Is there a way to (spatially) join lines into segments of touching lines?

Screenshot of a selected line in a line segment (current situation): Screenshot of a selected line in a line segment.

It can be done in ArcGIS according to this post (link) among others.

FYI, the dataset is an extract from a pipeline network and I will examine which line segments that cross the most properties / parcels of land.

I'm using QGIS 2.14.0. I have no experience with python or GRASS yet.

Ilias
  • 143
  • 1
  • 1
  • 7
  • I was facing the same issue (Screenshot: Errno 9) as mentioned Above but the process is working fine when i selected the Data (Poly-lines).Try select the data first :) – Shakil Sep 06 '16 at 11:26

2 Answers2

11

In QGIS Plugins you'll find a 'merge lines' plugin, which at first sight seems to accomplish what you are after.

cited from description:

MergeLines

Simplifies the topology of a line network by merging adjacent lines

This plugin merges segments of a line network (e.g. river network) in order to simplify its topology. Two merging methods are currently available : length (a segment is merged with its longest neighbor) and alignment (a segment is merged with its best aligned neighbor).

UPDATE:

Below you find a geoprocessing script of which I hope that it does what you want. For testing purposes I created a shapefile with a bunch of irregularly intersecting lines and no attributes (network):

enter image description here

The standard dialog when executing the script looks like this (in this case the result is a memory layer):

enter image description here

Running the script produces a 'copy' of the input data with a field 'subnet' distinguishing to which subnet a feature belongs. With a categorized style the result looks like this:

enter image description here

This can be dissolved using the field 'subnet'.

Create a new geoprocessing script, copy the code in the editor, save it and things should work.

##Networking=group
##lIn=vector
##lOut=output vector

from qgis.core import *
from qgis.utils import *
from qgis.gui import *
from PyQt4.QtCore import *
from PyQt4.QtGui import QColor
#from processing.core.VectorWriter import VectorWriter

c=iface.mapCanvas()
rubberBand = []

v = processing.getObject(lIn)

#generate a list of all features in the layer
subNets = []
flist = []
for f in v.getFeatures():
    flist.append(f)


def makeSubnets(featureList):
    #print "making subnet ---------------------------------------------------"
    if len(featureList) > 1:
        print [featureList[0]]
        print featureList[1:]
        #print "start finding a subnet"
        fTest, fRest = findSubnet([featureList[0]], featureList[1:])
        #print "finished finding a subnet"
        subNets.append(fTest)
        makeSubnets(fRest)
    else:
        subNets.append(featureList)

def findSubnet(featTest, featRest):
    found = True
    while found:
        newTestList = []
        #print "candidates: ", len(featTest)
        #print "search in: ", len(featRest)
        #print "-------------"
        for fT in featTest:
            for fR in featRest:
                if not fT.geometry().disjoint(fR.geometry()):
                    #print "!"
                    newTestList.append(fR)
                    #addRubberBand(fR.geometry())

        featTest += newTestList

        if newTestList == []:
            found = False
        else:
            #print "Found (undis)joining segments"
            for fn in newTestList:
                if fn in featRest:
                    featRest.remove(fn)
                    #print "removed ", fn
                else:
                    pass
                    #print "allready removed ", fn

    return featTest, featRest

def addRubberBand(theGeom):
    rubberBand.append(QgsRubberBand(c, False))
    rubberBand[-1].setToGeometry(theGeom, None)
    rubberBand[-1].setColor(QColor(255,0,0))
    rubberBand[-1].setWidth(3)

makeSubnets(flist)

fields = QgsFields()
fields.append(QgsField('subnet', QVariant.Int))
writer = QgsVectorFileWriter(lOut, None, fields, QGis.WKBLineString,     v.crs())

net = 0
for sn in subNets:
    for f in sn:
        #print net, f
        feat = QgsFeature()
        feat.setFields(fields)
        feat.setGeometry(f.geometry())
        feat.setAttribute('subnet', net)
        writer.addFeature(feat)
    net += 1
del writer

UPDATE #2:

To create a geoprocessing script do the following (I've got the german gui, so I try to translate in en):

A: Menu 'Processing' -> 'Toolbox' (appears as a dock on the right)

B: Under 'Scripts [...]' -> 'Tools' doubleclick 'create new script'

enter image description here

An Editor with a little toolbar appears, in wich you copy the code above. Herein you can:

C: Save the script. It appears (in this case) in the group 'Networking' or in whatever group you write in the first line of the script ##MyGroup=group. Be aware not to write blanks in the ##-lines!!!

D: Start the script with the two little gears. A gui appears (cp. above) with the in- and output layers defined in the script line 2 and 3. When saved, alteratively start the script by doubleclicking its name under 'scripts' > 'mygroup' > 'myscriptname' (if saved under myscriptname.py)

enter image description here

Jochen Schwarze
  • 14,605
  • 7
  • 49
  • 117
  • Thanks for your comment @jochen-schwarze. I tried this plugin again now, and I get a Python error: File "C:/Users/imc/.qgis2/python/plugins\QGISMergeLines-master\merge_lines.py", line 216, in onStart self.joinLines( inputLayer, params ) File "C:/Users/imc/.qgis2/python/plugins\QGISMergeLines-master\merge_lines.py", line 312, in joinLines deletedFeaturesID += delFeaturesID UnboundLocalError: local variable 'delFeaturesID' referenced before assignment

    Sorry for long error log. I will delete it again if we find a solution.

    – Ilias Jun 14 '16 at 12:29
  • I'll test this with 2.14.3 and see what happens... – Jochen Schwarze Jun 14 '16 at 12:31
  • I get the same error with 2.14.3. Could you make yourself familiar with how QGIS geoprocessing scripts work in general? I think I can find a solution using some lines of python... – Jochen Schwarze Jun 14 '16 at 13:57
  • OK, I will try to look into some Python tutorials. Furthermore I'll notify the developer of the plugin. – Ilias Jun 15 '16 at 07:10
  • test the script above. it's bit of quick and dirty, and silent. If I get an answer to this http://gis.stackexchange.com/questions/198706/qgis-geoprocessing-script-output i'll update it. perhaps it's worth writing a plugin? – Jochen Schwarze Jun 16 '16 at 13:38
  • Looks like great work. I've been trying to look into Python lately and seem to succeed in copying the script into QGIS. Unfortunately I receive an error message maybe related to undefined functions. I have provided with a link to a screenshot :
    Screenshot
    – Ilias Jun 17 '16 at 08:35
  • Sorry the delay, just back from holidays. I see, you simply did not create a geoprocessing script. I'll update the answer above with some screenshots for explanation. – Jochen Schwarze Jun 23 '16 at 09:13
  • Late reply, but I've been trying to get it to work for some days, however I get an error message: "[Errno 9] Bad file descriptor See log for more details".

    Screenshot: Errno 9
    Btw. I have update to the latest QGIS.

    – Ilias Jul 04 '16 at 09:03
  • No problem ;-) , in the script (in the version above) you can see some print-statements, generating some output on the python console. If you have this open (Menu Plugins > Python console) this error message should disappear. For geoprocessing scripts this is bad style, and if you compare the script to the last screenshot (with the C and D) you can see that I replaced the print "sth"'s with progress.setText("sth")'s which generates output directly in the scripts gui (protocol tab). One never stops learning... You could replace the print's as an exercise :-) – Jochen Schwarze Jul 04 '16 at 09:13
  • But I think I'll try to put my solution to github and provide you the link when I'm done... – Jochen Schwarze Jul 04 '16 at 09:15
  • @JochenSchwarze do you plan to publish your code somewhere with a more adequate code license then cc-by-sa 3.0 (say GPL 2/3) and or to port it to QGIS 3? I've modified it by myself in order to let it work on QGIS 3, but I'm unsure if I can publish my modified version on GitHub with GPL 2/3. – Andrea Giudiceandrea Aug 31 '21 at 23:06
  • @AndreasGiudiceandrea Glad that my code is somehow usefuI! I didn't plan to port this to QGIS 3 because it was just to solve this issue. Don't worry to to publish your modified version with GPL! – Jochen Schwarze Sep 01 '21 at 13:57
  • Thanks @JochenSchwarze! In the mean time I've ported the code to QGIS 3 with the same license https://gist.github.com/agiudiceandrea/621e4f0f2804e49cbd6f2a4314c0e482 I will use GPL 2+ if I decide to publish it as a processing plugin for QGIS 3. – Andrea Giudiceandrea Sep 03 '21 at 12:09
-4

Alternative solution (QGIS 3.8):

  1. Processing Toolbox> v.clean:

    • Use the tools "break", "snap", "rmdup", "rmline"
    • Set the threshold in 0.3,0,0 (replace 3 by the maximum distance for which the snapping must be done)
  2. Processing Toolbox> Regroup

  3. Processing Toolbox> Merge Lines

  4. Processing Toolbox> Cut with lines

    • For this step, just cut the layer with itself

If necessary a second v.clean treatment can be applied to ensure that the topology is good. Deleting duplicate / null geometry can also be useful.

NOTE: If someone could develop a small plugin for this purpose it would be great :), this feature is only available in the Advanced version of ArcGIS (the most expensive).

  • 5
    What did you mean by Regroup? Can't find it in the toolbox. In which part of the toolbox is it? Can you please post a screenshot? – Techie_Gus Oct 21 '19 at 10:25
  • Can you clarify, is this solution applicable in Arcgis or QGIS? As mentioned in the comment above Regroup is not the QGIS Processing Toolbox. – mapperx Oct 21 '21 at 10:29