web-dev-qa-db-fra.com

Vérifier si un géopoint avec latitude et longitude se trouve dans un fichier de formes

Comment puis-je vérifier si un géopoint se trouve dans la zone d'un fichier de formes donné? 

J'ai réussi à charger un fichier de formes en python, mais je ne peux pas aller plus loin.

20
Gerald Bäck

Ceci est une adaptation de la réponse de yosukesabai.

Je voulais m'assurer que le point que je cherchais se trouvait dans le même système de projection que le fichier de formes. J'ai donc ajouté du code pour cela.

Je ne comprenais pas pourquoi il effectuait un test de contenance sur ply = feat_in.GetGeometryRef() (dans mon test, les choses semblaient tout aussi bien fonctionner sans lui), alors je l'ai supprimé.

J'ai également amélioré les commentaires pour mieux expliquer ce qui se passe (si je comprends bien).

#!/usr/bin/python
import ogr
from IPython import embed
import sys

drv = ogr.GetDriverByName('ESRI Shapefile') #We will load a shape file
ds_in = drv.Open("MN.shp")    #Get the contents of the shape file
lyr_in = ds_in.GetLayer(0)    #Get the shape file's first layer

#Put the title of the field you are interested in here
idx_reg = lyr_in.GetLayerDefn().GetFieldIndex("P_Loc_Nm")

#If the latitude/longitude we're going to use is not in the projection
#of the shapefile, then we will get erroneous results.
#The following assumes that the latitude longitude is in WGS84
#This is identified by the number "4326", as in "EPSG:4326"
#We will create a transformation between this and the shapefile's
#project, whatever it may be
geo_ref = lyr_in.GetSpatialRef()
point_ref=ogr.osr.SpatialReference()
point_ref.ImportFromEPSG(4326)
ctran=ogr.osr.CoordinateTransformation(point_ref,geo_ref)

def check(lon, lat):
    #Transform incoming longitude/latitude to the shapefile's projection
    [lon,lat,z]=ctran.TransformPoint(lon,lat)

    #Create a point
    pt = ogr.Geometry(ogr.wkbPoint)
    pt.SetPoint_2D(0, lon, lat)

    #Set up a spatial filter such that the only features we see when we
    #loop through "lyr_in" are those which overlap the point defined above
    lyr_in.SetSpatialFilter(pt)

    #Loop through the overlapped features and display the field of interest
    for feat_in in lyr_in:
        print lon, lat, feat_in.GetFieldAsString(idx_reg)

#Take command-line input and do all this
check(float(sys.argv[1]),float(sys.argv[2]))
#check(-95,47)

Ce site , ce site , et ce site ont été utiles en ce qui concerne le contrôle de la projection. EPSG: 4326

16
Richard

Une autre option consiste à utiliser Shapely (une bibliothèque Python basée sur GEOS, le moteur de PostGIS) et Fiona (essentiellement pour lire/écrire des fichiers):

import fiona
import shapely

with fiona.open("path/to/shapefile.shp") as fiona_collection:

    # In this case, we'll assume the shapefile only has one record/layer (e.g., the shapefile
    # is just for the borders of a single country, etc.).
    shapefile_record = fiona_collection.next()

    # Use Shapely to create the polygon
    shape = shapely.geometry.asShape( shapefile_record['geometry'] )

    point = shapely.geometry.Point(32.398516, -39.754028) # longitude, latitude

    # Alternative: if point.within(shape)
    if shape.contains(point):
        print "Found shape for point."

Notez que les tests de point sur polygone peuvent être coûteux si le polygone est volumineux/complexe (par exemple, des fichiers de formes pour certains pays dont le littoral est extrêmement irrégulier). Dans certains cas, il peut être utile d’utiliser des cadres de sélection pour éliminer rapidement les problèmes avant de procéder au test plus intensif:

minx, miny, maxx, maxy = shape.bounds
bounding_box = shapely.geometry.box(minx, miny, maxx, maxy)

if bounding_box.contains(point):
    ...

Enfin, gardez à l'esprit qu'il faut un certain temps pour charger et analyser des fichiers de formes volumineux/irréguliers (malheureusement, ces types de polygones sont souvent coûteux à garder en mémoire également).

29
Clint Harris

