Let's say I have the following to GeoDataFrames of linestrings, one of which represents roads and one of which represents contour lines.
>>> import geopandas as gpd
>>> import geopandas.tools
>>> import shapely
>>> from shapely.geometry import *
>>>
>>> r1=LineString([(-1,2),(3,2.5)])
>>> r2=LineString([(-1,4),(3,0)])
>>> Roads=gpd.GeoDataFrame(['Main St','Spruce St'],geometry=[r1,r2], columns=['Name'])
>>> Roads
Name geometry
0 Main St LINESTRING (-1 2, 3 2.5)
1 Spruce St LINESTRING (-1 4, 3 0)
>>>
>>> c1=LineString(Point(1,2).buffer(.5).exterior)
>>> c2=LineString(Point(1,2).buffer(.75).exterior)
>>> c3=LineString(Point(1,2).buffer(.9).exterior)
>>> Contours=gpd.GeoDataFrame([100,90,80],geometry=[c1,c2,c3], columns=['Elevation'])
>>> Contours
Elevation geometry
0 100 LINESTRING (1.5 2, 1.497592363336099 1.9509914...
1 90 LINESTRING (1.75 2, 1.746388545004148 1.926487...
2 80 LINESTRING (1.9 2, 1.895666254004977 1.9117845...
>>>
If I plot these, they look like this:
There are 3 contour line and 2 roads. I want to find the elevation at each point along each road. Basically I want to intersect roads and contours (which should give me 12 points) and preserve the attributes from both geodataframes (road name and elevation).
I can generate the 12 points as such by using an intersection of the unions of the two geodataframes:
>>> Intersection=gpd.GeoDataFrame(geometry=list(Roads.unary_union.intersection(Contours.unary_union)))
>>> Intersection
geometry
0 POINT (0.1118644118110415 2.13898305147638)
1 POINT (0.2674451642029509 2.158430645525369)
2 POINT (0.3636038969321072 2.636396103067893)
3 POINT (0.4696699141100895 2.530330085889911)
4 POINT (0.5385205980649126 2.192315074758114)
5 POINT (0.6464466094067262 2.353553390593274)
6 POINT (1.353553390593274 1.646446609406726)
7 POINT (1.399321982208571 2.299915247776072)
8 POINT (1.530330085889911 1.46966991411009)
9 POINT (1.636396103067893 1.363603896932107)
10 POINT (1.670759586114587 2.333844948264324)
11 POINT (1.827239686607525 2.353404960825941)
>>>
However, how do I now get the road name and elevation for each of those 12 points? A spatial join does not behave as I would expect and only returns 4 points (all 12 should intersect with the line files since they were created that way by definition).
>>> gpd.tools.sjoin(Intersection, Roads)
geometry index_right Name
2 POINT (0.3636038969321072 2.636396103067893) 1 Spruce St
3 POINT (0.4696699141100895 2.530330085889911) 1 Spruce St
5 POINT (0.6464466094067262 2.353553390593274) 1 Spruce St
6 POINT (1.353553390593274 1.646446609406726) 1 Spruce St
>>>
Any suggestions as to how I can do this?
EDIT: It appears that the issue has to do with how the intersection points are created. If I buffer the roads and contours by a very small amount, the intersection works as expected. See below:
>>> RoadsBuff=gpd.GeoDataFrame(Roads, geometry=Roads.buffer(.000005))
>>> ContoursBuff=gpd.GeoDataFrame(Contours, geometry=Contours.buffer(.000005))
>>>
>>> Join1=gpd.tools.sjoin(Intersection, RoadsBuff).drop('index_right',1).sort_index()
>>> Join2=gpd.tools.sjoin(Join1, ContoursBuff).drop('index_right',1).sort_index()
>>>
>>> Join2
geometry Name Elevation
0 POLYGON ((1.636395933642091 1.363596995290097,... Spruce St 80
1 POLYGON ((1.530329916464109 1.469663012468079,... Spruce St 90
2 POLYGON ((1.353553221167472 1.646439707764716,... Spruce St 100
3 POLYGON ((0.5385239436706243 2.192310454047735... Main St 100
4 POLYGON ((0.2674491823047923 2.158426108877007... Main St 90
5 POLYGON ((0.1118688004427904 2.138978561144256... Main St 80
6 POLYGON ((0.6464467873602107 2.353546141571978... Spruce St 100
7 POLYGON ((0.4696700920635739 2.530322836868614... Spruce St 90
8 POLYGON ((0.3636040748855915 2.636388854046597... Spruce St 80
9 POLYGON ((1.399312865255344 2.299919147068011,... Main St 100
10 POLYGON ((1.670752113626148 2.333849053114361,... Main St 90
11 POLYGON ((1.827232214119086 2.353409065675979,... Main St 80
>>>
The above is the desired output although I'm not sure as to why I have to buffer the lines to get them to intersect the points that were created from the intersection of the lines.
A tutorial on how to intersect point features inside a polygon boundary in python geopandas Intersection is one of the most commonplace geospatial analysis tool in GIS (Geographic Information Systems).
Intersection is one of the most commonplace geospatial analysis tool in GIS (Geographic Information Systems). The simplest intersect method is where various input geometric features (points, polygons, lines) overlap with one another, to derive the overlaid features as the output.
But, the problem is actually in the plotting. You can see that the second geometry in the above is actually a GeometryCollection with one point and a linestring. And it appears that geopandas is silently skipping it while plotting ..
These manipulations are often referred using the language of sets – intersections, unions, and differences. These types of operations are made available in the geopandas library through the overlay () method.
Notice that operations unary_union
and intersection
are made over the geometries inside the GeoDataFrame
, so you lose the data stored in the rest of the columns. I think in this case you have to do it by hand by accessing each geometry in the data frames. The following code:
import geopandas as gpd
from shapely.geometry import LineString, Point
r1=LineString([(-1,2),(3,2.5)])
r2=LineString([(-1,4),(3,0)])
roads=gpd.GeoDataFrame(['Main St','Spruce St'],geometry=[r1,r2], columns=['Name'])
c1=LineString(Point(1,2).buffer(.5).exterior)
c2=LineString(Point(1,2).buffer(.75).exterior)
c3=LineString(Point(1,2).buffer(.9).exterior)
contours=gpd.GeoDataFrame([100,90,80],geometry=[c1,c2,c3], columns=['Elevation'])
columns_data = []
geoms = []
for _, n, r in roads.itertuples():
for _, el, c in contours.itertuples():
intersect = r.intersection(c)
columns_data.append( (n,el) )
geoms.append(intersect)
all_intersection = gpd.GeoDataFrame(columns_data, geometry=geoms,
columns=['Name', 'Elevation'])
print all_intersection
produces:
Name Elevation geometry
0 Main St 100 (POINT (0.5385205980649125 2.192315074758114),...
1 Main St 90 (POINT (0.2674451642029509 2.158430645525369),...
2 Main St 80 (POINT (0.1118644118110415 2.13898305147638), ...
3 Spruce St 100 (POINT (0.6464466094067262 2.353553390593274),...
4 Spruce St 90 (POINT (0.4696699141100893 2.53033008588991), ...
5 Spruce St 80 (POINT (0.363603896932107 2.636396103067893), ...
Notice each geometry has two points, that you can access later if you want point by point information, or you can create a row for each point introducing a for loop that iterates over the points, something like:
for p in intersect:
columns_data.append( (n,el) )
geoms.append(p)
But in this case you depend on knowing that each intersection produces a multi-geometry.
About your other approach using the sjoin
function, I couldn't test it because the version of geopandas
I'm using does not provide the tools
module. Try to put buffer(0.0)
to see what happens.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With