I am wondering whether it is possible to identify all neighbors to each polygon using only python (with, e.g., geopandas) in the same way that can be done with python in QGIS (Find neighbors polygon).


This code finds and adds neighbors as new field value joined by comma.

import geopandas as gp

file= "C:/path/to/shapefile.shp"    

df = gp.read_file(file) # open file
df["NEIGHBORS"] = None  # add NEIGHBORS column

for index, country in df.iterrows():   
    # get 'not disjoint' countries
    neighbors = df[~df.geometry.disjoint(country.geometry)].NAME.tolist()
    # remove own name from the list
    neighbors = [ name for name in neighbors if country.NAME != name ]
    # add names of neighbors as NEIGHBORS value
    df.at[index, "NEIGHBORS"] = ", ".join(neighbors)

# save GeoDataFrame as a new file

enter image description here

| improve this answer | |

This is an addendum to Kadir's answer (which works great).

For one, instead of using not disjoint you can just use touches directly, which does the same thing but is easier to read.

If, as the OP asked, you want to search internally to find all neighboring geometries in a single geoDataframe:

for index, row in df.iterrows():  
    neighbors = df[df.geometry.touches(row['geometry'])].name.tolist() 
    neighbors = neighbors.remove(row.name)
    df.at[index, "my_neighbors"] = ", ".join(neighbors)

For a related capability you may want to determine whether each row of a dataframe borders some particular other shape, potentially from a different dataframe or some input value (converted into a geoDataframe).

someSpecialGeometry = GEOdataframeRow['geometry'].values[0] 
df['isNeighbor'] = df.apply(lambda row: row['geometry'].touches(someSpecialGeometry), axis=1)

This creates a boolean-valued column for whether each row borders the shape of interest.

| improve this answer | |

This is a further addendum relating to Aaron's comment.

If you find 'touches' is failing to find all neighboring polygons like it should this is likely down to the resolution of your polygon borders which may in fact intersect hence 'touches' ignoring them.

I solved this issue using 'overlaps' additionally to 'touches'.

neighbors = np.array(df[df.geometry.touches(row['geometry'])].name)
#overlapping neighbors use if discrepances found with touches
overlap = np.array(df[df.geometry.overlaps(row['geometry'])].name)

neighbors = np.union1d(neighbors, overlap)
| improve this answer | |

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.