Repository navigation
Description
What. Replace the straight lines between nurseries with the shortest path by water that never crosses land, precomputed as GeoJSON by a script. Then use each route's length for distances and for the timing of the move animation.
Why. Straight lines cut across the island (for example Haapiti ↔ the east-coast nurseries), so both the map and the "km" figures on /recaptures ("Longest moves between nurseries") mislead. A pup has to swim around the island. A route by water is still a minimum distance, not the path actually swum.
1. Script: scripts/ocean-routes.mjs (e.g. pnpm routes)
- Coastline: OpenStreetMap coastline for Moorea (
natural=coastline;src/lib/moorea-outline.jsonis a simplified copy and may be too coarse near the shore). Credit OSM (ODbL) wherever routes are shown. - Graph: rasterise the sea around Moorea (≈ 20–50 m cells, out to a few km beyond the reef), or build a visibility graph on buffered coastline vertices. Edges may not touch land; buffer the coast by a few metres so routes don't graze the beach.
- Shortest path: Dijkstra or A* between every pair of nurseries (all ~12 sites, so ~66 pairs), then simplify (string-pulling or Douglas–Peucker) without reintroducing land crossings.
- Output: a
FeatureCollectionofLineStrings, one per unordered nursery pair, withproperties: { from, to, km, straightKm }, plus a test that no route intersects the coastline polygon. - Deterministic output: same input, same file. Document it in AGENTS.md (Repository map + Commands).
2. Where the GeoJSON lives (decision needed)
Nursery coordinates come from the GPS workbook in data/, which is never committed (golden rule 2). The site already shows the nursery points publicly, but committing a file of routes between them puts those coordinates in the public repo. Options:
- a) run the script during import (
DATA_DIR→cache/, in the fingerprint), so nothing derived from the sheets is committed; or - b) commit
external/nursery-routes.geojsonif the lab agrees the nursery coordinates can be public (they already are on the map).
Default to (a) unless the lab says (b) is fine. Bump IMPORTER_VERSION if it runs in the importer.
3. Use the routes
InteractiveMap/MooreaMapdraw a move with its route geometry (fallback: straight line if no route)./recaptures: "Longest moves" and the pair popups report km by water (straight-line km in brackets or in the CSV).- Animation timing (the Play on each shark's page, Charts: map shark moves between nurseries and each shark's capture sites #173): the dot moves along the route's geometry instead of in a straight line, and how long each step takes scales with route length (for example a fixed km per second, with a minimum and maximum so very short or very long hops stay readable). Under reduced motion it jumps to the end, as now.
- Optional: on the shark page caption, add "≥ N km by water in D days".
Acceptance
- No route crosses land or the coastline (tested).
- Every nursery pair has a route; distances are plausible against the straight-line km (never shorter).
- Moves animate along the water route, with timing proportional to distance.
- Copy says routes are the shortest path by water, not the path swum.
- CSP unchanged (no new hosts; OSM data is fetched only by the script, never in the browser).
Related: #173 (PRs #178 and #179), #182 (direction arrows should follow the route). Epic: #70
Activity
Luen commentedon Nov 10, 2026
We will also have to note in the caption that this route polyline and animation are an estimate and made up. We can't tell for sure which direction or path the sharks took.
Also, please see my other repo where I've already implemented this for paddling.
Also, just let the server calculate this and store these precalculated GeoJSON files on the server, not commit them to the repo.
feat(map): progressively animate move line along water route during p…
feat(sharks): continuous water route scrubbing and smooth swim animat…
fix(sharks): synchronize slider scrubbing with shark marker and water…
feat(map): replace shark SVG with clean circular traveller dot (#183)
style(map): style water route track as blue dotted line (#183)
feat(map): hide directional arrow during route playback on shark pages (
feat(sharks): default to All moves with arrows on page load (#183)