# stAndrews — Claude Context ## Required reading Before working on this project, read: - `/home/rkw/.claude/CLAUDE.md` — infrastructure context (network, Docker, deployment) - `/home/rkw/CLAUDE.md` — home directory layout ## What this project is A Shiny mobile app (using `shinyMobile` / Framework7) that maps property owners and community information for St. Andrews Park, Venice, Florida. 388 properties across 14 subdivisions in Plantation Golf and Country Club. **Entry point:** `app.R` (single-file Shiny app) ## Stack - **UI:** `shinyMobile` (Framework7-based, iOS theme, dark mode) - **Maps:** `leaflet` + `leafpop` - **Tables:** `DT` - **Spatial:** `sf` - **Data wrangling:** `dplyr` ## Data | File | Description | Source | |------|-------------|--------| | `data/owners.rds` | SF object — geocoded property owners (post-QGIS edit) | Built by `data-raw/main.R` | | `data/plats/plats.shp` | Subdivision boundary polygons | Built by `data-raw/create_sbdv_plats.R` | | `data/venice.rds` | Venice city boundary polygon | Built by `data-raw/create_venice_bndry.R` | | `data/venice_facts.rds` | Venice demographic facts table | Built by `data-raw/main.R` or similar | | `data/beaches.rds` | EPA beach monitoring locations | Built from `data-raw/epa/` | **Raw inputs** (gitignored — large or sensitive): - `data-raw/property/SCPA Public.xlsx` — Sarasota County Property Appraiser export - `data-raw/addresses/owners_raw.gpkg` — geocoded points before QGIS editing - `data-raw/addresses/owners_moved.gpkg` — points after manual QGIS adjustment (source of truth for `owners.rds`) - `data-raw/geotagged_street_addresses.rds` — cached geocoding results (Google API) **Rebuild pipeline:** 1. `data-raw/main.R` — geocodes addresses, exports to `owners_raw.gpkg` 2. Manual QGIS step — adjust duplicate-location points, save as `owners_moved.gpkg` 3. `data-raw/main.R` (continued) — reads QGIS output, writes `data/owners.rds` 4. `data-raw/create_sbdv_plats.R` — builds `data/plats/plats.shp` 5. `data-raw/create_venice_bndry.R` — builds `data/venice.rds` ## App tabs | Tab | Content | |-----|---------| | About | Community description + photo | | Venice | City boundary map + facts table | | Beach | EPA beach locations map + helpful links | | Owners | Searchable owner map + table (filter by name, address, subdivision) | | Resources | Links to city services + PDF documents | PDF documents are served from `www/docs/`. ## Running locally Run inside the `rstudio` Docker container (see global CLAUDE.md): ```r shiny::runApp("/home/rstudio/projects/r/stAndrews") ``` Or from RStudio with the project open: click **Run App**. ## Deployment Deployed as a Shiny app. Path routing via the analytics gateway: - Add a route to `~/docker/gateway/Caddyfile` on the analytics VM if not already present. ## Old app Previously deployed to shinyapps.io at `https://rob-wiederstein.shinyapps.io/stAndrews/` (account: `rob-wiederstein`, appId: 14173710). No longer maintained — app should be archived/deleted from the shinyapps.io dashboard. The local `rsconnect/` directory has been removed. ### Problems **1. Geocoding was unreliable and required manual correction** St. Andrews has three property types: standalone homes, villas (2- and 4-unit structures where each side is a separate unit), and 8-unit buildings. The 8-unit buildings have unit numbers (1–8) in the raw property records. Those unit numbers were stripped before geocoding, so all 8 units in a building returned a single shared coordinate. Google's geocoding was also only approximate — coordinates landed near addresses but not on top of the actual structures. The result was that every point had to be manually reviewed and dragged to its correct location in QGIS. This was labor-intensive and not repeatable. **2. No update path** The data pipeline was designed as a one-shot process with no way to refresh. Property ownership changes hands regularly — the data should be updated roughly weekly. The manual QGIS correction step makes this especially painful: any new or changed record would require re-running geocoding and repeating the manual editing. There is no mechanism to diff new records against existing ones or to carry forward previously corrected coordinates. ### Geographic data flow There are three independent geographic data flows, all ending in WGS84 (EPSG:4326): **1. Owner points** (`data-raw/main.R`) Raw property records from the Sarasota County Property Appraiser (Excel) are filtered to the 13 St. Andrews subdivisions. Street addresses are parsed and assembled into geocodable strings, then sent to the Google geocoding API via `tidygeocoder`. Results are cached as `geotagged_street_addresses.rds` so the API isn't called twice. The geocoded points are written to `owners_raw.gpkg`. Because many units share a building address, they all land on the same coordinate — so the file is loaded into QGIS and points are manually dragged to their actual locations. The adjusted file (`owners_moved.gpkg`) is read back into R and saved as `data/owners.rds`. **2. Subdivision boundary polygons** (`data-raw/create_sbdv_plats.R`) A Sarasota County GIS shapefile (`data-raw/PlatBoundary/`) containing all county plat boundaries is read in, reprojected to WGS84, and filtered to the same 13 subdivision IDs. Subdivision names are cleaned up and the result is written to `data/plats/plats.shp`. **3. Venice city boundary** (`data-raw/create_venice_bndry.R`) A Sarasota County boundary shapefile (`data-raw/SarasotaCountyBoundary/`) is filtered to the City of Venice (`municipali == "CV"`), keeping only the main polygon (acreage > 2500 to drop small outliers). It is simplified with `rmapshaper`, interior holes are removed, reprojected to WGS84, and saved as `data/venice.rds`. ## New app — decisions **Same folder/repo.** The UI in `app.R` is already good — tabs, maps, and layout don't need to change. What needs replacing is entirely in `data-raw/`: the pipeline that produces `data/owners.rds`. The app just consumes that file. Rebuild the pipeline, keep the app. **Feasibility hinges on the join.** The geometry side is straightforward — filter building footprints to the subdivision boundary, compute centroids, done. The hard part is linking those geometries to SCPA property records. Two approaches worth investigating: - **Address join** — if the footprint layer carries situs addresses in the same format as the SCPA records, a direct join works. If formatting differs, fuzzy matching may be needed. - **Account number join** — the SCPA assigns an account number to each property. It's worth checking whether account numbers follow the *property* (stable, ideal join key) or the *owner* (changes on sale, less useful). If account numbers are property-stable and also appear in the footprint layer, this would be the cleanest and most reliable join key — especially for multi-unit buildings where address disambiguation is messy. Also needs confirming: whether the footprint polygons exist at the **unit level** or only at the **building level**. If only building-level, the stacking problem from the old pipeline reappears. Data inspection will resolve all of this. ## New app — proposal Instead of geocoding addresses, use the Sarasota County GIS building footprint layer, which contains polygon outlines of every structure. Key advantages: - **No geocoding API needed.** Building outlines are already precisely positioned over actual structures. Deriving centroids from the polygons gives accurate, stable coordinates for every unit — far better than approximate Google geocoding results. - **Handles multi-unit buildings cleanly.** Each unit in an 8-unit building has its own footprint polygon, so each gets its own distinct centroid. No stripping of unit numbers, no stacking of points, no manual QGIS correction. - **No new construction to worry about.** St. Andrews is a built-out community with no active development, so the building footprint layer is effectively static. **Proposed pipeline:** 1. Download the Sarasota County building footprint GIS layer. 2. Spatially filter it to the St. Andrews subdivision boundary (already have `data/plats/plats.shp`). 3. Compute centroids for each building footprint polygon. 4. Join centroids to property records from the SCPA using address as the key. 5. Save the joined dataset as the owner points layer. **Update path:** Because the geometry is fixed (building centroids don't change), only the ownership attributes need refreshing. The SCPA property records can be re-downloaded weekly and re-joined to the stable centroid table. This makes weekly updates a simple scripted operation with no manual steps. ## Notes - Geocoding used Google API via `tidygeocoder`; results cached in `data-raw/geotagged_street_addresses.rds` to avoid re-calling the API. - Point deduplication (multiple units at same address) was done manually in QGIS — not scripted. `owners_moved.gpkg` is the authoritative geocoded dataset. - `data-raw/` is gitignored except for the shapefiles in `data-raw/PlatBoundary/` and `data-raw/SarasotaCountyBoundary/` which are committed.