6

I am looking for a method in PyQGIS (QGIS3.x) of reversing the direction of a MultiLineString feature. If there is more than one part, the order of the parts should be reversed.

I have found a similar question - How can I switch line direction in QGIS? - for LineString/Polyline. - which only works for QGIS2 - and modified it slightly for use in QGIS3. However, I can't work out how to adapt the approach for MultiLineStrings.

layer = qgis.utils.iface.mapCanvas().currentLayer()
fid = 1 # feature id
feature = layer.getFeature(1)
geom = feature.geometry()
nodes = geom.asPolyline()
nodes.reverse() 
newgeom = QgsGeometry.fromPolylineXY(nodes)
layer.startEditing()
layer.changeGeometry(feature.id(),newgeom)
layer.commitChanges()

In theory the above approach could be used with newgeom = QgsGeometry.fromMultiPolylineXY() but I can't see how to generate the necessary QgsMultiPolylineXY object.

I have also noted the reversed() function on the QGSMultiLineCurve class, but I couldn't attach the generated QGSMultiLineCurve to the original feature.

I'm not really interested in UI-based approaches, as the features to be reversed are determined programatically.

Any approaches are welcome - if there's a better general approach than above, I'm happy to hear.

2
  • Do you also want to re-order the parts of multiline so that line1-line2-line3 would become line3reversed-line2reversed-line1reversed?
    – user30184
    Mar 1, 2019 at 7:10
  • @user30184 - yes, have added clarification to the question. Mar 1, 2019 at 8:29

1 Answer 1

10

I will focus in the geometry creation part, because the feature editing part is fully explained in many places, including the example inside the question.

I will use some python list comprehension, but the key here is the use of the QgsAbstractGeometry type QgsMultiLineString, which is iterable and converts easly from/to list type.

from the Api, you will see that each QgsGeometry is a container for the "real" QgsAbstractGeometry:

A geometry is the spatial representation of a feature. Since QGIS 2.10, QgsGeometry acts as a generic container for geometry objects. QgsGeometry is implicitly shared, so making copies of geometries is inexpensive. The geometry container class can also be stored inside a QVariant object.

The actual geometry representation is stored as a QgsAbstractGeometry within the container, and can be accessed via the get() method or set using the set() method.

#First, will create an example multilinestring geometry
lines = [[[0,0],[1,1],[2,2]],[[0,1],[1,2],[2,3]],[[0,2],[1,3],[2,4]]]
mls = QgsMultiLineString()
for i in [QgsLineString([QgsPoint(x,y) for x,y in i]) for i in lines]:
    _ = mls.addGeometry(i)

old_geometry = QgsGeometry(mls)

# QgsGeometry can be converted to QgsAbstractGeometry with the
# .get() method, so real example begins HERE:

mls1 = old_geometry.get()
mls2 = QgsMultiLineString()
# For each reversed linestring, visited in reverse order
for i in [QgsLineString([*i][::-1]) for i in [*mls1][::-1]]:
    _ = mls2.addGeometry(i) # add it to new geometry

new_geometry = QgsGeometry(mls2)
old_geometry
#[out]: <QgsGeometry: MultiLineString ((0 0, 1 1, 2 2),(0 1, 1 2, 2 3),(0 2, 1 3, 2 4))>
new_geometry
#[out]: <QgsGeometry: MultiLineString ((2 4, 1 3, 0 2),(2 3, 1 2, 0 1),(2 2, 1 1, 0 0))>

Note here, QgsMultiLineString objects are iterables so, you can expand it to a list of Linestrings using [*list] python syntax.

In the developer forum they say it is recommended to use QgsMultiLineString, QgsLineString, QgsPoint,... etc when creating geometries, one advantage is the easy conversion from/to list conversion.

One thing to be aware of is that if you are using existing features, rather than a newly created one as per the example above, don't try to shortcut the "cloning" process. Given a feature f, use, for example:

geom = f.geometry()
mls1 = geom.get()

rather than

mls1 = f.geometry().get()

Otherwise you will get a crash due to python garbage collection

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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