wBus is a modern, high-performance public transit tracking and timetable exploration web platform built for Wonju City (원주시), South Korea. Engineered specifically to solve transit scheduling pain points—particularly for Wonju citizens and commuters heading to and from Yonsei University Mirae Campus—wBus combines real-time GPS vehicle tracking, automated route polyline snapping, municipal timetable scraping, and official service alerts.
- Campus Departure Isolation: Curated schedule directories specifically for routes 30, 34, and 34-1.
- Depot Filtering: Eliminates misleading depot origin times (Jangyang-ri), displaying only verified Yonsei University campus departures (Routes 30 & 34) and Hoechon departures (Route 34-1).
- Live Departure Spotlight: Automatically calculates the next upcoming departure with a dynamic countdown timer
(
N minutes remaining), scheduled departure indicators, and real-time status cues. - Detailed Timetable Modal: Single-column inspection drawer showing full operational runs, departure sequences, operational notes (e.g., via Maeji-ri / Wonju Station), and single-click CSV export functionality.
- On-Demand Engine Loading: The MapLibre GL engine and telemetry listeners are mounted exclusively when the user navigates to the Real-Time Map tab.
- Zero Overhead on Schedules: Browsing timetables triggers zero background map tile transfers or telemetry requests, maximizing battery life and conserving mobile data.
- Live User Geolocation ("내 위치"): Integrated high-accuracy GPS user positioning with real-time tracking, pulsing location indicator, accuracy radius circle, service boundary validation with out-of-bounds notifications, and glassmorphic control integration.
- Theme-Adaptive Navigation Controls: Map control buttons (
NavigationControlfor zoom in/out and 3D pitch/bearing compass, plusGeolocateControl) dynamically adapt to the active color theme. In light mode, controls render with clean frosted glass, subtle gray dividers, and high-contrast slate icons; in dark mode, controls transition to an obsidian midnight glass palette with delicate bright borders, glowing sky-blue GPS indicators, a vivid red North needle on the compass, and full Korean screen-reader accessibility labels. - Polyline Map-Matching: Bus GPS coordinates from the national transit portal are matched onto high-precision OSRM route polylines with 3-second animated transition smoothing.
- Directional Clarity: Automatic detection of UP (outbound) and DOWN (inbound) vehicle paths, vehicle occupancy badges, and interactive bus stop arrival estimates.
- Complete Route Coverage: Directory covering all active bus routes operating throughout Wonju City, scraped directly from the Wonju Intelligent Transportation System (ITS).
- Operational Day Modes: Multi-calendar switching between Automatic detection, Weekdays, Saturdays, and Sundays or National Holidays.
- Client-Side Bookmarks: Bookmark favorite bus routes with persistence in browser LocalStorage.
- Cross-View Navigation: Launch directly into the real-time map view with the target route preselected from any timetable card.
- Municipal Announcements: Direct ingestion and caching of service changes, construction detours, and schedule adjustments published by Wonju City ITS.
- In-App Notice Drawer: Dismissible header alerts and dedicated detail modals to read official notices without leaving the app.
- Tiered Cache Architecture: L1 In-Memory LRU Cache with request deduplication and coalescing, coupled with CDN edge
micro-caching (
s-maxage=2, stale-while-revalidate=3). - Circuit Breaking and Fallbacks: Graceful fallback to static cached snapshots whenever external government APIs experience timeouts or service outages.
The project adheres to Feature-Sliced Design (FSD) principles, enforcing strict modular boundaries, unidirectional dependencies, and domain-driven encapsulation:
src/
|-- app/ # Next.js App Router
| |-- api/ # Serverless API routes and edge endpoints
| | |-- bus/ # Route schedules, health checks, cache refresh
| | | |-- [routeId]/ # Real-time telemetry endpoint for route ID
| | | |-- health/ # External API connectivity check
| | | `-- refresh/ # Forced schedule scraper trigger
| | |-- bus-arrival/ # Bus stop estimated arrival query
| | |-- bus-stops/ # Stop list for route ID
| | |-- notice/ # Wonju ITS announcements
| | |-- route-stops/ # Directional stops by route name
| | |-- schedule/ # Timetable data and refresh trigger
| | `-- data/ # Static file fallback handler
| |-- live/ # Real-time map route (/live alias)
| |-- map/ # Real-time map route (/map)
| |-- privacy/ # Privacy policy documentation
| |-- schedule/ # Timetable page route (/schedule)
| |-- globals.css # Tailwind CSS 4 theme imports and utility rules
| |-- layout.tsx # Root HTML layout, font setup, theme provider
| `-- page.tsx # Application shell entry point
|-- data/ # Fallback datasets and route identifiers
|-- entities/ # Domain entities and core business definitions
| |-- bus/ # Bus telemetry types, transformers, and utilities
| |-- notice/ # Notice parsers and storage models
| |-- route/ # Route definitions, polyline hooks, color palettes
| |-- schedule/ # Schedule definitions, time math, and parsers
| `-- station/ # Bus stop coordinates and sequence definitions
|-- features/ # User-centric application workflows
| |-- live-tracking/ # Telemetry store, external store sync, direction math
| `-- map-view/ # Map state persistence, route headers, viewport hooks
|-- shared/ # Cross-cutting infrastructure and reusable UI
| |-- animation/ # Interpolation and coordinate transition helpers
| |-- api/ # HTTP clients, retry wrappers, handler factories
| |-- cache/ # CacheManager (LRU, TTL policies, request coalescing)
| |-- config/ # Environment variable schemas, routes, storage keys
| |-- context/ # Global application and map context providers
| |-- hooks/ # Generic utility hooks (debounce, window size, etc.)
| |-- lib/ # Time helpers and utilities
| |-- types/ # Core TypeScript interfaces and shared schemas
| |-- ui/ # Design system primitives, bottom navigation bar
| `-- utils/ # Coordinate math, route formatting, concurrency limiters
`-- widgets/ # Composite UI modules
|-- AppShell/ # Main tab orchestrator and route sync controller
|-- BusListSheet/ # Interactive drawer listing active buses on route
|-- MapContainer/ # MapLibre GL wrapper, vector route layers, live markers
|-- NoticeWidget/ # ITS announcement banners and detail modals
|-- TimetableWidget/ # City-wide searchable schedule directory
`-- YonseiTimetableWidget/# Dedicated campus schedule cards and export modal
Dependency Rule: app -> widgets -> features -> entities -> shared.
+-------------------------------------------------------------------------+
| EXTERNAL DATA SOURCES |
| |
| apis.data.go.kr (TAGO API) its.wonju.go.kr OpenStreetMap |
| - Real-time bus telemetry - Official schedules - Road network |
| - Bus stop arrival data - Notice alerts vector data |
+-------------------+--------------------+-------------------+------------+
| | |
| | v
| | +------------------+
| | | OSRM Engine |
| | | (MLD Algorithm) |
| | +--------+---------+
| | |
| | v
| | +------------------+
| | | scripts/cache/ |
| | | (Polyline JSONs) |
| | +--------+---------+
v v |
+---------------------------------------------------+ |
| Next.js API Layer | |
| - GET /api/bus/[routeId] (Micro-cached polling) | |
| - GET /api/bus (Schedule metadata) | |
| - GET /api/schedule (Timetables) | |
| - GET /api/notice (Official ITS notices) | v
+-------------------+-------------------------------+ +--------------+
| | public/ |
v | - routeMap |
+-------------------+ | - routes/*. |
| CacheManager L1 | +--------------+
| - In-Memory LRU | |
| - Request Coalesce| |
| - Edge Cache HTTP | |
+---------+---------+ |
| |
+-------------------+--------------------+
|
v
+-------------------------------------------------------------------------+
| CLIENT APPLICATION |
| |
| Timetable View: |
| - Instant schedule lookup from static JSON and local cache |
| - Zero telemetry network calls or background tile requests |
| |
| Real-Time Map View: |
| - MapLibre GL mounted dynamically with hardware acceleration |
| - BusLocationStore fetches /api/bus/[routeId] with 2-second caching |
| - Background tab detection suspends polling when window is hidden |
| - Smooth 3-second coordinate interpolation along route polylines |
+-------------------------------------------------------------------------+
| Layer | Technologies | Details |
|---|---|---|
| Framework | Next.js 16 (App Router, Turbopack) | Server Components, dynamic route handlers, fast refresh |
| User Interface | React 19, Lucide React, Next Themes | Strict concurrent mode, modern hooks, dark mode support |
| Language | TypeScript 6 (Strict Mode) | Full type safety across API handlers, models, and UI |
| Styling | Tailwind CSS 4, Modern CSS Directives | Variable-driven theme system, zero-runtime overhead |
| Map Rendering | MapLibre GL 6.7 via react-map-gl |
WebGL hardware-accelerated vector tile rendering |
| Telemetry & Sync | Custom BusLocationStore, SWR |
Micro-cached polling, tab visibility suspension |
| Data Ingestion | Node.js Fetch, OSRM MLD Routing | TAGO REST integration, Cheerio ITS scraping |
| Testing | Vitest 5 | High-speed unit tests covering utils, geo, and cache |
| Deployment | Vercel Serverless & Edge Network | Global CDN caching headers, edge request deduplication |
The application exposes a set of REST endpoints designed for low-latency responses, aggressive edge micro-caching, and automated error recovery:
| Method | Endpoint | Description | Cache Policy |
|---|---|---|---|
GET |
/api/bus |
Returns full city timetable data and metadata | s-maxage=60, SWR: 300s |
GET |
/api/bus/[routeId] |
Live bus GPS positions for the specified route ID | s-maxage=2, SWR: 3s |
GET |
/api/bus/health |
Connectivity check against national TAGO API | no-store |
POST |
/api/bus/refresh |
Triggers a fresh scrape of official Wonju ITS schedules | no-store |
GET |
/api/bus-arrival/[busStopId] |
Predicted arrival times for buses arriving at a stop | s-maxage=5, SWR: 10s |
GET |
/api/bus-stops/[routeId] |
Ordered sequence of bus stops for a specific route | s-maxage=86400 |
GET |
/api/route-stops/[routeName] |
Ordered directional stops mapped by route name | s-maxage=86400 |
GET |
/api/schedule |
Complete schedule JSON payload | s-maxage=60, SWR: 300s |
POST |
/api/schedule/refresh |
Force scrape and update local schedule cache | no-store |
GET |
/api/notice |
List of municipal transit notices from Wonju ITS | s-maxage=300 |
GET |
/api/notice/[id] |
Full detail of a specific ITS transit notice | s-maxage=600 |
GET |
/api/data/[...path] |
Serves static assets from local public/ directory |
public, max-age=3600 |
- Node.js: Version 20.x or higher
- Package Manager:
npm(version 10 or later) - Container Engine (Optional, for running local OSRM): Podman or Docker
Clone the repository and install project dependencies:
git clone https://github.com/kernelily/wBus.git
cd wBus
npm installCreate a local environment file by copying .env.example:
cp .env.example .env.localPopulate the required environment variables in .env.local:
# Public Data Portal (MOLIT Bus Location and Route Information API)
DATA_GO_KR_SERVICE_KEY="YOUR_DECODED_OR_ENCODED_PUBLIC_DATA_KEY"
# Static Data Remote Loading Switch (default: false, loads from public/)
NEXT_PUBLIC_USE_REMOTE_STATIC_DATA="false"
NEXT_PUBLIC_STATIC_API_URL=""
# Local OSRM Routing Engine URL (used during polyline generation pipeline)
OSRM_API_URL="http://localhost:4000/route/v1/driving"Before running the application for the first time, generate the necessary route geometries and schedule datasets.
Scrapes the latest bus departure times, interval details, and holiday adjustments:
node scripts/fetch-schedule.mjsThe output is verified and saved to public/data/schedule.json.
Connects to the public data portal and your OSRM routing container to construct high-precision route vector GeoJSON files:
node scripts/generate-polylines.mjsNote
For instructions on setting up the local OSRM routing server on ARM64 or x86_64, refer to the OSRM Setup Guide.
Start the local Next.js server with Turbopack acceleration:
npm run devOpen http://localhost:3000 in your browser to inspect the application.
| Command | Purpose |
|---|---|
npm run dev |
Starts the development server using Next.js Turbopack |
npm run build |
Compiles the production application bundle |
npm run start |
Launches the compiled production application |
npm run lint |
Runs ESLint across all source and script files |
npm run lint:fix |
Automatically resolves fixable ESLint warnings and errors |
npm run typecheck |
Executes TypeScript compiler checks without emitting output |
npm run test |
Runs unit test suites using Vitest |
The codebase includes automated unit test suites covering coordinate mathematics, cache invalidation, and concurrency control.
Run the test suite:
npm run testExecute type and lint checks:
npm run typecheck
npm run lintThis project is licensed under the terms of the MIT License.