3

I am spatially joining census data to building data using GeoPandas spatial join gpd.sjoin(left_df=gdf_building,right_df=gdf_census_data, how="left", op="intersects"). In the results, I observed that if a building is intersecting two census blocks, GeoPandas generates two copies of the census properties in the building shape. How can I remove one of the duplicates and only keep the data corresponding to the census block with the largest area of intersection?

Red polygons shows buildings and the yellow ones shows census blocks:

Red polygons shows buildings and the yellow ones shows census blocks

This builidng intersects two polygons (census blocks)

enter image description here

Two separate links in the building attribute

enter image description here

5

That's correct - the spatial join is going to give any row from the census blocks that the buildings touch. Instead if you want to guarantee that the building is attached to only a single block then there's a couple of steps.

Let's assume we have some census blocks and some buildings:

Fake census blocks

fake_census = gpd.GeoDataFrame([
    {'block': 'Block A', 'geometry': geometry.box(1, 0, 4, 4)},
    {'block': 'Block B', 'geometry': geometry.box(4, 0, 7, 4)}
])

fake_buildings = gpd.GeoDataFrame([
    {'building': 'Building 1', 'geometry': geometry.box(1, 0, 2, 1)},
    {'building': 'Building 2', 'geometry': geometry.box(3, 1, 6, 3)},
    {'building': 'Building 3', 'geometry': geometry.box(0, 3, 2, 5)},
])
  1. Instead of a spatial join use the gpd.overlay function to calculate the spatial intersection between the two layers (and copying across the attributes:
overlay = gpd.overlay(fake_census, fake_buildings)

Spatial intersection of the two layers

  1. Calculate the area of the area and sort descending by the area:
overlay['area'] = overlay.area
overlay.sort_values('area', ascending=False, inplace=True)
| block   | building   | geometry                            |   area |
|:--------|:-----------|:------------------------------------|-------:|
| Block B | Building 2 | POLYGON ((4 1, 4 3, 6 3, 6 1, 4 1)) |      4 |
| Block A | Building 2 | POLYGON ((4 3, 4 1, 3 1, 3 3, 4 3)) |      2 |
| Block A | Building 1 | POLYGON ((2 0, 1 0, 1 1, 2 1, 2 0)) |      1 |
| Block A | Building 3 | POLYGON ((1 3, 1 4, 2 4, 2 3, 1 3)) |      1 |
  1. Group by the building identifier and take the first row - this will give you the building tied to the census block with the largest area.
overlay.groupby('building').first()
| building   | block   | geometry                            |   area |
|:-----------|:--------|:------------------------------------|-------:|
| Building 1 | Block A | POLYGON ((2 0, 1 0, 1 1, 2 1, 2 0)) |      1 |
| Building 2 | Block B | POLYGON ((4 1, 4 3, 6 3, 6 1, 4 1)) |      4 |
| Building 3 | Block A | POLYGON ((1 3, 1 4, 2 4, 2 3, 1 3)) |      1 |
1
  • 1
    I like this answer =) – Taras Oct 6 '20 at 9:51

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.