Discover how Davao Transit is built, and explore Spine, its design system.

Explore

My Notes

davao transit

public transit app for davao city

main stuff
* trip planner
* bus routes
* jeep routes
* bus stops
* live bus tracker
* map
* offline

mobile first
low end phones matter
bad data should fail conservative

things that kept surviving
* geometry != transit meaning
* graph valid != rider useful
* proximity != transfer
* reverse linestring != reverse service
* geocoder != network authority
* road router != route authority
* fare != routing cost
* live data optional
* dwell != congestion
* static work at build time if possible
* keep request path small
* keep service ids
* keep direction
* keep stop ids
* keep variants
* don't invent shit the data doesn't prove


trip planner

first version was simpler
direct route / same route stuff
good enough to get ui working

then planner demo got ahead of engine

* richer cards
* transfers
* ride legs
* progress
* share
* savings / co2
* canned scenarios
* better loading
* jeep illustration

looked more finished than routing actually was

bad pattern
prototype starts acting like spec


planner topology

bus side easier

* official stops
* route ids
* directions
* schedules
* live vehicles

jeep side messy

* mostly linestrings
* weak stop semantics
* incomplete route descriptions
* direction not safe to guess
* overlapping routes
* no clean gtfs graph

line crossing doesn't mean transfer
near line doesn't mean transfer
reverse geometry doesn't mean reverse service


jeep connectivity

* explicit direction
* no guessed reverse
* bad direction should fail compile
* no proximity transfer
* shared canonical points only when actually accepted
* keep service identity on edges

better no route than fake route


bus connectivity

* official stop id matters
* stop order matters
* direction matters
* shared stop can mean real transfer
* proximity alone still weak


bus + jeep transfers

early version basically avoided invented intermodal

later reviewed / approved transfer work

+ check current transfer registry
+ check exact distance bound
+ check whether distance is just a limit or actual rule


bad planner result

origin about
7.11925,125.62562

to sampaguita street

roughly
* 22.2 km total
* 5.2 km walking
* 4.7 km before boarding
* 17 km ride
* 534 m final walk

graph said valid
rider says stupid

search wasn't necessarily broken
rules were


planner selection

historical rules around

* max 2 rides
* reject 3+
* 1500m max access each side
* 2000m total access
* 250m min useful ride
* don't board same service again
* hide near dupes about 100m / 2min
* same place about 25m
* best per first directed service
* around 4 results
* keep bus / jeep diversity when both wanted

+ verify current numbers


planner states

* unavailable
* same place
* outside area
* too far
* no connection
* trips

access line warning was basically
direct line only. streets / barriers not checked

+ check current wording


planner costs

historical

* walk 75m/min
* ride 300m/min
* jeep board +4min
* bus board +7min or live
* same place 25m
* nearest tie +1m

search cost not fare
search cost not display time
search cost not rider preference

+ verify current constants


planner search

changed a bunch

* direct planning
* graph search
* dijkstra-ish
* reverse from destination
* multi-source destination
* packed numeric state
* uint32 heap
* heapify tried / dropped
* richer state
* multi-label / pareto later

not going to pretend it was one clean evolution

+ check current solver
+ check reference solver
+ check actual pareto state


server planner

got too expensive for worker budget

remembered warm cpu
~560ms to ~1900ms

+ need exact benchmark context

static topology was being paid for too often

wrong place for the work


client planner

move static work to build

build
* load transit
* validate
* normalize
* compile
* planner image
* hash / metadata

runtime
* download
* verify
* cache
* mount
* web worker search
* materialize result

fixed server cpu problem

created new problems
* binary compat
* cache
* integrity
* worker protocol
* memory
* stale image
* partial download
* storage eviction


planner image

historical shape

* binary
* typedarray views
* fixed point coords probably e7
* csr-ish adjacency
* string tables
* sha256
* content addressed filename

+ check exact header
+ check version
+ check current byte layout
+ check current image size
+ check actual parse work
+ don't say zero parse unless true


offline planner

shell-only offline isn't enough

first online
* app
* transit data
* planner image

later offline
* app opens
* local planner image
* worker searches locally

live feed not required

problems
+ bad hash
+ partial download
+ stale app / new image
+ new app / old image
+ indexeddb failure
+ cache failure
+ service worker stale
+ storage eviction
+ corrupt image
+ protocol mismatch


place search

separate from routing

text
resolver
coords
planner

tried / researched
* nominatim
* google places new
* place details
* session tokens
* map tap
* recents
* offline fallback

geocoder finds place
doesn't create transit edges


bus data

dibs-ish upstream

historical route ids
* r102
* r103
* r402
* r403
* r503
* r603
* r763
* r783
* r793

am / pm directions

dcbus ids around 01..10

historical counts
* ~125 upstream stop locations
* ~85 normalized official stops
* ~9 route families
* ~18 directional routes

+ verify current counts


bus geometry

first lines were basically stop to stop

topology okay
map looked wrong

straight lines cut blocks
ignored roads

osrm reconstruction

* ordered stops
* route between stops
* stitch
* validate
* fallback

