Reimagining_SF_Shiny_App / Rscripts /prep /making-rsfprogram-raster.R
avephill
Deploy origin/main to HF Space (PNGs via LFS)
a4b8c45
Raw
History Blame Contribute Delete
5.1 kB
# ============================================================================
# Prep: Nearest RSF Program rasters (30 m, same grid as greenspace)
# ============================================================================
# Produces:
# - nearest_rsfprogram_dist.tif — distance (m) to nearest RSF program polygon
# - nearest_rsfprogram_id.tif — polygon_id for lookup in RSF gpkg (prj_name)
#
# Source polygons: data/source/RSF_Program_Projects_polygons.gpkg
# Template grid: same slope.tif + SF county mask as making-greenspace-raster.R
# ============================================================================
library(tidyverse)
library(terra)
library(tidyterra)
library(sf)
library(duckdb)
library(glue)
library(tictoc)
library(tidycensus)
# ============================================================================
# DuckDB + spatial
# ============================================================================
tcon <- dbConnect(duckdb::duckdb())
tcon |> dbExecute("
INSTALL spatial;
LOAD spatial;
SET memory_limit = '200GB';
SET preserve_insertion_order = false;
SET threads TO 8;
")
rsf_gpkg <- "data/source/RSF_Program_Projects_polygons.gpkg"
# ============================================================================
# SF county boundary + template grid (match greenspace raster script)
# ============================================================================
sf <- get_acs(
geography = "county",
state = "CA",
variables = "B01003_001",
year = 2016,
geometry = TRUE
) |>
filter(NAME == "San Francisco County, California") |>
st_transform(3310) |>
st_cast("POLYGON") |>
mutate(area = st_area(geometry)) |>
slice_max(area, n = 1) |>
select(GEOID, NAME, geometry)
empty.sr <- rast("data/source/slope.tif")
template.sr <- empty.sr %>%
mutate(cell_id = 1:ncell(.)) %>%
filter(!is.na(prcnt_slope30)) %>%
crop(sf %>% st_transform(4326), mask = TRUE) %>%
crop(ext(c(-123, -122, 37.65, 37.85))) %>%
trim()
template_pts <- template.sr %>%
as.points(na.rm = TRUE) %>%
st_as_sf() %>%
st_transform(4326)
dir.create("data/intermediate", showWarnings = FALSE)
dir.create("data/output", showWarnings = FALSE)
template_pts |>
write_sf("data/intermediate/template_pts.gpkg")
# Escape single quotes in path for SQL (Windows paths unlikely here)
rsf_gpkg_sql <- gsub("'", "''", normalizePath(rsf_gpkg, winslash = "/", mustWork = TRUE))
tcon |> dbExecute(glue("
CREATE OR REPLACE TABLE rsf_geo AS
SELECT
polygon_id,
prj_name,
ST_TRANSFORM(geom, 'EPSG:4326', 'EPSG:3310', always_xy := true) AS geom3310,
ST_SimplifyPreserveTopology(
ST_TRANSFORM(geom, 'EPSG:4326', 'EPSG:3310', always_xy := true),
10
) AS simple_geom3310
FROM ST_Read('{rsf_gpkg_sql}');
CREATE INDEX idx_rsf_simple_geom ON rsf_geo USING RTREE (simple_geom3310);
"))
tcon |> dbExecute("
CREATE OR REPLACE TABLE template_grid_geo AS
SELECT
cell_id,
geom,
ST_TRANSFORM(geom, 'EPSG:4326', 'EPSG:3310', always_xy := true) AS geom3310
FROM ST_Read('data/intermediate/template_pts.gpkg');
CREATE INDEX idx_template_geom ON template_grid_geo USING RTREE (geom3310);
CREATE INDEX idx_template_cell_id ON template_grid_geo (cell_id);
")
nn_query <- glue("
CREATE OR REPLACE TABLE rsf_distance_complete AS
WITH distances AS (
SELECT
template.cell_id,
template.geom AS template_geom,
ST_AsText(template.geom) AS geom_wkt,
rsf.polygon_id,
ST_Distance(template.geom3310, rsf.simple_geom3310) AS distance_meters
FROM template_grid_geo AS template, rsf_geo AS rsf
)
SELECT
cell_id,
template_geom,
geom_wkt,
MIN(distance_meters) AS distance_to_rsf_meters,
arg_min(polygon_id, distance_meters) AS nearest_rsf_polygon_id
FROM distances
GROUP BY cell_id, template_geom, geom_wkt;
")
cat("Calculating nearest RSF program distances...\n")
tic()
tcon |> dbExecute(nn_query)
toc()
rsf_dist_complete <- tcon |>
tbl("rsf_distance_complete") |>
select(cell_id, distance_to_rsf_meters, nearest_rsf_polygon_id) |>
collect()
empty_raster <- empty.sr
values(empty_raster) <- NA
aligned_results <- tibble(cells = 1:ncell(empty.sr)) |>
left_join(rsf_dist_complete |> rename(cells = cell_id), by = "cells")
rsf_nearest_dist <- empty_raster
values(rsf_nearest_dist) <- aligned_results$distance_to_rsf_meters
names(rsf_nearest_dist) <- "rsfprogram_nearest_dist"
cropped_rsf_dist <- rsf_nearest_dist %>%
crop(sf %>% st_transform(4326), mask = TRUE) %>%
crop(ext(c(-123, -122, 37.65, 37.85))) %>%
trim()
rsf_nearest_id <- empty_raster
values(rsf_nearest_id) <- as.integer(round(as.numeric(aligned_results$nearest_rsf_polygon_id)))
names(rsf_nearest_id) <- "rsfprogram_nearest_id"
cropped_rsf_id <- rsf_nearest_id %>%
crop(sf %>% st_transform(4326), mask = TRUE) %>%
crop(ext(c(-123, -122, 37.65, 37.85))) %>%
trim()
plot(cropped_rsf_dist)
plot(cropped_rsf_id)
writeRaster(cropped_rsf_dist, "data/output/nearest_rsfprogram_dist.tif", overwrite = TRUE)
writeRaster(cropped_rsf_id, "data/output/nearest_rsfprogram_id.tif", overwrite = TRUE, datatype = "INT4S")
dbDisconnect(tcon, shutdown = TRUE)