4

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

3

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

  • 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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