cloudcroft disc golf: how this works
Table of Contents
This page documents the pattern so anyone can map their own course the same way. The code stays private; the recipe is public.
the dataset
Four open sources, each verified covering Cloudcroft before use:
- USGS 3DEP 1-meter DEM (project NM_SouthEast_2018_D19). One 248MB tile covers both courses. Sampled anchor elevations match the National Map point service to 0.1 ft. This is the elevation source of record — 30m DEMs give roughly one pixel per 100-foot hole, useless for per-hole profiles.
- USGS NAIP 2022 60cm imagery. One tile covers both courses, located via the state’s quarter-quad shapefile index. Cropped to a course-window JPEG for the satellite overlay toggle.
- OpenStreetMap (ODbL). Live bbox extract: 46 paths + 26 footways with real trail names. The S3-native Daylight parquet path was attempted and rejected on measurement — no spatial partitioning, planet-wide scan projected past 25 minutes for a 5 km box. Same underlying data, bbox-native API, 20 seconds.
- Ground GPS (pending). Every tee pad and basket pinned on foot with a phone GPS + compass bearings. Until those land, the map shows course anchors and published distances — no fabricated pins.
the pipeline
Registry buckets → our S3 prefix → derived JSON, all scripted and rerunnable:
dem_profiles.pysamples the 1m tile straight from S3 (/vsis3/, no local copy) for anchor validation and, once pins land, per-hole elevation profiles.dem_crop.pycuts the course-area window plus a 200x200 mesh grid that feeds the 3D walkthrough terrain.naip_crop.pycuts the course-window JPEG from the NAIP tile (copied to our prefix first — NAIP is requester-pays, pennies for two tiles).osm_to_geojson.pyturns the Overpass extract into trail/park GeoJSON.
Terrain Tiles (Terrarium PNGs) were evaluated and set aside: the Cloudcroft tile verified live, but both frontends render elevation from the local 1m 3DEP DEM grid instead — nothing reads Terrarium at render time.
the AWS stack
One CDK stack, account 211125425201, us-east-1. TypeScript, stable L1 constructs for Location Service (the L2 package is still alpha):
- Amazon Location Service: two maps (Esri topo + imagery), an Esri place index, a per-hole geofence collection, a player tracker, a route calculator. Maps and geocoding carry the 2D experience; geofences trigger hole arrival on-site; the tracker ingests player positions.
- Amazon Cognito identity pool with guest access. One scoped role serves
both the web map and the Android app: map/place reads plus tracker writes
and geofence evaluation. (A referer-locked API key was the plan for web,
but
geo:CreateKeyis denied account-side even for administrators — the guest pool is the fallback until that clears.) - AWS Lambda (Rust, ARM64, musl-static) behind an HTTP API: a
plays-like distance function (haversine + elevation model) and a scorecard
API over DynamoDB (on-demand, point-in-time recovery). Static musl
because the container gnu toolchain links glibc 2.38+, which
provided.al2023(2.34) rejects at init. - Amazon S3 + CloudFront: the site bucket hosts the dataset and the web bundle under a registered sibling prefix the site deploy can never wipe.
the frontend
Vite + MapLibre GL + the official Location auth helper, authenticating as a Cognito guest. Bundled data copies keep every fetch same-origin. Verified in headless Chromium against the live URL: tiles render, nine hole rows, zero failed requests, full scorecard click-path green.
reuse it for your course
- Pin your tees and baskets (any GPS app, 30 seconds per pin) and note par + distance per hole.
- Query the National Map API for your bbox — if a 1m project covers you, one tile probably covers the whole course.
- Find your NAIP tile through your state’s QQ index; crop the window.
- Pull the Overpass bbox extract for trails and parking.
- Stand up the CDK stack, swap the course JSON, deploy the web bundle to any static host. The AR overlays key off the same GeoJSON.