Voici une solution simple basée sur pyshp et galbée .

Supposons que votre fichier de formes ne contienne qu'un seul polygone (mais vous pouvez facilement l'adapter à plusieurs polygones):

import shapefile
from shapely.geometry import shape, Point

# read your shapefile
r = shapefile.Reader("your_shapefile.shp")

# get the shapes
shapes = r.shapes()

# build a shapely polygon from your shape
polygon = shape(shapes[0])    

def check(lon, lat):
    # build a shapely point from your geopoint
    point = Point(lon, lat)

    # the contains function does exactly what you want
    return polygon.contains(point)
7
chilladx
2
ViennaMike

j'ai fait presque exactement ce que vous faites hier en utilisant gdal ogr avec une liaison python. Cela ressemblait à ceci.

import ogr

# load the shape file as a layer
drv    = ogr.GetDriverByName('ESRI Shapefile')
ds_in  = drv.Open("./shp_reg/satreg_etx12_wgs84.shp")
lyr_in = ds_in.GetLayer(0)

# field index for which i want the data extracted 
# ("satreg2" was what i was looking for)
idx_reg = lyr_in.GetLayerDefn().GetFieldIndex("satreg2")


def check(lon, lat):
  # create point geometry
  pt = ogr.Geometry(ogr.wkbPoint)
  pt.SetPoint_2D(0, lon, lat)
  lyr_in.SetSpatialFilter(pt)

  # go over all the polygons in the layer see if one include the point
  for feat_in in lyr_in:
    # roughly subsets features, instead of go over everything
    ply = feat_in.GetGeometryRef()

    # test
    if ply.Contains(pt):
      # TODO do what you need to do here
      print(lon, lat, feat_in.GetFieldAsString(idx_reg))
2
yosukesabai

Une façon de procéder consiste à lire le fichier de forme ESRI à l’aide de la bibliothèque OGR http://www.gdal.org/ogr puis à utiliser la bibliothèque GEOS geometry http://trac.osgeo.org/geos/ pour faire le test de point-dans-polygone. Ceci nécessite une programmation en C/C++.

Il existe également une interface python vers GEOS à l’adresse http://sgillies.net/blog/14/python-geos-module/ (que je n’ai jamais utilisée). Peut-être que c'est ce que tu veux?

Une autre solution consiste à utiliser la bibliothèque http://geotools.org/ . En Java.

J'ai aussi mon propre logiciel Java pour le faire (que vous pouvez télécharger À partir de http://www.mapyrus.org plus jts.jar à partir de http://www.vividsolutions.com/products .asp ). Vous n'avez besoin que d'une commande texte Fichier inside.mapyrus contenant Les lignes suivantes pour vérifier si un point se trouve à l'intérieur du premier polygone Du fichier de forme ESRI:

dataset "shapefile", "us_states.shp"
fetch
print contains(GEOMETRY, -120, 46)

Et courir avec:

Java -cp mapyrus.jar:jts-1.8.jar org.mapyrus.Mapyrus inside.mapyrus

Il imprimera un 1 si le point est à l'intérieur, 0 sinon.

Vous pouvez également obtenir de bonnes réponses si vous postez cette question sur https://gis.stackexchange.com/

1
Simon C

Si vous voulez savoir quel polygone (à partir d'un fichier de formes qui en contient) contient un point donné (et vous en avez aussi beaucoup), le moyen le plus rapide est d'utiliser postgis. En fait, j’ai implémenté une version basée sur fiona, en utilisant les réponses fournies ici, mais c’était terriblement lent (j’utilisais d’abord le multitraitement et la vérification de la boîte de sélection). 400 minutes de traitement = 50k points. En utilisant postgis, cela a pris moins de 10 secondes. Les index des arbres B sont efficaces!

shp2pgsql -s 4326 shapes.shp > shapes.sql

Cela générera un fichier SQL contenant les informations des fichiers de formes, créera une base de données avec support Postgis et exécutera ce SQL. Créez un index Gist sur la colonne geom. Ensuite, pour trouver le nom du polygone:

sql="SELECT name FROM shapes WHERE ST_Contains(geom,ST_SetSRID(ST_MakePoint(%s,%s),4326));"
cur.execute(sql,(x,y))
0
Fábio Dias