- Weekly refresh: 388 owners, 10 sales, 11 listings (2026-04-16) - Move cron logs from ~/ to logs/ in each project dir - Add logs/ to .gitignore and .dockerignore - Update CLAUDE.md with log location and ops notes - Update TODO.md with log relocation completion
9.2 KiB
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 exportdata-raw/addresses/owners_raw.gpkg— geocoded points before QGIS editingdata-raw/addresses/owners_moved.gpkg— points after manual QGIS adjustment (source of truth forowners.rds)data-raw/geotagged_street_addresses.rds— cached geocoding results (Google API)
Rebuild pipeline:
data-raw/main.R— geocodes addresses, exports toowners_raw.gpkg- Manual QGIS step — adjust duplicate-location points, save as
owners_moved.gpkg data-raw/main.R(continued) — reads QGIS output, writesdata/owners.rdsdata-raw/create_sbdv_plats.R— buildsdata/plats/plats.shpdata-raw/create_venice_bndry.R— buildsdata/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):
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/Caddyfileon 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:
- Download the Sarasota County building footprint GIS layer.
- Spatially filter it to the St. Andrews subdivision boundary (already have
data/plats/plats.shp). - Compute centroids for each building footprint polygon.
- Join centroids to property records from the SCPA using address as the key.
- 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 indata-raw/geotagged_street_addresses.rdsto avoid re-calling the API. - Point deduplication (multiple units at same address) was done manually in QGIS — not scripted.
owners_moved.gpkgis the authoritative geocoded dataset. data-raw/is gitignored except for the shapefiles indata-raw/PlatBoundary/anddata-raw/SarasotaCountyBoundary/which are committed.
Ops
- Check latest refresh log:
tail -50 /data/projects/r/stAndrews/logs/refresh.log - Cron runs every Sunday at 11pm; logs go to
logs/refresh.log(not~/)