8

In QGIS, I want to merge features lines that same attribute value and are adjacent. What characterizes a line to be adjacent to another is the final vertex of a line to be coincident with the initial vertex of another line.

The Dissolve command does not consider the adjacency criterion. The tool merged features that have equal attribute value. The figure shows an example, where numbers represent the IDs of each line. There are 3 lines that have the attribute value equal to 17. That way, if they have the same attribute and are adjacent then should be merged. There are also 2 lines that have the attribute value equal to 22, but they are not adjacent and so should not be merged.

The problem of the Dissolve tool in QGIS is that the criterion is based only on the value of the attribute. In this way, when executing the tool, the lines 22 will be unified creating multipart.

Another tool is the Singleparts to multipart which also does not consider adjacency. Would there be another way to merge lines in QGIS?

I know that in ArcGIS the Dissolve tool considers adjacency if "Create multipart features" option is unchecked. I'm looking for something similar.

enter image description here

mgri
  • 16,159
  • 6
  • 47
  • 80
Jayme Muzzi
  • 335
  • 4
  • 14
  • 2
    Interesting question. Would you accept a solution using PyQGIS? – mgri Feb 13 '17 at 18:45
  • unanswered duplicate http://gis.stackexchange.com/questions/203065/how-to-join-segments-of-a-line-in-qgis?rq=1 & related http://gis.stackexchange.com/questions/173446/dissolve-line-segments-in-qgis-if-they-touch-and-fall-within-the-same-class-to?rq=1 – underdark Feb 13 '17 at 21:27
  • Actually the first two steps of http://gis.stackexchange.com/a/174991/187 should work for you – underdark Feb 13 '17 at 21:29
  • 2
    Do you mean by "adjacent" that ending vertex of one line is the starting vertex of the other? – user30184 Feb 13 '17 at 21:35
  • @user30184 yes. That's exactly it. – Jayme Muzzi Feb 14 '17 at 10:04
  • @underdark It does not work (http://gis.stackexchange.com/a/174991/82192). As I mentioned the Dissolve command and Singleparts to multipart do not consider adjacency (the ending vertex of one line is the starting vertex of the other). – Jayme Muzzi Feb 14 '17 at 10:15
  • Do you want to merge adjacent lines even if they have different attributes? Like glue highway=primary and highway=secondary segments together? Or only combine adjacent lines if they both are highway=primary? – user30184 Feb 14 '17 at 21:44
  • @user30184 Only adjacent lines. The merge of the problem is based on the attribute is that lines that have the same attribute but are not adjacent, will be merged (multipart lines).Thus the merge tool should also be capable of merged lines based on, for example, the geometry (the ending vertex line is one of the starting vertex of the other). – Jayme Muzzi Feb 14 '17 at 22:11
  • I am not sure about QGIS but with another software I have used union/merge by attribute, which makes multilinestring, and then exploded that into parts -> ready. – user30184 Feb 14 '17 at 22:22
  • @JaymeMuzzi I'm probably near to a solution. As far as I know, are you only interested in the adjacent features? Since you want to dissolve those features, you don't care about the attributes. So, in the image you attached, all those features need to be dissolved because they are adjacent. Am I right? – mgri Feb 17 '17 at 18:23
  • @mgri The attributes have to remain, otherwise the final result is a single line. Only adjacent arcs (line segments) with the same ID can be merged. In the attached figure, arcs 20 and 22 can not be merged because they have different ID numbers, although they are adjacent. However, the three arcs of ID 17 must be merged because they have the same attribute (ID = 17) and are adjacent. – Jayme Muzzi Feb 18 '17 at 20:05
  • 1
    Maybe I'm on it. Please, see my answer and let me know if it works! – mgri Feb 19 '17 at 11:42

1 Answers1

7

You may try to use this code as a new script from Processing Toolbox (there's a pure Python function and I isolated it for the sake of clearness):

##Lines=vector line

from qgis.core import *

def find_adjacent(selected_ids): # for finding adjacent features
    outlist = []
    outinds = []
    outset = set()
    for j, l in enumerate(selected_ids):
        as_set = set(l)
        inds = []
        for k in outset.copy():
            if outlist[k] & as_set:
                outset.remove(k)
                as_set |= outlist[k]
                inds.extend(outinds[k])
        outset.add(j)
        outlist.append(as_set)
        outinds.append(inds + [j])
    outinds = [outinds[j] for j in outset]
    del outset, outlist
    result = [[selected_ids[j] for j in k] for k in outinds]
    return result

layer = processing.getObject(Lines)
crs = layer.crs().toWkt()

# Create the output layer
outLayer = QgsVectorLayer('Linestring?crs='+ crs, 'snapped' , 'memory')
prov = outLayer.dataProvider()
fields = layer.pendingFields()
prov.addAttributes(fields)
outLayer.updateFields()

already_processed = []
for feat in layer.getFeatures():
    attrs = feat.attributes()
    geom = feat.geometry()
    curr_id = feat["ID"]
    if curr_id not in already_processed:
        query = '"ID" = %s' % (curr_id)
        selection = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
        selected_ids = [k.geometry().asPolyline() for k in selection]
        adjacent_feats = find_adjacent(selected_ids)
        for f in adjacent_feats:
            first = True
            for x in xrange(0, len(f)):
                geom = (QgsGeometry.fromPolyline([QgsPoint(w) for w in f[x]]))
                if first:
                    outFeat = QgsFeature()
                    outFeat.setGeometry(geom)
                    outGeom = outFeat.geometry()
                    first = False
                else:
                    outGeom = outGeom.combine(geom)
            outFeat.setAttributes(attrs)
            outFeat.setGeometry(outGeom)
            prov.addFeatures([outFeat])
        already_processed.append(curr_id)
    else:
        continue

# Add the layer to the Layers panel
QgsMapLayerRegistry.instance().addMapLayer(outLayer)

You only need to select your input layer and the dissolving-by-adjacency operation is automatically done. I assumed to order the features using the ID field (I called it "ID", but remember to change it with the real name in the query = '"ID" = %s' % (curr_id) line ). The result is a new linestring memory layer.

Let me explain it with an example. Starting from this layer (12 features, one label for each feature):

enter image description here

I obtain a new memory layer with 9 features (one label for each feature):

enter image description here

mgri
  • 16,159
  • 6
  • 47
  • 80
  • 1
    Woow is amazing. That's exactly what I was looking for. Merge features lines that same attribute value and are adjacent. Thank you very much. I think this type of operation should be available in an upcoming QGIS update. Seeing the Dissove command considers only the tabular information (same attribute value). Where I work this type of operation is recurrent. To work I had to add the line: import processing – Jayme Muzzi Feb 21 '17 at 18:26
  • @JaymeMuzzi I'm glad it worked for you too! I think it could be an useful feature for the upcoming version. – mgri Feb 21 '17 at 18:32