3

I have fieldwork photos stored in a vector layer, geotagged from EXIF GPS data. Sometimes the GPS is off, or even missing, and I need to move their position manually. Unfortunately, the Advanced Digitization Move feature command requires I click on the current (geotagged) position (after selecting the feature first) and only then the corrected position, which is awkward if the geotagged position is say Point (0 0).

As a workaround, I use the Lat Lon tools plugin to copy the desired position to the clipboard (as WKT), and then I've written a PyQGIS feature action to paste that as the feature's geometry. However, following the PyQGIS cookbook the code below uses the dataProvider() to change the geometry, and so the currently open form and attribute table that are invoking the action don't pick up on the change. Even hitting the refresh icon isn't enough.

How could the action code be changed to use the (already active) QGIS editing session instead of going behind its back to the provider?

# find ourselves, layer first
ourLayerId = '[% @layer_id %]'
ourLayer = QgsProject.instance().mapLayer(ourLayerId)

# now the feature, note getFeatures returns an iterator, but will be unique
fid = [% $id %]
featIterator = ourLayer.getFeatures(QgsFeatureRequest().setFilterFid(fid))
feature=featIterator.__next__()

# figure out new geometry, using WKT from clipboard
# should check CRS (if WKT is output of Lat Long tools it should be 4326!). For now assume it's fine.
geomWkt = QgsApplication.clipboard().text()
geom = QgsGeometry.fromWkt(geomWkt)

# make the change. This bypasses the editing cache going to the data provider
result = ourLayer.dataProvider().changeGeometryValues({fid : geom})
2

Solved. Since v3.0, QGis has a changeGeometry method in the layer itself, which does use the edit buffer and so plays nice with the form calling the action. It just hasn't made it into the Python cookbook yet even though it should be a more robust solution than going straight to the dataProvider() even in more straightforward situations.

Simplified action code that now behaves as it should:

# find ourselves
ourLayerId = '[% @layer_id %]'
ourLayer = QgsProject.instance().mapLayer(ourLayerId)
fid = [% $id %]

# figure out new geometry, using WKT from clipboard
geomWkt = QgsApplication.clipboard().text()
geom = QgsGeometry.fromWkt(geomWkt)

# make the change. 
result = ourLayer.changeGeometry(fid,geom)

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.