Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Intersection of Two LineStrings Geopandas

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:

enter image description here

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.

like image 946
AJG519 Avatar asked Nov 20 '15 00:11

AJG519


People also ask

What is intersection in Python geopandas?

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

What is intersection in GIS?

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.

Is geopandas silently skipping certain geometry while plotting?

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

What are the different types of manipulations available in geopandas?

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.


1 Answers

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.

like image 197
eguaio Avatar answered Oct 04 '22 13:10

eguaio