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.
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
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).
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)
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))
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/
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))