Commit Graph

6 Commits

Author SHA1 Message Date
6457501520 Unified leg system: every movement is a first-class leg + fix 4 in-the-sea coordinates
Legs:
- day.firstLeg (hotel -> first stop) is now a first-class leg object exactly
  like retLeg/transferIn: own mode/dur/geometry, same leg-row rendering, same
  click-to-edit L3 card (drag waypoint + walk/taxi toggle), same router +
  sanity guards, same strip/budget/context accounting
- hotel rendered as a timeline NODE on every day: 'arrive HH:MM - drop bags'
  on the arrival day (after the airport->hotel leg row), 'depart HH:MM'
  (startMin minus the out-leg) on ordinary days; the arrival drop-off is a
  stand-alone leg row instead of being embedded in the flight card
- arrival day: the first stop starts at hotel-arrival + out-leg; ordinary
  days keep startMin = time AT the first stop (the out-leg happens before)
- out-leg suppressed when the day doesn't start at the base hotel: travel
  days (city change, first stop > 25km away) and breakfast-at-hotel days
  (< 120m); stale firstLeg objects self-heal
- set_start tool shifts arrLandingMin on arrival days so the whole chain
  (hotel arrival, first stop, ...) follows the new landing time

Coordinates (verified on land via Nominatim + tile coastline scan):
- S14 Castillo San Diego -> Bodeguita promontory [10.4205,-75.5517]
- S16 Malecon de Bocagrande -> seawall [10.43545,-75.539]
- S18 Punta Canoa -> sandbar beach [10.4403,-75.530]
- S19 beachside lunch -> same sandbar [10.4402,-75.5294]
healCoordinates syncs saved plans from data.js on load
2026-09-11 00:46:08 -04:00
1ba39f7333 Fix arrival/return transfer legs + plan restore + router guards
Transfers (airport<->hotel) were the app's weakest surface; this fixes the
long-standing cluster of bugs in one pass:

- saneRoute(): reject OSRM results implausible for the straight-line distance
  (extract coverage holes once produced a 9h 'route' for a 5km taxi hop);
  applied at every routePts call site
- healTransfers() on load: re-sync transfer endpoints against current hotel +
  AIRPORTS table, re-sync stays[] against the day-base hotel objects (the
  saved stays[] carried pre-coord-fix locations, and dayBase() resolves the
  hotel FROM stays — every return leg was routed to the stale hotel), drop
  implausible or endpoint-drifted stored routes so they re-route cleanly,
  default a missing mode to car, and recover arrLandingMin from the booked
  flight time
- the last-stop->hotel ride is now a first-class editable leg (retLeg): the
  rail row opens the route card with drag + walk/taxi toggle, exactly like
  stop-to-stop legs; previously auto-routed returns seed it
