11

I have a layer which has one feature. I try to move the feature(multipolygon) using the script below:

layer = iface.activeLayer()

for feat in layer.getFeatures():
    prev_pos = feat.geometry().centroid().asPoint()
    success  = feat.geometry().translate(100, 100) # translation
    next_pos = feat.geometry().centroid().asPoint()

    print(success)        
    print(prev_pos)
    print(next_pos)

But I get different results in QGIS 2 and QGIS 3. (The multipolygon has many points, therefore, I printed out only its centroid)

Output in QGIS 2:

0 # means operation succeeded
(552084,433685)
(552184,433785) # changed coordinates

Output in QGIS 3:

0 # means operation succeeded
<QgsPointXY: POINT(552084 433685)>
<QgsPointXY: POINT(552084 433685)> # same coordinates

When I try to move a point feature, I get same results.

Why does not translate method move the feature in QGIS 3? What am I missing?


EDIT: If I create a QgsGeometry object manually, translate method works correctly in QGIS 3.

points = [QgsPointXY(0,0), QgsPointXY(2,0), QgsPointXY(2,2), QgsPointXY(0,2)]
geom = QgsGeometry.fromMultiPolygonXY( [ [points] ] )
print(geom)

geom.translate(100, 100)
print(geom)

Output in QGIS 3:

<QgsGeometry: MultiPolygon (((0 0, 2 0, 2 2, 0 2, 0 0)))>
<QgsGeometry: MultiPolygon (((100 100, 102 100, 102 102, 100 102, 100 100)))>

2 Answers 2

10

To change QgsFeature geometry in QGIS 3 you have to call setGeometry() explicitly.

geom = feat.geometry()
geom.translate(100, 100)
feat.setGeometry(geom)

Explanation:

In QGIS 2 geometry() returns a pointer, so it can be modified in place.

QgsGeometry* QgsFeature::geometry() //QGIS 2

In QGIS 3 geometry() returns value instead of a pointer. Additionally it is const now, so it is not allowed to modify the QgsFeature.

QgsGeometry QgsFeature::geometry() const //QGIS 3

(see https://qgis.org/api/2.18/classQgsFeature.html#ab0a934a1b173ce5ad8d13363c20ef3c8)

0
6

In your examples the manipulated geometry is not written back to data source. The Python Cookbook for PyQGIS offers two possibilities:

(1) using te methods changeAttributeValues(), changeGeometryValues() of the dataProvider (2) do that within an editing buffer, to have the possibility to explicte save in an commit and so on.

a solution for (1) based on your script:

layer = iface.activeLayer()

for feat in layer.getFeatures():
    geom = feat.geometry()
    print(geom.asWkt())
    success  = geom.translate(100, 100) # translation

    fid = feat.id()
    print('id:',fid)
    layer.dataProvider().changeGeometryValues({ fid : geom })

# reread geoms to see if they changed
for feat in layer.getFeatures():
    geom = feat.geometry()
    print(geom.asWkt())
2
  • To write back to data source is next step. The problem is that the geometry (in memory) is not translated in QGIS 3.0 using translate method. But, yes, your suggestion is a part of solution. Using a variable (geom) for feat.geometry() is reasonable. As @bartekschas pointed out in his/her answer, while geometry() returns a pointer in QGIS 2, it doesn't return a pointer in QGIS 3. The problem is related to that. Nov 12, 2018 at 22:49
  • I don't see the problem, I suppose it's the way it's meant to be. What are you trying to achieve with your code? Nov 13, 2018 at 10:05

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.