Self-hosted map tiles with full bikeway rendering - #73
Merged
rodrigohpalmeirim merged 1 commit intoJul 22, 2026
Merged
Conversation
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
merged commit Jul 22, 2026
154af79
into
t3code/bike-route-planning
2 checks passed
rodrigohpalmeirim
added a commit
that referenced
this pull request
Aug 15, 2026
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
added a commit
that referenced
this pull request
Aug 15, 2026
* add route planning with gira bikes and location search
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.
* Keep search UI below the map loading preview
- Add a z-index to contain SearchBar’s stacking context
- Ensure the live map loading preview remains visible above search UI
* Refactor routing.ts, add proper types to osrm
* Add debug mode with walking and fake trip
Fix interaction with destination input pill
* Clip routes on the client side.
* open the station menu automatically at the route's pickup station
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.
* only clip the route along legs the user can be traversing
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).
* defer pin drops past the double-tap window to keep one-finger zoom
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.
* improve trip status metrics formatting and progress tracking
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.
* search for bike stations in the search bar
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.
* show search history and dismiss the search with keyboard or back button
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.
* clear unsubmitted search text when pressing the clear button
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.
* make the keyboard search key select the first result
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.
* fix layout when rotating during a trip and re-lock portrait after trips
* collapse the search bar into a round button during landscape trips
* avoid flashing the fallback name while reverse-geocoding a dropped pin
* cap the search bar width on large screens
* self-host map tiles and render the full bikeway network (#73)
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.
---------
Co-authored-by: ttmx <git@tteles.dev>
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.
Note
Stacked on #71 (branch includes its commits); retarget to
mainif #71 merges first, or merge after it.What
Moves the base map from
tiles2.intermodal.ptto our own tile server athttps://tiles.gira-mais.app, and fixes the map showing fewer bikeways than the routing service actually uses.Why
Routing (#71) and the map tiles are both built from OSM, but the standard OpenMapTiles schema drops the
cycleway/cycleway:left/cycleway:righttags, so on-road cycle lanes — ~89 km in Lisbon alone, ~50% more than the dedicated cycleways the map showed — were invisible no matter how the style was filtered. The OSRM bicycle profile weights those lanes favourably, so routes kept preferring bikeways the map didn't draw.Server
/data/v3.json— OpenMapTiles base map built with Planetiler (~400 MB)/data/bikeways.json— 1.3 MB overlay with every way the bicycle profile treats as cycling infrastructure, classifiedkind=cycleway|lane|sharedApp
map-style.ts: sources/glyphs/sprite point atTILES_URL; cycleway layers read the bikeways overlay; new dashedbikeway_lanelayer for on-road lanesTest plan
bun run check, eslint on touched filescyclewayandlanefeatures