feat(roadtrip): a road trip addon with drive-first days and search along the route - #2124
Draft
mauriceboe wants to merge 12 commits into
Draft
feat(roadtrip): a road trip addon with drive-first days and search along the route#2124mauriceboe wants to merge 12 commits into
mauriceboe wants to merge 12 commits into
Conversation
The trip planner could show one day's route at a time and nothing across days, so a road trip had to be pieced together in Google Maps (#1797, #435). The addon adds a mode to the plan view rather than a tab of its own: the map stays the workspace and gains the route over the whole trip, while the left rail becomes one chain of stops with the driving distance and time between them and arrival times that follow from them. A time pinned on a stop is kept and restarts the chain, so a ferry or a museum ticket stays where the user put it. Car bookings can finally carry the stops the schema has always allowed: the endpoints table has held role/sequence for every transport type since #384, but only flights and trains had an editor for them. The MCP tools already accepted them, so this closes the gap the other way round. Legs are fetched one at a time and spaced out — the public routing hosts answer a whole trip's worth of requests with 429 — and the distance cap now looks at the single leg instead of the sum, which is what dropped long drives entirely.
The road trip mode could show the route but not what sits beside it, which is half of what planning a drive is about (#1797). The right column becomes a corridor search: pick what you need, pick how far off the route you will go for it, and the day's drive is searched box by box. The boxes come from the route itself rather than the viewport, so the answer is "on the way", not "on screen"; each hit reports how far off the route it lies and how far into the drive it comes, and adding one opens the normal place form so it lands on the day like any other stop. Overpass is asked in plain bounding boxes rather than with a polyline filter, because that is what the existing /api/maps/pois route accepts and what the mirrors can answer; the geometry work is pure and tested. Long routes stop at ten boxes and say so instead of quietly searching half the drive. Fuel, charging, rest areas and campsites join the server's POI categories, so the explore pill and the MCP search_pois tool gain them too. A car booking's stops now also reach the PDF, which printed pick-up and return with everything in between missing.
…e request Three things the first cut got wrong. Switching into road trip mode took its time: every leg was its own routing request, paced a second apart so the shared hosts would answer at all. The router already returns a leg per consecutive pair of the waypoints it is handed, so a day is now one request — split only where the travel mode changes, because one request carries one mode. Answers that come from the cache are no longer paced at all, which is what made returning to the view feel broken. A list of petrol stations is half an answer: which one is on your side of the road is the other half. The corridor search therefore moved out of the panel into the planner, and its hits are handed to the map like any other place suggestion — grey, because they are not part of the trip until you add one. The explore pill stands down while the mode is on: it draws into the same channel and answers the same question for a drive. The panel itself was a stack of controls without an order. It now reads as header, search, results: what is being searched (with the day's distance and driving time), what is being looked for, how far off the route, then the hits grouped by kind with a count — and a progress bar while the boxes are being worked through.
…tops The corridor was built from the day's stops, so it followed straight lines across country the car never drives. Between two cities that line runs through open fields while the motorway swings wide of it, which is why every hit came back sitting on a stop rather than on the way. The days now carry the routed geometry, and the corridor is cut from that. Same trip, same search: 192 hits spread over the whole 482 km — first fifty, middle, and the last stretch — where before all of them reported kilometre zero. Following real roads takes more boxes to cover the same drive, so they go out three at a time instead of strictly one after another, and the budget rises from ten to sixteen. The panel head loses the sentence explaining what the sections below already say, and the day's distance and driving time, which the day list has been showing all along.
…a stretch went unsearched A petrol station and a campsite both came back as the same nameless grey dot, because the road categories only existed on the server: the map falls back to grey for a category it does not know. They now sit in the client's category table too — with the pump, the bolt, the parking sign and the tent, in their own colours — which both map renderers and the popup read from the same place. They stay out of the explore pill's own list on purpose: that pill offers what a city visit needs, and while road trip mode is on it is not on screen anyway. The search also stayed quiet when only some boxes failed, which reads as "no fuel on this stretch" when the truth is that nobody looked. A partial failure now says how many stretches went unanswered.
A corridor full of petrol stations was a row of identical coloured dots, and the one thing that tells them apart is the brand. OSM carries brand:wikidata on most of them — 88% of the fuel stations in a Berlin box — Wikidata carries the logo as property P154, and Commons serves it. The bytes are proxied and cached rather than linked: one self-hosted instance asking Wikimedia for a handful of logos is a very different egress profile from every visitor's browser announcing which petrol stations they are looking at. The cache is in memory because a GET must not write to the database, and there are only so many fuel brands; a brand with no logo is remembered too, or every pan would ask again. A logo-less or unreachable brand answers 204 and the pin keeps its category icon, so nothing depends on Wikidata being reachable. Both renderers show it, since both read the same category table.
…egory The road trip corridor asks for fuel, charging, rest areas and campsites over a dozen boxes, and asking per category was four times the requests for the same answer — slow, and four times the chance a shared Overpass mirror times out and reports the stretch as unsearched. `category` now takes a comma-separated list. Each OSM selector belongs to exactly one category, so a hit is still labelled with the kind it answered, and the result cap is shared out per category so one dense kind can't crowd the rest. The MCP tool takes an array for the same reason. Also puts the POI, plugin and route via-point pins on the map's render clock, the same fix the planned-place pins already had: a library marker repositions on every pointer sample while the canvas draws once a frame, so the suggestions swam during a drag and snapped back on release.
The addon shipped with only its two pure modules under test, which left the parts that actually talk to the network — and the panel a user reads the answer from — resting on nothing. Covers the routing hook (one request per travel-mode run, day default over trip fallback, retry then give up, no re-fetch on a rename, abort on leave), the corridor search (one query per box carrying every kind, the perpendicular filter, dedupe, the retry pass before a stretch counts as unsearched), the corridor state (roads driven over the line between stops, day follow-along) and both panels. Two findings from writing them, fixed here: - A dead straight leg is reduced to its two ends by the simplifier, and tiling walks point to point — so a 250 km motorway stretch produced a box at each end and nothing in between, while the search still reported itself complete. Steps longer than half a box are now split before tiling. - projectOnSegment guarded a degenerate segment with `lenSq === 0`, which two ends that came through a chain of floating point arithmetic never hit. Also splits the anchor resolution out of computeSchedule, which was three levels of nesting for one question, and sorts the POI cache key with a comparator.
The client's lib is ES2020, where Array#at does not exist. Local typecheck ran before the file was added and CI caught it.
|
Three read paths built the same nested place object by hand, and two of them left `stop_type` out of it while their SQL was already selecting the column. The effect was visible: a petrol station added along a route drew correctly for a moment from the optimistic write, then the server's answer replaced it with a place that had no kind, and the road-trip rail redrew it as an ordinary numbered destination. `getAssignmentWithPlace` now goes through `formatAssignmentWithPlace` like the list path already did, which removes the third copy rather than adding the missing field to it. The two queries in the days service gain the column, and its hand-built shape gains the field it cannot yet be spared.
The query carries `[timeout:20]`, so the mirror is allowed twenty seconds of work on it — and the fetch around it was aborted after twelve. A mirror under load that would have answered at fifteen never got the chance, and the caller saw a failed area. That is what makes a corridor search along a long drive report half its stretches as unsearchable: the stretches were being searched, we just hung up. Both numbers now come from one constant, the client budget is the query's own plus room to hand the answer back, and a test holds the relationship rather than the two literals.
…the way The road-trip addon's second half: the parts that let a planned drive be argued with rather than only read. Reshaping. A via records which stop it follows by position, and that position is resolved fresh every time the day is routed — so inserting, reordering or removing a stop moved the ground under every via after it, and the drive silently snapped back to the road it had been steered away from while the handle still sat on the map where it was left. The correction is computed against the day as it stands and written with the same change, in one transaction: a half-applied re-anchoring mixes two numbering schemes in one day. The one judgement that needs geometry — which half of a split leg a via belongs to when a stop lands in the middle of it — is measured on the road actually driven, so a fuel stop added to a bent leg keeps the bend on the side it belongs to. Alternatives. OSRM answers most long legs with exactly one route, which read as "there is no other way" when a perfectly good slower road existed. When it offers nothing, the same drive is asked for again without the motorway, and failing that without the tolls — one question at a time, stopping as soon as one lands. The offer says what it is rather than how much slower. Stops along the way. Everything the corridor search can find is a pause, not a destination: fuel, charging, rest areas, campsites, food and sights all sit inside the leg they were found on, carry their own colour and icon, and are left out of every stop count. That colour is defined once and read by the rail, the corridor list, the map pin and the picker — and it replaced the chains' logos, which turned a corridor full of petrol stations into a row of advertisements and left the brands with no logo on file looking like something else entirely. How long a stop takes was readable in the rail and writable nowhere; new places were given an invented hour that nobody had entered. It is now set from the rail, cleared as a zero because the update statement folds a null into "leave it alone", and new places start with nothing. Both columns are rebuilt on one type scale, anchored to the day plan's own sizes so switching tabs does not change the size of the text beside it. The rail reads as trip totals, day cards and a chain of stops with the drives between them; the corridor column as what to look for, how the search is getting on, and what it found. Six category pills over three lines became one dropdown, which is the space the results wanted. Also: the offered routes are framed on the map when the picker opens (`fitKey` carries no coordinates and each renderer reads it as "the selected day"), road trip mode does not cluster at all so the whole drive stays readable zoomed out, and the drive-time labels finally render in the app's own typeface instead of Leaflet's Helvetica. All of it in both renderers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Description
A road trip addon: a second way to look at a trip, for the case where the driving is the trip.
Switching a trip into road trip mode replaces the day-by-day list with a drive-first view. Each day reads as a chain of stops with the drive between them, and the times cascade the way they actually do on the road: leave at nine, drive two hours, stay ninety minutes, and everything after that moves with it. Anchoring a stop to a fixed time restarts the chain from there, so a ferry or a check-in holds while the rest of the day rearranges around it. The view warns when a day ends after midnight or runs past the last stop's opening time.
Alongside it, search along the route: pick a day, pick what you need (fuel, charging, rest areas, campsites, food, sights) and a corridor width, and it looks along the roads actually driven rather than along the straight lines between stops. That is the difference between finding one petrol station and finding forty. Hits are drawn on the map as suggestions and listed in the right column with how far off the route and how far along it each one is, so "which of these" and "which side of the road" are both answerable. One click adds a hit as a stop on that day.
The drive can be argued with. Dropping a waypoint on a leg pulls it onto another road, and the leg keeps that shape. What is stored is the point where the chosen road differs most from the default, not a polyline, so the choice survives the next OSM update and every stop that moves afterwards. "Other ways" lays the alternatives for one leg over the map with the drive time on each road, placed where that road is unmistakably itself rather than on the stretch they share.
Stops along the way are pauses, not destinations. Everything the corridor search can find sits inside the leg it was found on, carries its own colour and icon, and is left out of every stop count: a day with a charger and a rest stop between four places is a four-stop day, not a six-stop one. How long you stay somewhere is set from the rail and pushes every later arrival back.
Where the work is. Everything above works end to end, but this is a skeleton with real gaps: no mobile road trip surface yet, no range or refuel suggestion, no per-day driving limits, and no wiki page. Opening it as a draft to have somewhere to keep building.
Notable pieces
@RequireAddon. Nothing changes for an instance that leaves it off.GET /api/maps/poisand thesearch_poisMCP tool both take a list now.Two fixes worth calling out separately, both older than this branch:
stop_typeout of it while their SQL was already selecting the column. The third copy is gone rather than corrected.[timeout:20]and the fetch around it was aborted after twelve seconds, so a mirror under load never got to finish and the corridor reported the stretch as unsearchable. Both budgets now come from one constant.Related Issue or Discussion
Addresses discussion #1797
Type of Change
Checklist
devdevbranch, notmain(wiki-only PRs are exempt)