QGIS - Retrieve start and end features from a line

5.5k Views Asked by At

I'm creating lines by selecting two features from various layers. When I create a line a form pops up. In this form I want to display data from the start and end features of the line. What I'm currently doing is retrieving the vertices as point :

geom = feature.geometry ()
line = geom.asPolyline ()
pointFather = ligne[0]
pointChild = ligne[-1]

then I get the coordinates of each point :

xf = pointFather.x()
yf = pointFather.y()

and then I look into each possible layer to find the features with the same coordinates, just to retrieve the features I just clicked on !

for layer in layerList:
    provider = layer.dataProvider()
    iter = provider.getFeatures()
    for feature in iter:
        geom = feature.geometry().asPoint()
        if geom.x() == xf and geom.y() == yf:    

It must be something easier to do to directly retrieve the start and end features, isn't it ?

EDIT 1 :

here is my try after PCamargo first answer :

def retrieve_feature_from_xy(geom,point,layerList):
  for layer in layerList:
    index = QgsSpatialIndex()
    iter = layer.getFeatures()
    for feat in iter:
        index.insertFeature(feat)
    ids = index.intersects(geom.boundingBox())
    request = QgsFeatureRequest()
    request.setFilterFids(ids)
    iter = layer.getFeatures(request)
    for feat in iter:
        geom2 = feat.geometry().asPoint()
        if geom2.x() == point.x() and geom2.y() == point.y():
            return feat 

EDIT 2 :

Here is my try after PCamargo second comment :

def retrieve_feature_from_xy2(geom,point,layerList):
    allfeatures = {}
    indexes=[]
    ids=[]
    for layer in layerList:
        index = QgsSpatialIndex()
        iter = layer.getFeatures()
        for feat in iter:
            index.insertFeature(feat)
            allfeatures[feat.id()]=feat
        indexes.append(index)
    for index in indexes:
        intersect_ids = index.intersects(geom.boundingBox())
        ids.append(intersect_ids)
    for id in ids:
        for i in id:
            feat=allfeatures[i]
            geom2=feat.geometry().asPoint()
            if geom2.x() == point.x() and geom2.y() == point.y():
                return feat  

EDIT 3

Here is my try after PCamargo third comment :

def retrieve_feature_from_xy3(geom,point,layerList):    
    allfeatures = {}
    indexes=[]
    ids=[]
    indexDict = {}
    intersectsIdsDict = {}

    for layer in layerList:
        index = QgsSpatialIndex()
        iter = layer.getFeatures()
        for feat in iter:
            index.insertFeature(feat)
            allfeatures[layer,feat.id()]=feat
        indexes.append(index)
        indexDict[layer]=index

    for layer, index in indexDict.items():
        intersectsIds = index.intersects(geom.boundingBox())
        intersectsIdsDict[layer]=intersectsIds      

    for layer, index in intersectsIdsDict.items():
        for id in index:
            feat = allfeatures[layer,id]    
            geom2=feat.geometry().asPoint()
            if geom2.x() == point.x() and geom2.y() == point.y():
                return feat
1

There are 1 best solutions below

7
On

Chris,

You can definitely improve the look for similar coordinates (Third part of the code).

Instead of looping through all features in each layer, create a spatial index (https://docs.qgis.org/2.2/en/docs/pyqgis_developer_cookbook/vector.html#using-spatial-index) for each link and use nearestNeighbor.

It would be something like this:

#You only need to create these indices once
indexes=[]
for layer in layerlist:
    index = QgsSpatialIndex()
    for feat in layer:
        index.insertFeature(feat)

    indexes.append(index)

Now that we have the indexes, we can use faster geographic search.

geom = feature.geometry ()

for index in indexes:
    intersect_ids = index.intersects(geom.boundingBox())

intersect_ids is a smaller list of features that are candidates to be equivalent, so you can compare only these features with the feature you selected.

You need to organize this a bit more, but that is the idea