I've coded the script below in PyQGIS 3 to get the azimuth of starting or ending points for a line layer that touches a border. It works fine, but I want to populate the azimuth field layer with the value from azDict and doesn't work.

from shapely.geometry import LineString, Point, Polygon, LinearRing

lLayer = QgsProject.instance().mapLayersByName('roads')[0]
pLayer = QgsProject.instance().mapLayersByName('aux_point')[0]
polLayer = QgsProject.instance().mapLayersByName('aux_boundary')[0]
feat = QgsFeature()
pr = pLayer.dataProvider()
vDict = {} 
azDict = {}

# iterates in vertex lines to get dict = {Point1:Point2}
for road in lLayer.getFeatures():
    if road.geometry() is not None:
        line = road.geometry().asMultiPolyline()
        for vrtx in line:
            stp = QgsPointXY(line[0][0]) #  start point
            scp = QgsPointXY(line[0][1]) #  second point
            vDict[stp] = vDict.setdefault(stp, scp)
            edp = QgsPointXY(line[-1][-1]) #  end point
            lbo = QgsPointXY(line[-1][-2]) #  last but one point
            vDict[edp] = vDict.setdefault(edp, lbo)

#  iterates in vDict to calculates the azimuth
for k,v in vDict.items():
    azim = k.azimuth(v)
    azDict[k] = azDict.setdefault(k,azim) #  gives a dict = {Point:Azimuth}

#  begin the spatial filter
for polygon in polLayer.getFeatures(): #  transforms my boundary in a shapely LinearRing
    if polygon.geometry() is not None:
        poly = polygon.geometry().asMultiPolygon()
pol = Polygon(poly[0][0]) #  creates a shapely polygon
linearring = LinearRing(list(pol.exterior.coords))

#  iterates in azDict to filter spatially
for k,v in azDict.items():
    if linearring.distance(Point(k)) < 1:
        orientation = pr.fieldNameIndex("azimuth")
        #  try to write the azimuth in the field but doesn't works
        pLayer.changeAttributeValue(feat.id(), orientation, v)

Am I losing something?

  • So azDict is a dictionary where the keys are QGSPointXY objects and the values are Azimuth values which are floating point numbers? Do you get an error? Do the fields not change? What if you do pLayer.changeAttributes(feat.id(), orientation, 99.9) - does it change? That will tell you if v is the problem. Maybe its a QFloat, or a string, or some other weird type. Is it actually getting to that code or is the if not true?
    – Spacedman
    Commented Jul 8, 2019 at 21:20
  • @Spacedman, yes azDict it's something like azDict = {(X,Y):99.9}. There isn't error messages and the fields doesn't changes. I put a float instead of v and doesn't make any changes. I'll put all the code in question maybe it helps. Commented Jul 8, 2019 at 23:06
  • I can't see anything wrong with the code that changes the attribute table but I can't test anything else without data like yours. I would try printing out the values of feat.id(), orientation, and v just before you try pLayer.changeAttributeValue to check that line is being exeuted and what the values are.
    – Spacedman
    Commented Jul 9, 2019 at 6:41

1 Answer 1


You could try an approach like this. I am not sure exactly what your azDict looks like, but I have created an example dictionary based on your comment, so if yours looks different, you may modify accordingly. Also I have created a memory layer with a field called 'Azimuth'. If you are working with an existing layer, you can ignore the first part of my script (which creates the memory layer and adds the 'Azimuth' field).

# just an example dictionary
azDict = {(131.5, -15.0): 99.9, (131.5, -14.75): 85.4, (131.5, -14.5): 93.5, (131.5, -14.25): 35.8, (131.5, -14.0): 78.3}
vl = QgsVectorLayer('Point?crs=EPSG:4326', 'Temp_Azimuth', 'memory')
pr = vl.dataProvider()
pr.addAttributes([QgsField('Azimuth', QVariant.Double)])
# create an empty list to hold our features
feats = []
# create features, set their geometries from dictionary key tuple and store them in a list
for k, v in azDict.items():
    f = QgsFeature()
    geom = QgsGeometry().fromPointXY(QgsPointXY(k[0], k[1]))
# add features to vector layer
# get and store ids of features
ids = [f.id() for f in vl.getFeatures()]
# get id of 'Azimuth' field
fld_id = vl.fields().lookupField('Azimuth')
# extract azimuth values from dictionary and store those in a list
vals = [v for k, v in azDict.items()]
val_idx = -1 # create an index to traverse the items in the value list
for i in ids:
    val_idx = val_idx + 1 # add 1 to list index for each iteration
    # store field and value in a dictionary
    atts = {fld_id: vals[val_idx]} # first index will be 0
    # set new attributes
    pr.changeAttributeValues({i: atts})
# load layer
  • Thanks @Ben W, your answer works fine with some changes to fit in my code. A friend said that I'm mixing dataprovider and buffer editing sessions, and I don't understand because I'm a newbie in pyqgis. Anyway, thanks. Commented Jul 10, 2019 at 11:51

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.