Route planning with GIRA bikes and location search - #71
Merged
Conversation
4 tasks
rodrigohpalmeirim
force-pushed
the
t3code/bike-route-planning
branch
from
July 17, 2026 19:43
831af9a to
0ad825a
Compare
rodrigohpalmeirim
marked this pull request as ready for review
August 15, 2026 18:51
Search for a destination, drop a pin on the map, or select a station, and instantly see the best route from the current location: walk to a station with bikes, ride to a dock near the destination (prioritizing bikeways), walk the rest — or walk directly when that's faster. During a trip, routes go from the current position to the best dock and feed the trip HUD's destination metrics (distance left, time left, arrival). Served by self-hosted OSRM instances (foot profile + bicycle profile with cyclability weighting and a dedicated-cycleway bonus) and a Photon geocoder behind routing.gira-mais.app, covering the greater Lisbon area. Also upgrades vitest so the test suite runs again (vitest 1.x was incompatible with vite 7), using happy-dom as the test environment.
- Add a z-index to contain SearchBar’s stacking context - Ensure the live map loading preview remains visible above search UI
Fix interaction with destination input pill
When a route involves picking up a bike (or leads to a station), the station menu now opens on its own once the user gets within unlocking distance of that station, so unlocking a bike doesn't require tapping it — which used to replace the route. It opens once per approach, so dismissing it sticks. Tapping a station that is already part of the route (pickup, dropoff or the station destination) now only opens its menu and centers it, instead of rerouting to that station.
Route clipping projected the position onto the whole route, so walking across the bike leg on the way to the pickup station (or riding across the final walking leg) would clip sections that weren't traversed yet. The projection is now restricted to the leg of the current progress plus consecutive legs matching the current travel mode (foot without a trip, bike during one).
Tapping the map to set a destination broke MapLibre's double-tap and double-tap-drag zoom gestures, since 'click' also fires on their first tap. Dropping the pin is now deferred by 200 ms and cancelled when a gesture materializes (dblclick, zoom, drag, rotate or pitch start). The route request still starts at the moment of the tap — only showing it is deferred — so the pin usually appears together with its route and the total time to see a route is unchanged.
Distance left now shows meters under 1 km like distance traveled, and
time left is rounded to minutes ("26min", "1h26min", "<1min") with the
hour unit styled like the minute one.
Both distance left and arrival time are also refined with the clipped
progress along the route on every position update, instead of stepping
only when a recompute lands every ~30 m.
Stations are matched locally (case- and accent-insensitive, by name or station number) and shown at the top of the results instantly, with live bike/dock availability, while geocoder results follow. Selecting one behaves like tapping the station on the map: the route ends at the station and its menu opens.
Focusing the search bar with an empty query now shows the last ten search selections, persisted across sessions; station entries resolve against the live station list and fall back to a plain location when the station is gone. Dismissing the keyboard (back press, done key or swipe) also unfocuses the search bar, closing the results — via the native keyboard event, so focusing the bar cannot re-trigger it. The back button handler dismisses the search first as a backstop when the keyboard is already closed.
Clearing relied on the routeDestination subscription resetting the query, but stores skip notifying when the value is unchanged — with no destination selected the store is already null, so typed text survived the clear button. Reset the query and results directly.
The search key picks the first visible row: the top station match if any, else the top geocoder result. When the debounced search hasn't returned yet, it runs immediately and the top hit is selected when it arrives.
The map style now points at tiles.gira-mais.app (tileserver-gl on gira-mais-vps) instead of tiles2.intermodal.pt, and draws bikeways from a dedicated tileset built from the same Geofabrik Portugal extract the routing server uses, so displayed bikeways always match what routing prefers. The standard OpenMapTiles schema drops cycleway:left/right tags, so on-road cycle lanes (~89 km in Lisbon) could never be rendered from the old tiles. The new bikeways tileset classifies every way the OSRM bicycle profile favours into kind=cycleway|lane|shared; lanes render dashed, and all bikeway layers now start at zoom 11 instead of 14.
rodrigohpalmeirim
force-pushed
the
t3code/bike-route-planning
branch
from
August 15, 2026 18:57
154af79 to
a4e3c91
Compare
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.
What
Adds multimodal route planning to the app: search for a destination (or drop a pin on the map, or tap a station) and instantly see the best route from your current location, combining walking and GIRA bikes.
https://routing.gira-mais.app (self-hosted on gira-mais-vps):
cyclabilityweighting with an extra 1.2× bonus for dedicated cycling infrastructure, so bike legs prefer bikeways while walking legs don't careRoute planning (
src/lib/routing.ts)destination/distanceLeft/arrivalTimefields ofActiveTrip, lighting up the existing TripStatus metricsUI
Tests & tooling
computeRouteagainst the live routing server (multimodal legs, empty-station avoidance, walk-only shortcut, on-trip routing, station destinations, mid-computation destination rename race)NoRoute)Server notes
Load-tested: ~900 req/s routing capacity vs ~75 req/s modeled worst-case peak (from collected usage data); Photon is the soft spot at ~45 req/s vs <5 req/s needed.
Test plan
bun run check, lint on touched files,bunx vitest run(10 tests)