Monday, 30 December 2019

Adding new field with expression in pyqgis


I want to use PyQGIS in QGIS 2.18 to add a new field to a vector (line) layer and calculate the bearings for each feature as i have been able to do easily in the field calculator using the expression


concat(floor(degrees(azimuth(start_point($geometry), end_point($geometry)))), '° ', 
floor(degrees(azimuth(start_point($geometry), end_point($geometry)))*60 % 60), ''' ',
degrees(azimuth(start_point($geometry), end_point($geometry)))*3600 % 60, '''')

The result of this expression is a string that displays the degrees, minute, seconds value


After a little research, i tried to carry out the task with this script which produced an error.(Actually i was able to use this script to create and update a field that shows the length of lines of the layer, using QVariant.Double and $length for the expression)


from PyQt4.QtCore import QVariant

from qgis.core import QgsField, QgsExpression, QgsFeature

vl = iface.activeLayer()
vl.startEditing()

myField = QgsField( 'Bearing', QVariant.String)
vl.dataProvider().addAttributes([myField])
vl.updateFields()
idx = vl.fieldNameIndex('Bearing')


#next step
exp = QgsExpression('
concat(floor(degrees(azimuth(start_point($geometry), end_point($geometry)))), '° ',
floor(degrees(azimuth(start_point($geometry), end_point($geometry)))*60 % 60), ''' ',
degrees(azimuth(start_point($geometry), end_point($geometry)))*3600 % 60, '''')')
exp.prepare( vl.pendingFields() )

for f in vl.getFeatures():
f[idx] = exp.evaluate( f )
vl.updateFeature( f )


vl.commitChanges()

Where am I going wrong?



Answer



You don't need this kind of expression if you want to work directly with PyQGIS. Following code does the same and it is more legible.


from PyQt4.QtCore import QVariant
import math

layer = iface.activeLayer()


myField = QgsField( 'Bearing', QVariant.String)

layer.dataProvider().addAttributes([myField])
layer.updateFields()

idx = layer.fieldNameIndex('Bearing')

layer.startEditing()


for feat in layer.getFeatures():
pt1 = feat.geometry().asPolyline()[0]
pt2 = feat.geometry().asPolyline()[1]
az = pt1.azimuth(pt2)
if az < 0:
az += 360
minutes = az%1.0*60
seconds = minutes%1.0*60
string = str(int(math.floor(az))) + "\xb0 " + str(int(math.floor(minutes))) + "' " + str(seconds) + "''"


feat[idx] = string
layer.updateFeature( feat )

layer.commitChanges()

I tried it out with shapefile of following image where it can be observed, after running above script, that 'with_form' field (calculated with your field expression) and 'Bearing' field are identical.


enter image description here


No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...