I'm having a GeoDataFrame of lines and a GeoDataFrame of polygons. The polygons have an attribute with the altitude of that polygon.

For each line, I want to find in what polygon it is located. As output I would like to have the GeoDataFrame of lines, with the altitude attribute of the accessory polygon added to each row (each row is a line).

Below code gives the desired output for most lines.

output = gpd.sjoin(lines, polygons, how='left', op='within')

I say this works for most lines, as it does not work for lines which are located in multiple polygons, shown blue in attached image. For these lines, it gives a NaN value for the attributes. However, I would like it to find the attributes of both polygons, and eventually I keep the lowest altitude of the matched polygons.

Hopefully I made myself clear. Could anyone explain how I can do this?

Below I explain what I already tried, but this is a bit messy and maybe not the right direction:

As solution, I tried to iterate over each row of the polygon GeoDataFrame (from lowest altitude to highest), and then do the spatial join, only with lines which are not yet matched. But this didn't work for me, as the spatial join didn't work on single rows of the GeoDataFrame (crs issue).

polygons= polygons.sort_values(by=['altitude'])
output = None

for index, row in polygons.iterrows():
    if output is not None:
        toappend = gpd.sjoin(newlines, row, how='left', op='within')
        newlines = toappend[toappend['altitude'].isna()]
        output = output.append(toappend.dropna())
        output = gpd.sjoin(lines, row, how='left', op='within')
        newlines = output[output['altitude'].isna()]

AttributeError: 'Series' object has no attribute 'crs'

The blue lines are located in multiple polygons, which gives a NaN result for the spatial join, op='within'


Try sorting by altitude (in my example called _mean) and drop duplicates on line id:

import geopandas as gpd

polyshape = '/home/bera/GIS/Data/testdata/polygons.shp'
lineshape = '/home/bera/GIS/Data/testdata/lines.shp'

polydf = gpd.read_file(polyshape)
linedf = gpd.read_file(lineshape)

newdf = gpd.sjoin(polydf, linedf)
newdf2 = newdf.sort_values('_mean', ascending=True).drop_duplicates('id')


       _mean  id
    0   34.0  22
    0   34.0  44
    1   41.0  55

enter image description here

| improve this answer | |
  • 1
    Thank you very much, this worked for me! I didn't realize you could use sjoin without the "op=..." input. Without that input, sjoin apparently works similar to a normal join, but spatial, as you would expect from the name! Thanks again. :) – Jordy W May 6 at 7:17
  • Nice! Please accept by answer with the checkbox – BERA May 6 at 7:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.