6

I have a point layer with close to 60.000 points that depict a river course (pixel extracts from satellite image). CRS of the layer is Geographic (Lat, Long).

The pixels are ordered ascendingly from source to outlet with an "ID" and each pixel has latitude and longitude. I want to know the distance in meter between two consecutive points (e.g. between point "ID"=35144 and "ID"=35145, between "ID"=35145 and "ID"=35146, etc.). This sounds very simple but I tried in numerous ways but failed.

enter image description here

I have tried:

  • to transform the point layer to a line layer to measure the line segments. However, using the Vector Geometry (points to path) or diverse plugins such as NNJoin or MMQGIS I loose the vertexes (individual pixels) and therefore the point to point distance
  • to create a distance matrix but this gets messy too, because it is the same layer. Also, other "nearest neighbour"-tools do not work since it is the same layer.
  • to use the field calculator and loop through the rows of my dataset --> but how?

3 Answers 3

6

You can use the following script in QGIS Python Editor. It returns the distance in meter for geographic crs.

Note: ID field should be integer/numeric. If it is string, you get wrong order like: 1, 10, 11, ..., 2, 20, 21, ..., 3, 30, 31, ...

layer_name = 'POINT_LAYER_NAME'
id_field_name = 'ID'

# get layer by name
layer = QgsProject.instance().mapLayersByName(layer_name)[0]

# Add new field. DIST_TO_NXT: distance to the next feature
layer.dataProvider().addAttributes([QgsField("DIST_TO_NXT",  QVariant.Double, len=15, prec=3)])
layer.updateFields()

# get the sorted feature list
request = QgsFeatureRequest().addOrderBy(id_field_name)
feats = list(layer.getFeatures(request))

# find the field's index
field_index = layer.fields().indexFromName("DIST_TO_NXT")

da = QgsDistanceArea()
da.setEllipsoid(layer.crs().ellipsoidAcronym())
# or maybe 
# da.setEllipsoid(QgsProject.instance().ellipsoid())

# skipped the last point, not to get IndexError.
for i in range(len(feats[:-1])):
    p1 = feats[i].geometry().asPoint()
    p2 = feats[i+1].geometry().asPoint()
    
    distance = da.measureLine(p1, p2)
    
    layer.dataProvider().changeAttributeValues(
                            {feats[i].id(): {field_index: distance}})

enter image description here

PS: The last distance is always NULL.

5
  • Nice solution, I will also appropriate the last distance with NULL (vice versa to @BERA), but maybe this 0 (zero).
    – Taras
    Jan 26 at 7:38
  • Hello. Can you help me with the calculation. I create a layer named POINT_LAYER_NAME by adding 4 points with an ID field. I enter the script and QGIS returns a message, Layer 'POINT_LAYER_NAME': Field -1 from object 2 doesnt exist. Why it happens that way ? Feb 6 at 14:29
  • 1
    @NikolayYordanov Did you specify a field name in field_index = layer.fields().indexFromName("DIST_TO_NXT"). You have to change DIST_TO_NXT to your field name. Feb 6 at 21:23
  • Thanks, it works fine but when the points are more than 10, something goes wrong. The distance between the points in my work file are approximately 130 meters away. When I add 10 points with an approximate distance of 130 meters the calculation looks like this: 1-1255, 2-139, 3-141, 4-134, 5-133, 6-132, 7-173, 8-158, 9-Null, 10-1126. Where am I wrong ? Feb 7 at 16:01
  • 1
    @NikolayYordanov You should also specify id_field_name (containing values 1, 2, ...10 ). In this example, it is ID. And ID field should be numeric, not string. Your id field is probably string so QGIS returns the order as 1, 10, 11, 2, 3, 4. ... Feb 8 at 13:28
5

I'm sure there's some less complicated way, but you can use pyqgis like this:

lyr = QgsProject.instance().mapLayersByName('Vertices')[0] #Change Vertices to the name of your layer

orderfield = 'vertex_index' #Change to the name of you order field
distancefield = 'distance' #Field to store the distances in. Add before executing script

features = [[f.id(), f[orderfield], f.geometry()] for f in lyr.getFeatures()] #List all points id, orderfield value and geometry
features.sort(key=lambda x: x[1]) #Sort the list by orderfield
distancefieldindex = lyr.fields().indexFromName(distancefield) #Find index of distancefield

attrmap = {} #A dictionary to store each points id, distance field index and distance in
for e, (f1, f2) in enumerate(zip(features[::1], features[1::1])): #For each point pair, for example point 1 and point2, then point2 and point3
    if e==0: #If first point set distance to 0
        attrmap[f1[0]]={distancefieldindex:0}
    else: #Else calculate the distance between the two points
        attrmap[f1[0]]={distancefieldindex:f1[-1].distance(f2[-1])}

lyr.dataProvider().changeAttributeValues(attrmap) #Update the values
print('Done')

enter image description here

0
4

I am not sure if this is the best / fastest way, but I think it should work for you:

length(
    make_line(
        $geometry,
        geometry(
            get_feature_by_id('*NAME/ID_of_your_layer*', "*ID_field*"+1)
            )
        )
    )

For "ID" 1 you will get the length from 1 to 2, for 2 you will get 2 to 3 ... for your last point you won't get a length.

2
  • @ Kadir : thanks for that proposition. The formula is correct and a distance is calculated. However, the results are in degrees and are subsequently rounded to 0 if the decimal number is not chosen with enough digits... (the distance between the points is small, between 10 and 20 meters) . In my properties I had set "meters" for area/distance calculations but this doesn't apply to the layer. How can I first transform the Lat Long of the dataset to something metric before doing the calculation you suggest ? Thanks!
    – sapho
    Jan 25 at 15:44
  • 1
    use the transform function to project from a geographic to a metric system: docs.qgis.org/3.22/en/docs/user_manual/expressions/…
    – pigreco
    Jan 26 at 8:12

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.