historical good pass
* 18/18 directional geometry
* zero fallback
* ~85 stops
* ~228kb bundle

road router fixed shape
didn't become source of truth


bus stops

official stop id is strong evidence

used for
* route sequence
* planner connectivity
* direction
* tracker arrivals
* eta
* route detail

+ check normalization centralized
+ check stable ids survive build


bus vector tiles

route geometry
tile build
validate
r2
client
maplibre

geojson fallback

tile path and fallback should mean same route

historical zoom maybe 8..16

+ verify


jeep data

historically ~52 linestrings
thousands of graph nodes
no official stop graph like bus

* explicit direction
* no reverse guess
* no proximity transfer
* geometry useful but weak authority


commute davao analysis

rough model

* 52 jeep features
* directed linestring order
* nearest vertex snap
* exact coordinate connection
* geographic dijkstra
* no auto reverse

useful baseline

problem
geographic node doesn't tell boarding / transfer / service state


jeep route inference

tried

* manual waypoints
* landmarks
* nominatim
* osrm
* mapbox directions
* chunks
* stitching
* continuity penalties
* visual review
* hausdorff
* frechet
* python / sse tool
* maplibre viewer

routes looked at
* baguio district - roxas
* marahan - roxas
* baracatan - roxas
* toril - astorga
* panabo - ecoland
* panabo - roxas

hard part wasn't generating a road path
hard part was knowing if it was the actual jeep route


jeep inference failures

* wrong landmark
* incomplete waypoint order
* outbound != return
* waypoint limit
* bad chunk seam
* router picks faster wrong road
* plausible geometry but wrong service
* good shape score but weak provenance

kept it as candidate / review tooling
not truth


live bus source

yutong / dibs-ish

fields roughly
* lat
* lon
* report time
* speed
* online state
* direction angle

poll around 20 sec historically

+ verify current


collector

* poll
* normalize bus id
* route / direction
* utc time
* reject junk
* retry
* append observations

historical unique
vehicle_id + reported_at

indexes
* route,direction,time
* vehicle,time

debug / ops endpoints existed
* start
* stop
* status
* collect
* routes
* observations


telemetry dirt

* dupes
* late reports
* stale vehicles
* gps drift
* heading noise
* inconsistent ids
* missing route
* wrong direction
* bad values
* upstream down
* startup weirdness
* retries arriving out of order

latest point isn't enough


vehicle normalization

api and collector had duplicated normalization at one point

bad idea

+ check if centralized now


route projection

gps point -> directional route position

derive
* snapped point
* progress
* distance along route

then can do
* stop visits
* segment timing
* route progress ui
* eta

+ max snap distance
+ parallel roads
+ loops
+ wrong route candidate


stop events

derive
* arrival
* departure
* dwell
* segment run

single radius check probably too weak

+ stop radius
+ hysteresis
+ min dwell
+ duplicate suppression
+ direction state


transit intelligence

observation
projection
progress
stop visit
dwell
segment run
history
eta

turn lat/lon into transit state


road intelligence

first idea
slow bus = slow road

wrong around stops

bus stops because it's a bus

split it

transit side
* arrivals
* dwell
* segment runs
* service behavior

road side
* moving samples
* map match
* road segment
* multiple vehicles
* slowdown


valhalla

used / explored for road matching

issues worked on
* matcher hardening
* config
* bounded analytics
* backlog
* route-road links
* eta adjustment

matcher can fail
collector should keep working


road intelligence problems

* dwell looks like traffic
* one vehicle dominates
* sparse samples
* bad trace
* timeout
* backlog
* repeated roads on loops
* wrong route-road association
* stale slowdown
* expensive matching

+ sample threshold
+ independent vehicle threshold
+ confidence
+ stale window


live tracker ui

source updates maybe every 20 sec
screen updates every frame

interpolate
don't pretend interpolation is measurement

worked on
* smooth marker
* bearing smoothing
* stale state
* stop movement when stale
* camera
* selected bus
* route context
* direction

tried route-first / context-first variants


map

maplibre

stuff on it
* bus routes
* jeep routes
* stops
* live buses
* planner routes
* access / egress
* route detail
* selected state
* vector tiles
* geojson fallback
* camera state

different render paths still need same route ids / semantics


map styles

tried
* positron-ish
* de-emphasized roads
* no roads
* transit first
* map catalog
* route-first tracker views

geographic map stayed primary

schematic separate


pwa

goals
* low end phone
* app shell
* static transit cached
* planner local
* live stuff degrades separately

core offline should not need
* live feed
* road matcher
* server route search


home ui

historical direction

* near black cover
* white logo
* davao transit
* move around davao better.
* white cards
* bus cyan #00c0e8
* jeep yellow #ffcc00
* restrained motion
* mobile first
* route illustrations


ui prototype lesson

good looking demo != implemented product

kept happening


storybook / visual fidelity

lots of stories rendered
still didn't match

render test != visual test

wrong can be
* spacing
* weight
* type
* dimensions
* alignment
* hierarchy
* responsive behavior

before screenshot
after screenshot
same viewport
same state


design system boundary

