Suite

Effectuer une requête spatiale en boucle dans PyQGIS


Ce que j'essaye de faire : boucle à travers un fichier de formes de points et sélectionnez chaque point qui tombe dans un polygone.

Le code suivant est inspiré d'un exemple de requête spatiale que j'ai trouvé dans un livre :

mitte_path = r"D:PythonTestingSelectByLocationmitte.shp" punkte_path = r"D:PythonTestingSelectByLocationpunkte.shp" polygon = QgsVectorLayer(mitte_path, 'Mitte', 'ogr') points = QgsVector_Layer 'Berlin Punkte', 'ogr') QgsMapLayerRegistry.instance().addMapLayer(polygon) QgsMapLayerRegistry.instance().addMapLayer(points) polyFeatures = polygon.getFeatures() pointsCount = 0 pour poly_feat dans polyFeatures : polyGeom = poly_feat. ) pointFeatures = points.getFeatures(QgsFeatureRequest().setFilterRect(polyGeom.boundingBox())) pour point_feat dans pointFeatures : points.select(point_feat.id()) pointsCount += 1 print 'Total :',pointsCount

Cela fonctionne et sélectionne des ensembles de données, mais le problème est qu'il sélectionne par cadre englobant, d'où évidemment le retour de points qui ne m'intéressent pas :

Comment pourrais-je ne retourner que des points dans le polygone sans utiliser qgis:selectbylocation?

j'ai essayé d'utiliser le dans() et se croise() méthodes, mais comme je ne les faisais pas fonctionner, j'ai eu recours au code ci-dessus. Mais peut-être qu'ils sont la clé après tout.


Vous n'avez pas besoin d'une fonction spéciale (comme "Ray Casting"), tout est dans PyQGIS (contient() dans la gestion de la géométrie PyQGIS)

polygons = [entité pour l'entité dans polygons.getFeatures()] points = [entité pour l'entité dans points.getFeatures()] pour pt en points : point = pt.geometry() # uniquement et non pt.geometry().asPolygon( ) pour pol dans les polygones : poly = pol.geometry() si poly.contains(point) : print "ok"

ou en une seule ligne

polygons = [entité pour l'entité dans polygons.getFeatures()] points = [entité pour l'entité dans points.getFeatures()] résultant = [pt pour pt en points pour poly dans les polygones si poly.geometry().contains(pt.geometry ())] imprimer len(résultant)…

Vous pouvez également utiliser directement

[pt.geometry().asPoint() pour pt en points pour poly en polygones si poly.geometry().contains(pt.geometry())]

Le problème ici est que vous devez parcourir toutes les géométries (polygones et points). Il est plus intéressant d'utiliser un index spatial englobant : vous parcourez uniquement les géométries qui ont une chance d'intersecter votre géométrie actuelle ('filtre', regardez Comment accéder efficacement aux fonctionnalités retournées par QgsSpatialIndex ?)


Vous pouvez utiliser l'algorithme "Ray Casting" que j'ai légèrement adapté pour une utilisation avec PyQGIS :

def point_in_poly(point,poly): x = point.x() y = point.y() n = len(poly) inside = False p1x,p1y = poly[0] for i in range(n+1): p2x ,p2y = poly[i % n] si y > min(p1y,p2y) : si y <= max(p1y,p2y) : si x <= max(p1x,p2x) : si p1y != p2y : xints = ( y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x if p1x == p2x or x <= xints: inside = not inside p1x,p1y = p2x,p2y return inside ## Test mapcanvas = iface.mapCanvas () couches = mapcanvas.layers() #Pour polygone polygone = [feature.geometry().asPolygon() pour l'entité dans les couches[1].getFeatures()] points = [feat.geometry().asPoint() pour feat in layer[0].getFeatures()] ## Appelez la fonction avec les points et le polygone count = [0]*(layers[1].featureCount()) pour point en points : i = 0 pour exploit en polygone : if point_in_poly(point, feat[0]) == True: count[i] += 1 i += 1 print count

Appliqué à cette situation :

le résultat, sur la console Python, était :

[2, 2]

Ça a marché.

Remarque sur l'édition :

Code avec la proposition plus concise de gène:

mapcanvas = iface.mapCanvas() couches = mapcanvas.layers() count = [0]*(layers[1].featureCount()) polygone = [fonctionnalité pour la fonctionnalité dans les couches[1].getFeatures()] points = [fonctionnalité pour l'entité dans les couches[0].getFeatures()] pour les points dans les points : i = 0 geo_point = point.geometry() pour pol dans le polygone : geo_pol = pol.geometry() si geo_pol.contains(geo_point): count[i ] += 1 i += 1 nombre d'impressions

Avec les conseils d'un collègue, j'ai finalement réussi à le faire fonctionner en utilisant inside().

Logique générale

  1. obtenir les caractéristiques du ou des polygones
  2. obtenir les caractéristiques des points
  3. boucle sur chaque entité du fichier de polygones, et pour chacune :
    • obtenir la géométrie
    • boucle à travers tous les points
      • obtenir la géométrie d'un point unique
      • tester si la géométrie est dans la géométrie du polygone

Voici le code :

mitte_path = r"D:PythonTestingSelectByLocationmitte.shp" punkte_path = r"D:PythonTestingSelectByLocationpunkte.shp" poly = QgsVectorLayer(mitte_path, 'Mitte', 'ogr') points = QgsVectorLayerLayer 'Berlin Punkte', 'ogr') QgsMapLayerRegistry.instance().addMapLayer(poly) QgsMapLayerRegistry.instance().addMapLayer(points) polyFeatures = poly.getFeatures() pointFeatures = points.getFeatures() pointCounter = 0 pour polyfeat dans polyFeatures : polyGeom = polyfeat.geometry() pour pointFeat dans pointFeatures: pointGeom = pointFeat.geometry() si pointGeom.within(polyGeom): pointCounter += 1 points.select(pointFeat.id()) print 'Total',pointCounter

Cela fonctionnerait aussi avec se croise() à la place de dans(). Lors de l'utilisation de points, il n'a pas d'importance lequel vous utiliseriez, car ils renverront tous les deux le même résultat. Cependant, lors de la vérification des lignes/polygones, cela peut faire une différence importante : inside() renvoie les objets qui sont entièrement à l'intérieur, alors que intersects() renvoie des objets qui sont entièrement à l'intérieur et qui sont partiellement à l'intérieur (c'est-à-dire que couper avec la fonction, comme son nom l'indique).


Voir la vidéo: QGIS Python PyQGIS - Raster Calculator in a Python Script (Octobre 2021).