0

I have a MultiPoint layer in QGIS 3.18 that I wanted to add x and y coordinates to in its attribute table. Normally, I can just use field calculator and calculate the new field to be $x or $y.

How do I achieve the same thing in PyQGIS?

My current workflow:

pr = multiPointLayer.dataProvider()
pr.addAttributes([QgsField("x_coord", QVariant.Double), QgsField("y_coord", QVariant.Double)])
multiPointLayer.updateFields()

with edit(multiPointLayer):
    for feature in multiPointLayer.getFeatures():
        feature['x_coord'] = ???
        feature['y_coord'] = ???
        multiPointLayer.updateFeature(feature)

QgsProject.instance().addMapLayer(multiPointLayer)

PyQGIS is unwilling to handle MultiPoint layers, seems like -- and when I use the Add X/Y coordinates algorithm provided by QGIS it wanted me to use single points, but I'd much rather keep it a MultiPoint layer since they can be relatively large.


Turns out, all I had to do is to convert my multipoint layer to singlepoint. When Clipping a point layer, the output point layer gets automatically converted to a multipoint one, which caused the issue. The commenting about a multipoint layer made me aware of the properties of the multipoint type.

3
  • 5
    How are you going to get multiple point vales in a single row? If all the multipoints are degenerate (singleton), you're wasting the storage overhead and costing yourself spatial index inefficiency.
    – Vince
    Aug 23 at 3:25
  • I suppose I gotta convert the multipoint layer from multipart to singlepart first to convert the layer back to a simple point layer. That's a good point about multipoints -- I didn't fully understand what that file type is.
    – WFL.GIS
    Aug 23 at 3:52
  • 2
    Maybe this one can help you: gis.stackexchange.com/questions/380783/… and additionally you may need to loop over each multipoint part.
    – Taras
    Aug 23 at 4:48
3

Here are three solutions. You can either

  • Get a list, array or concatenated string of all x and y coordinates of each MultiPoint
  • Get the x and y coordinate of the centroid of each MultiPoint
  • Get the x and y coordinates of the first point of each MultiPoint

If not using the centroid, you need to loop through the vertices of each MultiPoint-Feature just as you would do it with a MultiLineString or a MultiPolygon.

#multiPointLayer = QgsProject.instance().mapLayersByName('Dissolved')[0] # get a layer
pr = multiPointLayer.dataProvider()
# Note the change of fieldtypes:
pr.addAttributes([
    # Will store the concatenated string of all x and y coordinates of each MultiPoint:
    QgsField("x_coords", QVariant.String),
    QgsField("y_coords", QVariant.String),
    # Will store the x and y coordinate of the centroid of each MultiPoint:
    QgsField("x_coord_centroid", QVariant.Double), 
    QgsField("y_coord_centroid", QVariant.Double),
    # Will store the x and y coordinates of the first point of each MultiPoint:
    QgsField("x_coord_firstpoint", QVariant.Double),
    QgsField("y_coord_firstpoint", QVariant.Double)
])
multiPointLayer.updateFields()

with edit(multiPointLayer):
    for feature in multiPointLayer.getFeatures():
        geom = feature.geometry() # get the geometry of the current feature
        x_arr = [] # create an empty list
        y_arr = [] # create an empty list
        for part in geom.parts(): # loop through the parts of each multipoint feature
            for p in part.vertices(): # now "loop through" each vertex of each part (actually a loop isnt really needed but easier to implement, since each part always has exact one vertex)
                x_arr.append(p.x()) # get the x coordinate of that vertex (p.x()) and append it to the list
                y_arr.append(p.y()) # get the y coordinate of that vertex (p.y()) and append it to the list
        
        # Same as array_to_string(array_foreach(generate_series(0,num_points($geometry)-1),x_at(@element)),','):
        feature['x_coords'] = ','.join(str(x) for x in x_arr) # turn the list of x coordinates into a comma spearated string. Therefore we need to iterate over the list and convert each double value to a string
        feature['y_coords'] = ','.join(str(y) for y in y_arr) # turn the list of y coordinates into a comma spearated string. Therefore we need to iterate over the list and convert each double value to a string
        # Same as x($geometry) and y($geometry):
        feature['x_coord_centroid'] = geom.centroid().asPoint().x() # get the x coordinate of the multipoint features centroid
        feature['y_coord_centroid'] = geom.centroid().asPoint().y() # get the y coordinate of the multipoint features centroid
        # Same as $x and $y:
        feature['x_coord_firstpoint'] = x_arr[0] # get the x coordinate of the first point of each MultiPoint
        feature['y_coord_firstpoint'] = y_arr[0] # get the y coordinate of the first point of each MultiPoint
        
        multiPointLayer.updateFeature(feature)

QgsProject.instance().addMapLayer(multiPointLayer)

A side note on your expressions, not PyQGIS related:

If you type $x or $y in fieldcaluclator on a MultiPoint-layer, you will always get the coordinate of the "first" point. This is mentioned in the function description.

If you use x($geometry) or y($geometry), you will get the centroid for each MultiPoint.

If you want another coordinate than the one from the first point, you can use x_at() or y_at(), e.g. x_at(2) to return the x coordinate of the 3rd point of a MultiPoint.

You can also use x_at() and y_at() to return an array of all x and y coordinates of a MultiPoint by combining it with num_points(), generate_series() and array_foreach(), e.g. by using array_foreach(generate_series(0,num_points($geometry)-1),x_at(@element)) to get an array of all x coordinates of each MultiPoint or array_foreach(generate_series(0,num_points($geometry)-1),y_at(@element)) to get an array of all y coordinates of each MultiPoint.

1
  • Turns out it was a relatively simple fix for me, as I needed to just convert my multipoint layer to a singlepoint one (the layer I was working with was a singlepoint layer but somehow automatically converted to multipoint after it was clipped). But I understood the answer you provided in terms of accessing certain coordinates in an actual multipoint layer. Thank you!
    – WFL.GIS
    Aug 23 at 20:25

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.