Files
stAndrews/data-raw/update_listings.R
Rob Wiederstein bf5f736cf3
Some checks failed
Deploy stAndrews / deploy (push) Failing after 5s
Add owner name to listings map popup
Join owner_1 from owners.rds to listings via address match key.
Display in marker popup above price on the Listings tab map.
2026-04-16 06:29:23 -04:00

108 lines
4.1 KiB
R

# update_listings.R
# Weekly update: pull active listings from RentCast within 1 mile of
# St. Andrews center, clip to plat boundary, save data/listings.rds.
# Input: .Renviron (RENTCAST_API_KEY)
# data/plats/plats.shp
# Output: data/listings.rds
library(httr2)
library(jsonlite)
library(sf)
library(dplyr)
# ── API key ───────────────────────────────────────────────────────────────────
api_key <- Sys.getenv("RENTCAST_API_KEY")
if (identical(api_key, "")) stop("Missing RENTCAST_API_KEY in .Renviron")
# ── Pull listings ─────────────────────────────────────────────────────────────
resp <- request("https://api.rentcast.io/v1/listings/sale") |>
req_headers(
"Accept" = "application/json",
"X-Api-Key" = api_key
) |>
req_url_query(
latitude = 27.076199,
longitude = -82.362253,
radius = 1,
status = "Active",
limit = 500
) |>
req_retry(max_tries = 3) |>
req_perform()
if (resp_status(resp) >= 400)
stop("RentCast API error: HTTP ", resp_status(resp), "\n", resp_body_string(resp))
listings_raw <- jsonlite::fromJSON(resp_body_string(resp), flatten = TRUE)
cat("Fetched:", nrow(listings_raw), "listings\n")
# ── Clip to St. Andrews plat boundary ────────────────────────────────────────
plats <- sf::st_read("./data/plats/plats.shp", quiet = TRUE) |>
sf::st_union()
listings_sf <- sf::st_as_sf(
listings_raw,
coords = c("longitude", "latitude"),
crs = 4326,
remove = FALSE
)
in_plat <- lengths(sf::st_within(listings_sf, plats)) > 0
listings <- listings_raw[in_plat, ]
cat("After plat clip:", nrow(listings), "listings\n")
# ── Override RentCast coordinates with building footprint geometry ────────────
# RentCast geocoding is approximate. Our owners data uses building centroids
# from Sarasota County GIS footprints — far more accurate. Match on house
# number + street name and substitute when found.
owners_sf <- readRDS("./data/owners.rds")
# Extract house number from owners location field (e.g. "878 CHALMERS DR, Venice FL")
owners_coords <- owners_sf |>
mutate(
house_num = trimws(sub("^(\\d+).*", "\\1", location)),
street_raw = trimws(sub("^\\d+\\s+(.*),.*$", "\\1", location)),
match_key = paste(house_num, toupper(street_raw))
) |>
select(match_key, owner_1, geom) |>
distinct(match_key, .keep_all = TRUE)
# Extract house number + street from RentCast address
# e.g. "878 Chalmers Dr, Unit 878, Venice, FL 34293" -> "878 CHALMERS DR"
listings <- listings |>
mutate(
house_num = sub("^(\\d+)\\s.*", "\\1", addressLine1),
street_raw = gsub("[^A-Za-z ]", "", sub("^\\d+\\s+(\\S+\\s+\\S+).*", "\\1", addressLine1)),
match_key = paste(house_num, toupper(trimws(street_raw)))
)
matched <- merge(listings, owners_coords, by = "match_key", all.x = TRUE)
# For matched rows, replace RentCast lat/lng with footprint centroid coords
has_geom <- !is.na(matched$geom)
if (any(has_geom)) {
coords <- sf::st_coordinates(sf::st_as_sf(matched[has_geom, ], sf_column_name = "geom"))
matched$longitude[has_geom] <- coords[, "X"]
matched$latitude[has_geom] <- coords[, "Y"]
cat("Coordinates corrected from building footprints:", sum(has_geom), "listing(s)\n")
}
listings <- matched
# ── Select and clean columns ──────────────────────────────────────────────────
listings <- listings |>
transmute(
listed_date = as.Date(listedDate),
address = formattedAddress,
owner_1,
sqft = as.numeric(squareFootage),
price = as.numeric(price),
price_per_sqft = round(price / sqft, 0),
latitude,
longitude
) |>
arrange(desc(listed_date))
cat("Listings saved:", nrow(listings), "\n")
saveRDS(listings, "./data/listings.rds")