Search code examples
rspatialr-spr-sf

Surface Area Weighted Spatial Join of Polygons


Problem Statement

I have two sets of polygons and I want to join quantitative features from one set of polygons into another.

For example, consider the multipolygon of Yolo county, yolo. I want to aggregate tract-level data from all features in the field estimate that fit inside the the polygon of the city of Davis, davis.

The result should be a polygon of davis with a new field estimate that is the surface-area weighted estimate of all the features in yolo that fall within davis. How do I do this either in sp or sf?


Reproducible example

City of Davis polygon (davis) downloaded from this website, file: CityLimits.zip.

# packages
library(tidycensus)
library(tidyverse)
library(raster)

# get tract level data for yolo county
yolo  <- get_acs(state = "CA", county = "Yolo", geography = "tract", 
                 variables = "B19013_001", geometry = TRUE)


# city of davis shapefile
davis <- raster::shapefile("Davis_DBO_CityLimits.shp")
davis <- davis %>% spTransform(., st_crs(yolo)$`proj4string` %>% crs())
davis <- st_as_sf(davis)
yolo <- yolo %>% st_transform(st_crs(davis)$`proj4string`) 

# plot
ggplot() + 
  geom_sf(data = yolo, aes(fill = estimate)) +
  geom_sf(data = davis, alpha = 0.3, color = "red") +
  coord_sf(xlim=c(-121.6, -121.9), ylim = c(38.5, 38.6))

enter image description here


Note: I've seen this SO post. Dead links make it non-reproducible.


Solution

  • Here is a reproducible example.

    Example data:

    library(raster)
    p <- shapefile(system.file("external/lux.shp", package="raster"))
    p$value <- 1:length(p)
    b <- as(extent(6, 6.4, 49.76, 50), 'SpatialPolygons')
    b <- SpatialPolygonsDataFrame(b, data.frame(bid = 1))
    crs(b) <- crs(p)
    plot(p)
    plot(b, add=T, border='red', lwd=2)
    

    First 'by hand'

    i <- intersect(b, p)    
    i$AREA <- area(i) / 1000000
    aw <- sum(i$AREA * i$value) / sum(i$AREA)
    aw
    # 5.086891
    

    sp approach:

    a <- aggregate(p['value'], b, FUN=sum, areaWeighted=TRUE)
    a$value
    # 5.085438
    

    Now with sf

    library(sf)
    pf <- as(p, 'sf')
    bf <- as(b, 'sf')
    
    x <- sf::st_interpolate_aw(pf['value'], bf, extensive=F)
    x$value
    # 5.086891