packages/ui looked like design system by name
wasn't a good product boundary

app ui still moving

spine became future external design-system direction

don't freeze temporary app components as architecture


schematic map

goal

real geojson
normalize
schematicize
edit
validate
approve
save
export
preview

0 / 45 / 90 degrees

must keep
* route id
* variant id
* stop id
* stop order
* transfers
* shared corridors
* loops
* out-and-back
* inbound / outbound
* cw / ccw
* figure eight
* route separation


schematic editor

poc had / aimed for

* import
* drag drop
* route select
* stop select
* transfer select
* segment select
* draw / repair
* move stop
* control points
* labels
* grid snap
* layers
* ghosting
* conflicts
* undo redo
* keyboard shortcuts
* save load
* project import export
* approval block
* fit view
* pan zoom
* scale / compass
* maplibre preview


schematic auto layout

looked good until hard cases

* loops distort
* turning bulbs collapse
* shared corridors break
* inbound / outbound confusion
* false crossings
* route separation
* topology damage hidden by clean geometry

full auto not enough

auto pass
human correction
validation
approval


loom

loom-wasm future direction
not current production

maplibre geographic map still current unless code changed


forecasting

separate research

* python
* pytorch
* transformer-ish
* observations
* route
* gps
* speed
* bearing
* time
* state

rule

if it doesn't beat deterministic baseline don't ship it

+ check actual results


community data ideas

future / experimental

* community jeep updates
* bus updates
* stop corrections
* route drawing
* assisted snapping / aimbot-ish helper

not shipped unless source says so


brand

logo work

* geometric reconstruction
* construction circles
* centered
* rotated
* top-right pointing
* construction svg
* morph
* optical scaling
* small size correction
* cyan around #41d1f9

math scale alone looked wrong small
needed optical correction


route teaser motion

* route draws on scroll
* mark follows path
* arrow tangent aligned
* tip on line
* color shouldn't lead tip
* shortest angle path
* avoid rotation inertia drift
* smooth corners
* clickable stops
* mobile
* reduced motion
* one raf
* precomputed samples

targets discussed
* <0.5px tip error
* <0.5px stop alignment
* same feel 60 / 120 / 144hz

stop copy
* para po!
* diri lang ko ya.
* paabot sa bayad. salamat!
* dira lang ko sa unahan kuya.
* para daw!


testing

don't say done because one test suite is green

separate
* unit
* integration
* package tests
* typecheck
* lint
* prod build
* data validation
* artifact validation
* offline
* visual regression

planner audit once had about 116/117 tests while typecheck/lint/build still had failures

not done


data provenance

stronger
* official stop ids
* verified directional service data
* canonical source data
* reviewed transfer data

weaker
* geocoder output
* road router output
* shape score
* proximity
* inferred reverse

validation means internally valid
doesn't mean true


recurring bad patterns

* stale workspace
* stale zip
* wrong checkout
* old archive treated current
* subsystem green / repo red
* generated file treated source
* prototype treated spec
* render smoke test treated fidelity
* inferred geometry treated truth
* proximity treated transfer
* stopped bus treated traffic
* duplicated normalization drift
* optional external service taking down core path


engineering approach

* kiss
* yagni
* patterns pay rent
* no cargo cult dry
* no speculative abstraction
* define domain rule once
* share network contracts
* build static stuff once
* keep hot path small
* bound work
* avoid extra api calls
* fail closed on bad transit data
* isolate optional services
* no live feed shouldn't kill planner
* matcher failure shouldn't kill telemetry


rough sequence

public transit app
map / routes / stops
simple planner
richer planner ui
planner rules get stricter
geometry-only connectivity proves unsafe
direction / service identity explicit
bad graph-valid trip exposes selection problem
rider limits
server planner too expensive
compile graph ahead
binary planner image
browser worker search
offline planner
artifact lifecycle headache

bus
official stops
straight route geometry
road-following reconstruction
vector tiles
geojson fallback

jeep
raw linestrings
strict direction
waypoint experiments
nominatim / osrm / mapbox
chunk / stitch / score
human review

live
latest point
observation history
validation / retry
projection
stop events
dwell
segment runs
eta
road intelligence split

ui
home / planner experiments
storybook
rendered but visually wrong
screenshot regression

schematic
automatic octilinear
topology failures
human-in-loop editor
validation
loom future

forecast
deterministic baseline
ml experiment
no promotion without win


current things to check

+ canonical repo
+ canonical branch
+ bus route count
+ bus stop count
+ jeep route count
+ current route ids
+ transfer rules
+ intermodal bound
+ access caps
+ min ride
+ duplicate suppression
+ same place threshold
+ current search
+ pareto state
+ cost constants
+ planner tick size
+ binary format
+ planner image size
+ cache ownership
+ worker protocol
+ cpu benchmark
+ telemetry cadence
+ stale timeout
+ normalization
+ projection threshold
+ stop radius
+ dwell rules
+ road sample threshold
+ independent vehicle threshold
+ valhalla config
+ slowdown confidence
+ vector tile zooms
+ forecast status
+ spine status
+ loom status