Static PWA: an OpenStreetMap with the Pedal Hidrográfico hydrography overlay, every passeio of the collective drawn on top of it, filterable by date, with the linked Instagram post embedded on click. Includes an in-browser GPX drawing tool and a photo overlay sourced from RDF/Turtle. Architecture and conventions in CLAUDE.md; ontology notes in docs/DESIGN.md; self-hosted backend in backend/README.md.
https://telhas.pedalhidrografi.co/rmsampa-v2/{z}/{x}/{y}.png), togglable
via the layer control.web/data/tours.ttl in white with a dark
casing, labelled by Data — Nome.PH 79 / BP 4)..gpx file. Esc cancels.web/data/uploads.ttl
(both kinds live in the same RDF catalog). Click a marker → centered
modal preview (popup near the marker when it fits; falls back to a
bottom-sheet modal otherwise). Photo markers have a white border;
video markers have a red-orange border and a small play glyph; both
share the same density-based clustering layout./live-location + /live-locations endpoints
(in-memory, never persisted). A Capacitor native shell (capacitor/)
wraps this same web app so the broadcast keeps running with the screen
off — see capacitor/README.md.It’s a fully static site with an optional self-hosted backend for uploads. Two stages:
web/data/tours.ttl (the Tour catalog) is maintained through the
app itself: web/upload_tour.html posts to the backend’s Tour CRUD
endpoints. (The old build-tours.py CSV pipeline was removed — the
catalog now carries hand-written narratives and announcement images
that a spreadsheet rebuild would wipe.)python scripts/build-routes.py reads web/data/tours.ttl, fetches
every referenced GPX from RideWithGPS, downsamples each track to
≤400 points, and writes everything into web/routes.json. Unique
RWGPS IDs are fetched once even when multiple tours share a route
(e.g. anniversary re-rides). This is the only place RWGPS credentials
are needed — the browser never sees them.python scripts/build-clips.py (optional) re-encodes any source
videos in web/clips/raw/ to 360p + 720p mp4 + .m4a audio +
thumbnail jpg, and upserts each as a ph:Video in
web/data/uploads.ttl (with ph:capturedDuring set to the closest
tour within ±12 h). Requires ffmpeg and exiftool in PATH.web/index.html loads routes.json and
resolves data graphs via web/data/data_graphs.ttl. No backend
required at runtime; serve web/ from any static host. For uploads
and photo storage, run the backend — see
backend/README.md.pip install python-dotenv rdflib pyshacl # build + backend deps
cp .env.example .env
# fill in RWGPS_API_KEY and RWGPS_AUTH_TOKEN — see "Credentials" below
python scripts/build-routes.py # writes web/routes.json
Then serve web/ with anything:
cd web && python -m http.server 8000
# open http://localhost:8000/
Python 3.10+ recommended (uses str | None annotations and urllib).
Re-run build-routes.py to refresh routes.json after tour edits
(the backend also upserts routes incrementally on every /upload-tour).
After the build steps above, web/ is fully self-contained. Push it to
GitHub Pages, Netlify, S3, etc. and you’re done — no server needed.
Photos won’t show on a static mirror unless you also publish web/data/
and web/photos/ from a backend run.
.env is gitignored. Build steps are the only thing that touches
credentials.
RWGPS_API_KEY — request one at https://ridewithgps.com/api.RWGPS_AUTH_TOKEN — auth token for the user account that owns the routes.
You can get it by POSTing email+password to
https://ridewithgps.com/users/current.json (response includes
user.auth_token), or from your account settings page.RWGPS_COLLECTION_PRIVACY_CODE — set if some routes are unlisted and need
the collection privacy code.web/routes.json is gitignored so credentials-derived data isn’t
committed by accident. To deploy via GitHub Pages without a build server,
remove it from .gitignore and commit the JSON.amora is free software under the GNU Affero General Public License v3.0
(AGPL-3.0) — see LICENSE. It was GPL-2.0 until September 2026;
the switch lets it bundle GPL-3.0 code (the leaflet-rotate map-rotation plugin)
and makes the network clause explicit: if you run a modified amora for other
people over a network, you must offer them its source.
Bundled third-party code keeps its own license: Leaflet and flatgeobuf (BSD-2-Clause), leaflet-rotate (GPL-3.0), N3.js, exifr, qrcode.js and leaflet.locatecontrol and geotiff.js (MIT), Tom Select (Apache-2.0), mediabunny (MPL-2.0).