问题
Here is my question.
1. Intro
- a shapefile in polygon type represent the study area
http://i8.tietuku.com/08fdccbb7e11c0a9.png
- some point located in the whole rectangle map
http://i8.tietuku.com/877f87022bf817b8.png
I want to test whether each point was located within/out the polygon and do some further operation(for example, sum the grid point amount within the study area)
2. My idea
I have two methods all thanks to the information on stack overflow.
2.1 Idea ARasterize the shapefile into raster file and then test.
I haven't done that yet, but I have asked one question here and get an answer.
2.2 Idea BI have tried to using poly.contain()
to test the scatter point's location, but the result wasn't match with the reality.
3. My code based on Idea B:
For example:
- Original data represent by pt(a pandas Dataframe) which contain 1000 grids X,Y.
- shapefile I already shown was the study area, I want to filter the original data leaving only point within this area.
# map four boundaries
xc1,xc2,yc1,yc2 = 113.49805889531724,115.5030664238035,37.39995194888143,38.789235929357105
# grid definition
lon_grid = np.linspace(x_map1,x_map2,38)
lat_grid = np.linspace(y_map1,y_map2,32)
3.1 Preparation
# generate (lon,lat)
xx = lon_grid[pt.X.iloc[:].as_matrix()]
yy = lat_grid[pt.Y.iloc[:].as_matrix()]
sh = (len(xx),2)
data = np.zeros(len(xx)*2).reshape(*sh)
for i in range(0,len(xx),1):
data[i] = np.array([xx[i],yy[i]])
# reading the shapefile
map = Basemap(llcrnrlon=x_map1,llcrnrlat=y_map1,urcrnrlon=x_map2,\
urcrnrlat=y_map2)
map.readshapefile('/xx,'xx')
3.2 Test
patches=[]
for info, shape in zip(map.xxx_info, map.xxx):
x,y=zip(*shape)
patches.append(Polygon(np.array(shape), True) )
for poly in patches:
mask = np.array([poly.contains_point(xy) for xy in data])
- Then, I have a numpy array mask with value 0,1 represent the within/out.
- Combine mask into pt ==> pt = pt[[pt.mask == 1]], I can filter the points
An example for my idea 2But the problem is using
poly,contains_point(xy)
, I couldn't get the results match with my attempt.
sum the value 0,1:
unique, counts = np.unique(mask, return_counts=True)
print np.asarray((unique, counts)).T
#result:
> [[0 7]
[1 3]]
http://i4.tietuku.com/7d156db62c564a30.png
From unique value, there must by 3 point within the shapefile area, but the result shows one point besides.
Another test for 40 points
http://i4.tietuku.com/5fc12514265b5a50.png
4. My problem
The result was wrong, and I haven't figured it out.
But I think the problem may happen by two reasons:
- the polygon shapefile was wrong(a simple polygon which I don't think the problem remains here).
- Using
poly.contains_point(xy)
incorrect.
Add 2016-01-16
Thanks for the answer, the reason I found out was the shapefile itself.
When I change it into shapely.polygon, it works well.
Here is my code and result
c = fiona.open("xxx.shp")
pol = c.next()
geom = shape(pol['geometry'])
poly_data = pol["geometry"]["coordinates"][0]
poly = Polygon(poly_data)
ax.add_patch(plt.Polygon(poly_data))
xx = lon_grid[pt_select.X.iloc[:].as_matrix()]
yy = lat_grid[pt_select.Y.iloc[:].as_matrix()]
sh = (len(xx),2)
points = np.zeros(len(xx)*2).reshape(*sh)
for i in range(0,len(xx),1):
points[i] = np.array([xx[i],yy[i]])
mask = np.array([poly.contains(Point(x, y)) for x, y in points])
ax.plot(points[:, 0], points[:, 1], "rx")
ax.plot(points[mask, 0], points[mask, 1], "ro")
http://i4.tietuku.com/8d895efd3d9d29ff.png
回答1:
you can use shapely:
import numpy as np
from shapely.geometry import Polygon, Point
poly_data = [[0, 0], [0, 1], [1, 0], [0.2, 0.5]]
poly = Polygon(poly_data)
points = np.random.rand(100, 2)
mask = np.array([poly.contains(Point(x, y)) for x, y in points])
and here is the plot code:
import pylab as pl
fig, ax = pl.subplots()
ax.add_patch(pl.Polygon(poly_data))
ax.plot(points[:, 0], points[:, 1], "rx")
ax.plot(points[mask, 0], points[mask, 1], "ro")
the output:
You can also use MultiPoint to speed the calculation:
from shapely.geometry import Polygon, MultiPoint
poly_data = [[0, 0], [0, 1], [1, 0], [0.2, 0.5]]
poly = Polygon(poly_data)
points = np.random.rand(100, 2)
inside_points = np.array(MultiPoint(points).intersection(poly))
you can also use Polygon.contains_point()
in matplotlib:
poly = pl.Polygon(poly_data)
mask = [poly.contains_point(p) for p in points]
来源:https://stackoverflow.com/questions/34825074/testing-point-with-in-out-of-a-vector-shapefile