- startMin now actually tracks landing + drop-off (syncArrivalStart had a
  null arrLandingMin guard that silently froze the day's start)
- applySavedPlan re-points day: days becomes a new object tree on restore,
  and the current-day reference stayed on the stale base-plan day — the rail
  rendered the base plan instead of the saved one until a day-tab click
- setDay enriches the opened day (idempotent), so a day opened from a saved
  plan gets its legs (incl. the return leg) routed instead of sitting on
  straight-line estimates

Also in this change set: correct LLM model name, keyword-router removal (chat
goes to the LLM), screenshot_map + relocate_stop tools for the UI LLM,
server-side plan store with localStorage migration, thinking toggle +
keep-alive warm-up, three-state LLM guard with proactive model load,
spatial /near /nearest /corridor SQL fixes, and the full data.js
coordinate correction for the Colombia demo.
2026-09-10 22:46:07 -04:00
04076294bd Fix Cartagena old-city points placed in the bay + misplaced CTG airport
Root cause: several Cartagena old-city landmarks in the data had latitudes
~4 km too high (≈10.46°N instead of ≈10.42°N), dropping them out into the
Caribbean bay — that's why Day 2 stops 2–5 rendered in the water. The chat
model couldn't fix them because its web search couldn't surface valid
coordinates for those specific buildings, and it (correctly) refused to move
them to arbitrary guessed locations.

Also found the CTG (Rafael Núñez) airport was misplaced ~8 km east onto the
Bocagrande area ([10.4622,-75.4385]); the real airport is [10.4425,-75.5131].
This inflated the Day 1 arrival drop-off to 313 min.

Changes:
- data.js: re-anchor 6 old-city stops (S5, S7–S10, S20) + the C2 candidate to
  verified on-land coordinates (all snap <21 m to the OSRM extract); fix the
  CTG airport in the CTG→SMR flight stations and the S22 stop.
- app.js: correct AIRPORTS.ctg (used by the arrival transfer) to the real
  airport. Add healCoordinates() so a plan saved before this fix (still holding
  the old in-the-bay points in the browser's localStorage) is re-anchored to
  the corrected source on load — stops re-sync by id, arrival/departure airports
  re-derived from the AIRPORTS table. Idempotent; user-swapped stops (candidate
  ids) are left alone. Verified with a CDP test that corrupts the saved plan
  (6 stops + airport) and confirms a reload self-heals them back.

All regression suites pass: coords 6/6, drop-off leg 17/17, smoke 0 errors,
rail structure 13/13, dates 7/7.
2026-09-09 21:02:19 -04:00
33ed5d9fe6 multi-trip mock + Colombia trip with a real walking router
The mock can now work several trips at once: a trip picker in the
topbar, per-trip state in localStorage (edits + version history survive
switching), and a per-trip router key so each trip routes on its own
OSRM extract.

Colombia (Cartagena -> Santa Marta, 8 days) becomes the default working
trip, inside a new walking-profile OSRM instance:
- scripts/crop_pbf.py: streaming 3-pass PBF cropper (no PBF library;
  the country file is too big for osmium extract -s complete_ways on
  this box). Framing validated against osmium test fixtures and
  osrm-extract. Drops relations (foot profile ignores restrictions).
- scripts/setup-osrm-colombia.sh: download -> crop (~4.6 MB) ->
  extract/partition/customize with the official foot.lua -> serve
  :5003. Verified: Getsemaní -> Castillo San Felipe 7.98 km / 96 min.
- server.js: ROUTERS map, ?router=colombia|northeast on /route,
  /table, /router-status (per-router probe points).
- app.js: legs touching a transit stop are fixed (flight/taxi/bus via
  M.multimodal), never re-routed; distance-0 router answers (both
  points on one node) read as adjacent stops, not out-of-coverage.
- multi-hotel fix: day-scope map fit uses the stays covering THAT day,
  so a two-city trip no longer fits the whole country per day.

Also: pre-cache-tiles.sh (Cartagena+Santa Marta z14-16, 2560 tiles),
tile cache write fix (Node rejects flags:'x'; buffer-then-write),
favicon, renderHotelMks load-order guard, tests updated for the new
default trip (all four puppeteer suites green, zero console errors).
2026-09-09 09:44:29 -04:00
30f3dd9cbf mock: wire to the real local OSRM router + L0 trip view, version history, offline cues
- server.js: proxy /route, /table, /router-status to the local OSRM on :5000
- app.js: routeClient (polyline-5 decode, ordered route fetch); legs start as
  straight-line estimates (conf 1) and are upgraded in the background to real
  road geometry + duration (conf 3); out-of-coverage trips (Florence) fall
  back gracefully with a 'estimate — outside router coverage' source
- L3 leg editor: dragging a waypoint now genuinely re-routes through the
  router (was silently no-op — #app covered the map with pointer-events:auto,
  so no map drag ever landed; #app is now pointer-events:none, children opt in)
- data.js: second demo city (Boston 14–16 Sep, inside the router's NE-US
  extract) via ?city=boston; nudges + multimodal legs are now dataset-driven
- L0: Day/Trip scope toggle — per-day summary cards (pacing bar, stops,
  end time, price, base) with click-to-jump; map fits the whole trip
- version history: commit() snapshots days+stays on every mutation; topbar
  vN pill with undo/redo + dropdown timeline showing a diff label per version;
  Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y; restore() re-enriches legs (snapshots may
  predate route enrichment)
- offline: staged 'prepare for offline' card in the trip rail (itinerary →
  POI details → vector tiles) that flips the topbar badge to '📦 offline ready'
- chat/scripts/nudges: Florence-specific strings generalized (stay name/dates,
  vibes, pacing summary) so both demo cities read naturally
2026-09-09 00:47:50 -04:00
efde2cc71b maps project: design, survey, mock app, and route-aware planning backend
- DESIGN.md: full design incl. driving-trip requirements (R1-R4),
  stays model, focus mode, mobile, provenance rules
- SURVEY.md: open-source landscape
- mock/: interaction mock (Florence itinerary, focus mode, stays,
  region stops, mobile layout)
- router/: Go module (stdlib-only) with Router interface
  (Valhalla + OSRM backends), stop_cost, optimize_stops, corridor,
  routectl CLI, bench (5 real NE-corridor tasks, 26 checks passing),
  integration tests, and setup-osrm.sh for the self-hosted router
- osm/: NH+MA+CT+NY PBFs (gitignored) + setup artifacts
2026-09-06 00:05:17 -04:00