Geographic Information Systems Asked by jburrfischer on January 8, 2021
I’m attempting to do a spatial join much like the example here: Is there a python option to "join attributes by location"?. However, that approach seems really inefficient / slow. Even running this with a modest 250 points takes almost 2 minutes and it fails entirely on shapefiles with > 1,000 points. Is there a better approach? I’d like to do this entirely in Python without using ArcGIS, QGIS, etc.
I’d also be interested to know if it’s possible to SUM attributes (i.e. population) of all the points that fall within a polygon and join that quantity to the polygon shapefile.
Here is the code I’m trying to convert. I get an error on line 9:
poly['properties']['score'] += point['properties']['score']
which says:
TypeError: unsupported operand type(s) for +=: ‘NoneType’ and ‘float’.
If I replace the “+=” with “=” it runs fine but that doesn’t sum the fields. I’ve also tried making these as integers but that fails as well.
with fiona.open(poly_shp, 'r') as n:
with fiona.open(point_shp,'r') as s:
outSchema = {'geometry': 'Polygon','properties':{'region':'str','score':'float'}}
with fiona.open (out_shp, 'w', 'ESRI Shapefile', outSchema, crs) as output:
for point in s:
for poly in n:
if shape(point['geometry']).within(shape(poly['geometry'])):
poly['properties']['score']) += point['properties']['score'])
output.write({
'properties':{
'region':poly['properties']['NAME'],
'score':poly['properties']['score']},
'geometry':poly['geometry']})
This web page shows how to use a Bounding Box point-in-polygon search before the more expensive Within spatial query of Shapely.
http://rexdouglass.com/fast-spatial-joins-in-python-with-a-spatial-index/
Answered by klewis on January 8, 2021
Use Rtree as an index to perform the much faster joins, then Shapely to do the spatial predicates to determine if a point is actually within a polygon. If done properly, this can be faster than most other GISes.
The second part of your question concerning 'SUM', use a dict
object to accumulate populations using a polygon id as the key. Although, this type of thing is done much more nicely with PostGIS.
Answered by Mike T on January 8, 2021
Fiona returns Python dictionaries and you can not use poly['properties']['score']) += point['properties']['score'])
with a dictionary.
Example of summing attributes using the references given by Mike T:
# read the shapefiles
import fiona
from shapely.geometry import shape
polygons = [pol for pol in fiona.open('poly.shp')]
points = [pt for pt in fiona.open('point.shp')]
# attributes of the polygons
for poly in polygons:
print poly['properties']
OrderedDict([(u'score', 0)])
OrderedDict([(u'score', 0)])
OrderedDict([(u'score', 0)])
# attributes of the points
for pt in points:
print i['properties']
OrderedDict([(u'score', 1)])
.... # (same for the 8 points)
Now, we can use two methods, with or without a spatial index:
without
for i, pt in enumerate(points): point = shape(pt['geometry']) #iterate through polygons for j, poly in enumerate(polygons): if point.within(shape(poly['geometry'])): # sum of attributes values polygons[j]['properties']['score'] = polygons[j]['properties']['score'] + points[i]['properties']['score']
with a R-tree index (you can use pyrtree or rtree)
from rtree import index idx = index.Index() for pos, poly in enumerate(polygons): idx.insert(pos, shape(poly['geometry']).bounds)
#iterate through points for i,pt in enumerate(points): point = shape(pt['geometry'])
for j in idx.intersection(point.coords[0]): if point.within(shape(polygons[j]['geometry'])): polygons[j]['properties']['score'] = polygons[j]['properties']['score'] + points[i]['properties']['score']
Result with the two solutions:
for poly in polygons:
print poly['properties']
OrderedDict([(u'score', 2)]) # 2 points in the polygon
OrderedDict([(u'score', 1)]) # 1 point in the polygon
OrderedDict([(u'score', 1)]) # 1 point in the polygon
What is the difference ?
After:
schema = fiona.open('poly.shp').schema
with fiona.open ('output.shp', 'w', 'ESRI Shapefile', schema) as output:
for poly in polygons:
output.write(poly)
To go further, look at Using Rtree Spatial Indexing With OGR, Shapely, Fiona
Answered by gene on January 8, 2021
Additionally - geopandas now optionally includes rtree
as a dependency, see the github repo
So, instead of following all the (very nice) code above, you could simply do something like:
import geopandas
from geopandas.tools import sjoin
point = geopandas.GeoDataFrame.from_file('point.shp') # or geojson etc
poly = geopandas.GeoDataFrame.from_file('poly.shp')
pointInPolys = sjoin(point, poly, how='left')
pointSumByPoly = pointInPolys.groupby('PolyGroupByField')['fields', 'in', 'grouped', 'output'].agg(['sum'])
To get this snazzy functionality be sure to install the C-library libspatialindex first
EDIT: corrected package imports
Answered by claytonrsh on January 8, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP