A responsive real‑time bus information web service built with Next.js (App Router), Material UI v6, and Google Maps JavaScript API.
Shows nearby bus stops, live arrival times, and a visual route line for each bus, with Light / Dark / Night themes and a dashboard-style UI.
-
Interactive map (Google Maps)
- Default center is the user’s current browser location (via
navigator.geolocation), with a “My location” button to re-center. - Users can drag the map; when dragging stops, the app fetches nearby bus stops around the new center.
- Custom bus stop markers (red pins), with a distinct icon for the selected stop.
- Default center is the user’s current browser location (via
-
Nearby bus stops
- Calls the Seoul Bus Station by Position API with current coordinates.
- Displays the selected stop name, direction, and approximate distance (meters).
- Automatically picks the nearest stop as the initial selection.
-
Realtime arrivals
- For the selected stop, calls the Seoul Bus Arrivals by Station API.
- Lists imminent buses with:
- Route number
- Destination / direction
- Raw arrival message (e.g., “2분후[3번째 전]”)
- Route number chips are color coded by route type:
- Blue – Trunk (간선, type 3)
- Green – Branch (지선, type 4)
- Yellow – Circular (순환, type 5)
- Red – Express / Metropolitan (광역, type 6)
- Clicking a route chip draws the bus route path as a polyline on the map.
-
Route visualization
- Uses a dedicated
/api/route-pathendpoint that wraps Seoul’s route-path API. - Draws a single red polyline for the currently selected route.
- When a new route is selected, the previous polyline is removed from the map so only one route is visible at a time.
- Uses a dedicated
-
Theme modes
- Light / Dark / Night themes with Night as the default.
- Consistent palette for map container + right-hand side panel.
- Theme toggle in the top-right app bar.
-
Responsive layout
- On desktop: map on the left, realtime arrivals on the right.
- On small screens: panels stack vertically (map on top, arrivals below).
- Scrollable arrivals list with preserved header and GPS status indicator.
- Next.js 15 (App Router), React 18
- Material UI v6 (
@mui/material,@mui/icons-material,@emotion/*) - @react-google-maps/api
- TypeScript
- Local dev (default): http://localhost:3000
- Production: https://seoul-bus-arrivals.vercel.app
Create .env.local at the repo root and add the following keys:
# Google Maps JavaScript API
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=YOUR_GOOGLE_MAPS_JS_API_KEY
# Seoul Bus Open API (Decoding service key)
SEOUL_BUS_API_KEY=YOUR_SEOUL_BUS_DECODING_KEYImportant
- For the Seoul Bus API, use the Decoding (원본) service key in
.env.local; the app handles URL-encoding internally.- The Google Maps key must have Maps JavaScript API enabled.
This project integrates with the Seoul Bus Information System (BIS) open APIs:
- Station by Position –
getStationByPos- Used by
/api/stationsto find stops near a given latitude/longitude. - The front-end passes user coordinates; the API returns a list of stops (arsId, stId, name, distance, GPS coordinates).
- Used by
- Arrivals by Station –
getArrInfoByStation- Used by
/api/arrivalsto fetch arrivals for the selected stop (arsId). - The app parses
arrmsg1 / arrmsg2, route name, and destination (adirection).
- Used by
- Route Path – (a route-path endpoint, e.g.,
getRoutePath)- Wrapped by
/api/route-pathto get polylines for a givenbusRouteId. - The resulting array of
{ lat, lng }points is drawn as the red route line.
- Wrapped by
app/
layout.tsx # Root layout, theme registry, metadata
page.tsx # Main dashboard: map + realtime arrivals
api/
stations/route.ts # GET /api/stations → nearby bus stops
arrivals/route.ts # GET /api/arrivals → realtime arrivals
route-path/route.ts # GET /api/route-path → bus route polyline
components/
ThemeRegistry.tsx # MUI theme provider + palette (Light/Dark/Night)
ThemeToggle.tsx # App bar toggle for theme modes
MapPanel.tsx # Core UI: GoogleMap, markers, arrivals panel
types.ts # Shared TypeScript types (BusStop, BusArrival, etc.)
public/
favicon.ico # App favicon (bus icon on dark background)
Wraps Seoul getStationByPos and returns normalized stop data for the front-end.
| Name | Type | Required | Example | Description |
|---|---|---|---|---|
lat |
number |
✅ | 37.4979 |
Latitude (WGS84) of current map center |
lng |
number |
✅ | 127.0276 |
Longitude (WGS84) of current map center |
{
"stops": [
{
"arsId": "22339",
"stationId": "112000123",
"name": "Gangnam Station",
"direction": "Yeoksam Station",
"distanceMeters": 79,
"lat": 37.497942,
"lng": 127.027621
}
]
}If the open API returns an error (e.g., header code not 0), the route responds with stops: [] and an error message, so the UI can fail gracefully.
Wraps Seoul getArrInfoByStation and returns arrivals for a specific stop.
| Name | Type | Required | Example | Description |
|---|---|---|---|---|
arsId |
string |
✅ | 22339 |
Stop unique number (정류소 고유번호) |
{
"arrivals": [
{
"routeId": "3411",
"routeName": "3411",
"routeType": 4,
"destName": "Hanam Station",
"predictMsg": "5분후[3번째 전]"
}
]
}The front-end:
- Uses
routeTypeto choose the chip color. - Sorts arrivals roughly by “soonest” using the first integer found in the message.
Returns path coordinates for a specific bus route so the map can draw a polyline.
| Name | Type | Required | Example | Description |
|---|---|---|---|---|
routeId |
string |
✅ | 3411 |
Seoul bus route ID |
{
"path": [
{ "lat": 37.49794, "lng": 127.02762 },
{ "lat": 37.50123, "lng": 127.03011 }
]
}On the client side, the path is stored in state and mirrored into a single google.maps.Polyline instance.
Whenever the user clicks a different route:
- The current polyline is removed with
setMap(null). - A new polyline is created and attached to the map.
This ensures there is never more than one route line displayed.
- Right-hand panel shows:
- GPS status chip (GPS OK / OFF / 오류 / 위치 확인 중…).
- Selected stop name + direction + distance.
- Scrollable Realtime arrivals list.
- Clicking a bus stop marker:
- Selects that stop, clears any existing route polyline, and loads arrivals.
- Clicking a route chip:
- Highlights the chip and draws the route line in red.
- When geolocation is unavailable or denied:
- GPS status switches to OFF or 오류, and the app falls back to a default center (Gangnam).
- All heavy asynchronous calls (stations, arrivals, route-path) show spinners and user-friendly error messages.
- Base layout using Next.js App Router and MUI with Night / Dark / Light theme support.
- Google Maps integration with custom red bus stop markers and selected-stop variant.
- Geolocation support: app centers on current browser location with a “My location” button.
- Nearby bus stops loader that reacts both to initial position and subsequent map drags.
- Realtime arrivals panel pulling live data from Seoul’s
getArrInfoByStationAPI. - Bus type color chips for Trunk / Branch / Circular / Express routes.
- Route polyline visualization, ensuring only the currently selected route is drawn.
- Added a custom favicon matching the Seoul Bus Arrivals theme.
# install dependencies
npm -i
# start dev server (http://localhost:3000)
next dev
# type check & build production bundle
next buildInternal demo for personal / internal projects.
Replace this section with your organization’s license terms if needed.