4

Dissolve Problem

I am trying to dissolve the grey line layer where the field "dis" ( "anzahl" || '-' || "heizlast") has the same value and the segments are touching ( end_point touches the next segments start_point).

The dissolve function ( with keep disjoint features separate) is not working for this specific example becuse the orange segment and the blue one touch in the end_point. For this kind of solution I thought of geometry by expression:

collect_geometries(
 aggregate(
            layer:='Aggregated',
            aggregate:='array_agg',  --or 'collect' I am not quite sure
            expression:=$geometry,
            filter:=intersects(end_point(@geometry), start_point(geometry(@parent))) 
            AND "dis"  = attribute(  @feature , 'dis')
        ))

However the result is not what I am expecting. I think I read it somewhere that the aggregate function is not working well with the geometry by expression. I am not quite sure that my function is good enough , maybe I need some kind of iteration where more than two segments are compared ? like array_foreach ? I am stuck ....

I just found quite the same question here and tried the expression: Merging line segments using length rules in QGIS

union (
        $geometry, 
        eval (
            'overlay_touches (''line'', $geometry, filter:=id= '  || id || ' and $id <> '  || $id  || ' )[0]'
        )
    )

The problem is that the expression is combining two segments at a time and also generates NULL geometries and duplicates. The expression needs a finishing touch for iterating and collecting all the touching lines where the end_point match the start_point and has the same value. Also the expression that I copied is referencing to "id" ( int) and my field for dissolving "dis" is of type string wich is not working in this context.Here is the part where I need some help.

Result overlay_touches

Retrospectively Dissolving line segments in QGIS if they touch and fall within same class?

I also found the exact the same question ( 8 years old) but the solution by the author himself can not be applicable in this case. I need a more general function otherwise it complicate the situation with the so called "higher" order and splitting by lines.

I tried something similar by myself:

Dissolve ( field: "dis" and keep disjoint feature separate )

In this case the 3 segments ( blue marked ) are being merged because they are touching. I can then generate the vertices of the segments and categorize this by the next expression:

count('Vertices', group_by:=geom_to_wkt($geometry))

Dissolving_Disjoint

Afterwards I would split the undesired merged segments from before with the vertices that are different then 1 and get what I need. BUT choosing this path it has again an undesirable effect for the remaining splitted lines which will changed the direction.

DirectionProblem

5
  • 1
    If you have access to PostGIS, a directed st_lineMerge could be used
    – JGH
    Commented Feb 8 at 16:52
  • Maybe possible to recreate that in spatialite (Virtual Layer) using a self join of the layer if OP can add an unique id column? Some version of SELECT t1.dis, ST_LineMerge(ST_Collect(t1.geometry, t2.geometry)) as geometry FROM layer AS t1 JOIN layer AS t2 ON t1.start_point = t2.end_point AND t1.id <> t2.id GROUP BY t1.dis ? maybe the group by needs to be separate - won't be in front of a PC for a while to test
    – she_weeds
    Commented Feb 9 at 2:02
  • Thanks but unfortunately I don't have access to PostGIS yet and a virtual layer is not gonna help me neither because I need to implement it for automatisation into the graphical model :( I was hopping for a "field calculator" solution / or something suitable for the model. Commented Feb 9 at 6:51
  • 1
    FYI, the graphical modeler has an "Execute SQL" algorithm in the Vector General group, which runs on any source data, not just PostGIS. GDAL also has a similar on under Vector Miscellaneous. Commented Feb 15 at 19:28
  • You absolutely right, I kind of forgot about the SQL in the graphical model. Thanks for reminding me Commented Feb 18 at 16:11

1 Answer 1

1
+50

I deleted my previous answer because it didn't work in all cases.

You have to create for this example an field "dissolveID":

enter image description here

layer = iface.activeLayer() # MultiLineString

anzahl_idx = layer.fields().indexOf('anzahl') # int 
heizlast_idx = layer.fields().indexOf('heizlast') # int
dissolveID_idx = layer.fields().indexOf('dissolveID') # int

f_endpoint = QgsFeature()
neighbour_startpoint = QgsFeature()

with edit(layer):
    for f in layer.getFeatures():
        f['dissolveID'] = NULL
        layer.updateFeature(f)

dissolveID = 0

with edit(layer):
    for f in layer.getFeatures():
        f_refValue = str(f.attributes()[anzahl_idx]) + str(f.attributes()[heizlast_idx])
        f_id = f.id()
        f_dissolveID = f.attributes()[dissolveID_idx]
        f_points = [point for point in f.geometry().asMultiPolyline()]
        endpoint = f_points[0][-1]
        f_endpoint.setGeometry(QgsGeometry.fromPointXY(endpoint))
        
        for neighbour in layer.getFeatures():
            f_dissolveID = f.attributes()[dissolveID_idx]
            neighbour_dissolveID = neighbour.attributes()[dissolveID_idx]
            neighbour_refValue = str(neighbour.attributes()[anzahl_idx]) + str(neighbour.attributes()[heizlast_idx])
            neighbour_id = neighbour.id()
            neighbour_points = [p for p in neighbour.geometry().asMultiPolyline()]
            startpoint = neighbour_points[0][0]
            neighbour_startpoint.setGeometry(QgsGeometry.fromPointXY(startpoint))
            if neighbour_startpoint.geometry().intersects(f_endpoint.geometry()) and  \
                neighbour_refValue == f_refValue and \
                neighbour_id != f_id :
                if f_dissolveID == NULL and \
                    neighbour_dissolveID == NULL:
                    f['dissolveID'] = dissolveID
                    neighbour['dissolveID'] = dissolveID
                    layer.updateFeature(f)
                    layer.updateFeature(neighbour)
                    f_dissolveID = f.attributes()[dissolveID_idx]
                    neighbour_dissolveID = neighbour.attributes()[dissolveID_idx]
                elif f_dissolveID == NULL and \
                    neighbour_dissolveID != NULL:
                    f['dissolveID'] = neighbour_dissolveID
                    layer.updateFeature(f)
                    f_dissolveID = f.attributes()[dissolveID_idx]
                elif f_dissolveID != NULL and \
                    neighbour_dissolveID == NULL:
                    neighbour['dissolveID'] = f_dissolveID
                    layer.updateFeature(neighbour)
                    neighbour_dissolveID = neighbour.attributes()[dissolveID_idx]
                    
        dissolveID += 1

enter image description here

The code above checks if you have the same attributes and it is connected endpoint - startpoint. In some cases you could want to check azimut - angle to check a significant change in direction. Because in my example from above a simple endpoint to startpoint connection will also dissolve Line ID 3 and Line ID 6 if the have the same attributes. Therefore you must calculate the difference between the azimuth of the feature and the neighbour AND define a tolerance. This could be achieved with:

startpoint.azimuth(endpoint)

enter image description here

You could verify your defined tolerance in the first if statement.

enter image description here

Another thing that could be necessary to verify is the amount of nodes at the endpoint of the feature. 1 would be an endpoint. 2 a line and more than 2 a kind of crossing.

count = 0
for i in layer.getFeatures():
    if f_endpoint.geometry().intersects(i.geometry()):
        count += 1

enter image description here

And again you could verify your accepted node count in the first if statement.

If you want you can implement the python script as function in the graphic modeler.

1
  • I am grateful for your answer but unfortunately I am not so into the python scripts…. Not yet. I was hopping for a singular advanced expression. Nevertheless I would give it a try and extract the knowledge out of it. Meanwhile I managed to resolve my problem by my own by adding some additional fields which lead to distinctive groups of the lines that I can later on dissolve as needed. Commented Feb 18 at 16: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.