Mini Map Macau 🚈🚌✈️🛥️
mini-map-macau.app
3D visualization of Macau's public transit, ferry, and aviation system, inspired by Mini Tokyo 3D and Mini Taiwan.
Visualizes the Macau Light Rapid Transit (LRT), bus network, HK–Macau ferry routes, and MFM airport flights on an interactive 3D map. Vehicles move along actual geometry in a timetable-driven simulation. A CITY layer set adds Macau's open data on top: DSAT road-works notices, every school's buildings coloured by level and shaded by founding era, the Housing Bureau's social and economic housing estates, the seven civil parishes as a boundary tint, IAM public toilets, the territory's temples, churches, mosque and Earth God (土地公) street shrines, IAM's walking trails and cycle tracks, DSAT public car parks with live vacancy, and IAM/DSPA refuse rooms, compacting bins and recycling points.
How fresh is this? See Data freshness & update strategy for a per-layer breakdown — LRT and buses run on simulated, manually regenerated timetables, while flights and ferries refresh on their own daily/monthly sync schedule.
!Demo — Macau bus fleet on the roundabout
!Demo — LRT line with timetable panel
Full-quality MP4s: bus fleet · LRT line
Contents
- Features
- Architecture
- Tech Stack
- Getting Started
- Data Pipeline
- Data Sources
- Data freshness & update strategy
- Project Structure
- Performance Notes
- Acknowledgements
- License
- Developer Docs 開發筆記 (繁中)
Features
- 3D LRT vehicles — 3 lines, 15 stations, real track geometry and elevated viaducts
- 3D Bus fleet — 91 routes, road-snapped via OSRM, with accurate cross-harbour bridge geometry
- 3D Aircraft — about 160 real MFM flights (auto-synced daily) with detailed airplane models, apron stands, and taxi paths
- 3D Ferries — 6 HK/Shenzhen ↔ Macau sea routes (TurboJET + CotaiJet) with a twin-hull catamaran shape, raised bridge, and multi-deck cabin
- Timetable-driven simulation — Schedule-synced playback with ETAs, service status, and trilingual labels (EN / 繁中 / PT)
- Time controls — Play/pause, 1×–60× speed, jump-to-now, free date/time picker
- Vehicle tracking — Click-to-follow with smooth camera and free zoom/pan
Transit & simulation
- 3D LRT vehicles — All 3 lines (Taipa, Seac Pai Van, Hengqin) with 15 stations, rendered as 3D models with real track geometry and elevated viaducts
- 3D Bus fleet — 91 routes with road-snapped paths via OSRM, including accurate bridge geometry (Macau–Taipa bridges)
- 3D Aircraft — about 160 real MFM airport flights (auto-synced daily) with detailed airplane models (fuselage, swept wings, vertical tail in airline colors, engine nacelles, window rows, cockpit windshield); aircraft park at 12 apron stands before departure and taxi along waypoint paths before takeoff
- Landing & holding patterns — Aircraft approach from North or South with multi-waypoint landing routes; when the runway is occupied, arriving flights enter a realistic circular holding pattern above the airport and smoothly transition back to the landing route when clear
- 3D Ferries — 6 sea routes (Hong Kong Outer Harbour / Taipa / Sheung Wan, HKIA, Shenzhen Airport, Shekou) served by TurboJET and CotaiJet, rendered as a twin-hull catamaran model (flared hulls, raised bridge with wraparound windscreen, cabin, windows, roof equipment) following waypoint routes between berth and open water, with berth-facing headings while docked
- Real-time simulation — Vehicles move along routes based on timetables, service frequencies, and schedule types (Mon–Thu / Friday / Sat–Sun)
- ETA & vehicle info — Click any vehicle or station to see live ETAs, next arrivals, route details, and service status
- Flight info — Click any aircraft to see flight number, airline, destination/origin (with localized names), scheduled time, aircraft type, and live/sim status
- Ferry info — Click any ferry to see operator, route, origin/destination port (localized), scheduled departure, crossing time, and live progress
- Time controls — Play, pause (spacebar), speed up (1×–60×), jump to current time, or pick any date/time with the DateTimePicker; Esc toggles the sidebar menu
- Vehicle tracking — Click a vehicle to follow it with smooth camera animation; freely zoom/pan while tracking
- Route visibility — Toggle individual bus routes by group (Peninsula, Cross-Harbour, Taipa/Cotai, Night, Special); auto-mode shows only routes currently in service
- Automated flight data — GitHub Actions workflow syncs MFM flight schedules from the AviationStack API daily
- Automated ferry data — GitHub Actions workflow scrapes TurboJET and CotaiJet timetables monthly and commits updated schedules if changed
City layers
- Parishes — Macau's seven civil parishes (花地瑪堂區 / 花王堂區 / 望德堂區 / 大堂區 / 風順堂區 on the peninsula, 嘉模堂區 on Taipa, 聖方濟各堂區 on Coloane) plus the Cotai reclamation zone, which belongs to no parish, drawn as a faint tint with a thin boundary and the area's name in the reading language (the name steps out above zoom 15.5, where a parish label across a street block is noise). Boundaries from OpenStreetMap, land area and the 2021 census population from DSEC. The tint draws above historical maps and below buildings and city/transport overlays, so it can be combined with any of them. Clicking a school block inside a parish opens the school; click empty ground inside an area for its name in all three languages, kind, island, area, population with the census year, and density.
- Road-works notices — DSAT traffic-diversion notices shown on the map for the simulated date, toggleable
- School buildings — Every school and tertiary campus rendered as coloured 3D blocks. Colour carries two facts: the hue is the teaching level (kindergarten fuchsia / primary crimson / secondary blue / university leaf green / all-through gold — a hue set chosen to stay clear of the public-housing families, since the two overlays can be read together), and the shade is the era the school was founded, from before 1900 (darkest) to 2000 onwards (lightest); a school with no known year takes the middle shade. Founding years are hand-transcribed by the pipeline from DSEDJ school profiles, the schools' own sites and zh.wikipedia. The legend section collapses, each level can be switched on/off on its own and carries its five-stop ramp; click a block for the school's name, level, system, founding year and approved stages
- Public housing — The Housing Bureau's social (社會房屋) and economic (經濟房屋) housing estates as 3D footprints, plus the government's elderly apartments (長者公寓) and the urban-renewal replacement (置換房) and temporary (暫住房) housing as a third colour — sandwich-class (夾心房屋) housing belongs to the same group but has no estate in the data yet. Coloured by type and shaded by the decade each block was first occupied; the legend section collapses and each type can be switched on/off on its own; click a block for the estate's name, type, category, district, address, occupation year, units, storeys and per-block dates. Footprints from OpenStreetMap. Can be combined with schools and any other city or transport layer.
- Public toilets — IAM public toilets as map markers with opening hours, barrier-free / family cubicles and temporary closures; toggleable
- Religion — every place of worship with a known location, in five toggleable groups: Tou Tei (土地公) — the Earth God's temples (土地廟 / 福德祠) and street shrines (土地神壇), merged from OpenStreetMap, the six sites the Cultural Affairs Bureau (IC) has classified as immovable heritage (with the IC's own trilingual name and description) and the sites photographed for Macau Memory's 社區守護神 exhibition (street-level positions, drawn dimmer and flagged approximate); the other Chinese temples and folk altars (媽閣廟, 蓮峯廟, 普濟禪院, 哪咤廟, 北帝廟, 譚公廟 …); churches and chapels from the Ruins of St. Paul's to the Anglican and Baptist churches; the mosque; and other faiths. Colour is the group, the marker's shape the building type (temple roof, cross, crescent, tablet); the classified sites carry the IC's description. Click a marker for its name (the inscription, in English or Portuguese only where a source publishes one), type, address, heritage code and the sources it came from. For Tou Tei the IC counts about 10 temples and 160+ public shrines; the layer maps the ones with a known location and says so; each group can be switched on/off on its own
- Walking trails — IAM's 16 walking trails and 2 cycle tracks (步行徑 / 單車徑) drawn along the government GIS lines, with the trail entrances, the 150 numbered distance posts (標距柱, codes such as 2-01-06 that IAM's closure notices use) and the 25 pavilions, plus seven summits (▲) the walking trails reach or pass, each labelled with its official height (疊石塘山 170.6 m, 大潭山 158.2 m …) and dotted climbs from the trail to each summit it only passes — 好漢坡 to Macau's highest point and OpenStreetMap footpaths up 路環中間, 東望洋山 and 小潭山 (where the mapped path stops about 38 m short, as the panel says). From zoom 19 a 3D hiker built like a toy-brick minifigure (hinged block legs, fixed-bend arms that rotate only at the shoulder, C-shaped hands, bush hat, backpack, a trekking pole through one hand, the other raised in a wave) stands at each entrance of an open walking trail, facing up the trail, and a red-torso minifigure taking a photo — the camera held up to the eye in one hand, as a real minifigure holds one — stands beside each summit a trail reaches (never on 小潭山, whose mapped path stops short), kept to the left of the summit's marker and label as the map turns. A trail whose published suspension covers the simulated date is drawn grey and dashed. Click a line or point for the trilingual name, the published and drawn length, entrance and exit, opening hours, the point clicked and a link to IAM's trail page; walking trails and cycle tracks can be switched on/off separately
- Public car parks — DSAT's 92 public car parks as map markers with entrances, height limits and fees, plus live vacancy shown only while the clock is at the present; toggleable
- Waste & recycling — IAM's refuse rooms, compacting bins, refuse stations and (via its own facility map) glass-bottle and clothing recycling banks, plus DSPA's smart recycling machines, three-colour recycling points, e-waste points and lamp/battery points — ≈1,157 collection points across nine types; DSPA's 10 Eco Fun drop-off stations; and a treatment-facilities layer covering the 澳門垃圾焚化中心 incineration plant, the hazardous-waste station, two landfills and the territory's 5 sewage treatment plants, all drawn as coloured 3D buildings or filled outlines — ≈1,176 marks across twelve toggleable key rows. The incinerator, the hazardous station, the construction-waste landfill and each sewage plant carry a monthly bar chart (tonnes received or cubic metres treated) from a small dedicated stats file, best-effort if DSPA's API is unreachable. Seven recycling-heavy rows start hidden so the ~300 collection/treatment points are not buried; switch them on from the key. Can be combined with every other layer.
- Water supply facilities — Macao Water's 22 plants, reservoirs, elevated tanks and pumping stations, plus the government's own Hac Sa Reservoir; footprints coloured by type where OSM has them, markers for the rest flagged approximate, connected by a schematic pipe network drawn along the roads (from two raw-water inlets: the Ilha Verde border canal, and a schematic Lotus Bridge point for the raw water that arrives via Hengqin, flagged as such) and a Macau-only distribution network along every road. Every marker carries its step number in the supply chain, the mains carry direction arrows, and a bright pulse walks the whole chain in order — inlet, reservoir, raw-water pump, plant, pump, elevated tank, then outward through the streets — so the sequence reads, not just the direction. Can be combined with electricity, historical maps and transport.
- Electricity grid — CEM's power station, the incineration plant and 33 HV substations (220 / 110 / 66 kV) with a schematic grid drawn along the roads and the three Guangdong interconnection inlets. Same reading aids as the water layer: every marker carries its step in the supply chain (① sources — import points, power station, incinerator — then ② 220 kV, ③ 110 kV, ④ 66 kV, ⑤ the streets), the lines carry direction chevrons, and a pulse walks the grid in that order; independently toggleable alongside other layers
- Grand Prix circuit — the Guia Circuit of the Macau Grand Prix: the 6.2 km racing line stitched from OpenStreetMap's
Circuito da Guiarelation (cross-checked against the organiser's official lap length), the pit lane, direction chevrons, the nine officially named corners in race order (names quoted from the Macau Grand Prix Committee in all three languages; positions derived from the track geometry by a stated rule and flagged as schematic), and a single open-wheel car lapping on the simulation clock in the record time — braking for the hairpin and running out along the straight on a speed profile derived from the track's curvature, stretched so every lap takes exactly the record — with a fading wake behind it and its live speed beside it. Flies to the circuit when enabled (never below zoom 14.4), without switching off other layers. The clock and speed controls appear while any transport layer is enabled. Turning off all transport layers hides them and resets playback speed to 1×. - Layer panel — desktop and mobile panels separate transport from everyday city layers and thematic overlays. All layer switches are independent and persist in localStorage; enabling or disabling a layer preserves every other selection. Bus, LRT and air & sea each offer show all / hide all. City layers also offer Show only, an explicit one-shot action that clears other city and transport layers; users can then add any layer back. No earlier selection is restored automatically. Road works are on by default and the other city layers remain opt-in.
Historical maps
Old plans of the city georeferenced onto today's streets and drawn as a translucent ground layer under the buildings, which turn into a paper-toned, half-see-through massing model while a plate is up so that the old town engraved underneath stays readable. Local plates with zoomable detail use a tile pyramid, so they get sharper as the map is zoomed in — down to about half a metre per pixel on the 1889 survey — while a phone only ever loads the tiles in view.
The collection includes: de Guignes' Plan de la Ville de Macao (surveyed in 1792 after Manuel de Agote's plan, published in 1808 as plate 94 of the atlas to Voyages à Péking, Manille et l'île de France; the Getty Research Institute's public-domain scan on the Internet Archive, straightened and stitched across its fold) Benjamin Baker's A Plan of the City and Harbour of Macao (engraved in 1796 for Staunton's Authentic Account of the Macartney embassy, with soundings and seabed notes across the Inner Harbour and the Praia Grande; the Library of Congress sheet), António Heitor's Planta da Peninsula de Macau at 1:5,000 (the Public Works Department's survey, lithographed in Macau in 1889 with every street named and the public buildings in red; the Library of Congress sheet), and the Public Works Department's 1:10,000 manuscript Planta da Peninsula de Macau signed by Alcino António Sauvage in 1893 (west at the top, a legend of 33 sites; the Arquivo Histórico Ultramarino's CC BY-SA 4.0 scan), the Comissão de Cartografia's 1912 city plan at 1:10,000 and Alves / Pires' 1927 city and new harbour plan at 1:4,000 (BNP public-domain scans; the latter includes proposed works and is labelled accordingly), and the Chinese Aomen Shi quan tu at approximately 1:10,000 (the Library of Congress catalogue dates it circa 1953; MUST dates the depicted geography to 1938–1941, so the legend marks the year as approximate; watermark-free scan via Wikimedia Commons). The catalogue now contains fifteen sheets, also including Bellin’s 1749 French–Dutch plan of the walled city and Inner Harbour and the 1780s Hogg edition of a navigation sketch of Macao and the island channels (Harvard Map Collection public-domain scans via Wikimedia Commons, approximately positioned), the 1858 revision of Admiralty Chart 1290 (surveyed in 1804, first published in 1840; PD-UKGov scan via Wikimedia Commons), the separate 1912 Taipa and Coloane sheets from BNP’s public-domain Atlas de Macau, the 1963 geological sketch (traditional 2× enlargement and light sharpening), and the Direcção dos Serviços de Cartografia e Cadastro’s 1990 peninsula sheet at 1:5,000 and 1991 territory map at 1:20,000 (Library of Congress scans via Wikimedia Commons), registered on their own printed Macau grid rather than on landmarks. Full sheets retain their paper margins and insets; inset positions are part of the printed layout, not separate geographic overlays. The 1858 chart has approximate island relief and suits regional comparisons.
Local spline-registered plates are treated as pieces of cloth: a thin-plate spline pins it exactly at its control points — 4–24 landmarks per plate (churches, forts, the Barrier Gate, the Ilha Verde summit, the Guia lighthouse — every one checked on a contact sheet of the scan and on an affine leave-one-out test), plus, on the two 18th-century plans, some 80 points round the whole shoreline of the peninsula, taken about every 120 m from the coast of 1794 — the 1889 survey's shoreline where it was still the old one, and along the Inner Harbour reclaimed after 1863 the streets that the 1993 Geografia de Macau names as the old shore; the whole shoreline of those two plans then gets a second pass, a coast snap that walks their traced coast line onto that coast of 1794 through about a thousand pairs a plate (along the Inner Harbour, where the drawings show wharves and inlets and the reference is a generalised line, it is the general run of the waterfront that is matched), so the drawn coast and the reference coincide all round the peninsula; the 1889 sheet's own north-east shore is in turn pinned to the shoreline of the 1893 manuscript, the baseline chosen for that stretch (without a control point on the D. Maria II fort it still puts the fort within 14 m of where it stands) — and stretches or pinches the paper between them as needed, so both 18th-century surveys, which draw the peninsula shorter than it is, are pulled back onto today's streets at every pin, while the two 1890s surveys barely move; what lies between the pins is interpolated, and the sea and the Zhuhai shore beyond them are extrapolated decoration.
Thirteen rows, filed under 18th-, 19th- and 20th-century headings, cover the fifteen sheets: the three 1912 Atlas de Macau sheets (peninsula, Taipa and Coloane) share one row. One plate is drawn at a time — choosing a row replaces the previous one — under a single opacity slider. On phones an on-map stepper (‹ year title ›) moves to the previous or next plate without opening the panel; tapping its middle opens the list. Titles identify the author or distinguishing content, while the second line gives scale, coverage or publication context. Notes and sources open under the chosen row and preserve each original catalogue title and source credit, including all three atlas sheets
Interface
- 3D/2D toggle — Switch between perspective and top-down views
- Dark/Light mode — Two map styles (CARTO Dark Matter / Positron)
- Trilingual UI — English / 繁體中文 / Português — flight destinations, station names, and all labels switch with the language
- Cyberpunk-styled menu — Hamburger menu with Orbitron-font title and gradient branding
- Responsive mobile UI — Hamburger menu for map controls, a chip stack for LRT, Bus and a combined Air & Sea chip plus one CITY chip that opens a shared sheet listing all twelve city layers, with a detail view per layer, optimized touch layout with safe-area support, and Add to Home Screen (a web app manifest plus an install card that names the Share / menu route for iOS and Android browsers)
- Lazy loading — Code-split panels (VehicleInfoPanel, StationInfoPanel, FlightInfoPanel, FerryInfoPanel, RoadWorkInfoPanel, SchoolInfoPanel, PublicHousingInfoPanel, ParishInfoPanel, ToiletInfoPanel, ReligionInfoPanel, TrailInfoPanel, CarParkInfoPanel, WasteSiteInfoPanel, WasteIncineratorInfoPanel, WasteEcoStationInfoPanel, WasteFacilityInfoPanel, WaterFacilityInfoPanel, WaterInletInfoPanel, PowerFacilityInfoPanel, PowerInletInfoPanel, GrandPrixCircuitInfoPanel, GrandPrixCornerInfoPanel), plus the map component itself (MapView), for fast initial load
Architecture
Upstream sources are normalized into JSON, loaded through static assets and schedule-specific API requests, and replayed by the browser on a simulated clock. The DSAT car-park vacancy API provides live updates only while the clock sits at the present.
Animated SVG (SMIL, no scripts) — generated, see docs/architecture.svg.
Tech Stack
| Layer | Technology |
|-------|-----------|
| Frontend | React 19, TypeScript 6, Vite 8 |
| 3D Map | MapLibre GL JS 6 (WebGL 2 required), procedural instanced vehicle meshes + fill-extrusion building/overlay layers |
| Geo utilities | Turf.js (nearest-point-on-line) + custom precomputed-polyline cache |
| Styling | Tailwind CSS v4 |
| Fonts | Orbitron, JetBrains Mono, Noto Sans HK (Google Fonts) |
| Data pipeline | Python 3.13+, uv, OpenStreetMap Overpass API, OSRM |
| Flight data | AviationStack API (daily sync) |
| Ferry data | TurboJET + CotaiJet timetables (monthly web scraper) |
| City data | data.gov.mo — DSAT road works, DSAT car parks + live vacancy (daily syncs); IAM toilets, IAM/DSPA waste & recycling points, IAM walking trails (monthly syncs); DSEDJ school list, IH's public-housing lists, Macao Water's facility list and CEM's substation list, all + OSM footprints (manual); DSEC 2021 census + OSM boundaries for the parishes (manual); OpenStreetMap + the IC cultural-heritage API (+ Macau Memory's exhibition map for the Tou Tei shrines) for the temples, churches and shrines (manual) |
| Data validation | zod schemas at load time, mirrored by validate_output.py in CI |
| Deployment | Cloudflare Pages (via GitHub Actions) |
| Analytics | Google Analytics (gtag.js) |
Getting Started
Prerequisites
Install & Run
npm install
npm run dev
The app will be available at http://localhost:5173.
Build for Production
npm run build
npm run preview
Diagnosing a device
Phones have no console, so the app carries its own. Append ?debug=1 to any URL (or set localStorage mini-macau-debug to 1) and a panel pins to the bottom of the page (src/debugOverlay.ts): the browser's capabilities (the existing map WebGL 2 context and renderer, GL limits, a module-worker probe), every error and unhandled rejection, MapLibre error events with their source and tile, a heartbeat every 3 s (alive, canvas size, shaders and programs compiled, map renders, tile reloads with the four busiest sources named), and a pinned strip for the lines that decide a diagnosis — a failed shader with its info log and whether the context was lost. The previous page load's tail is kept in localStorage and replayed on the next load, so a page the OS killed still leaves a trace.
Switches for narrowing a failure down without a redeploy:
| URL | What it does |
|-----|--------------|
| ?debug=1&layers=none | Basemap only: none of the app's sources or layers |
| ?debug=1&nosim=1 | Never runs the simulation tick |
| ?debug=1&no3d=1 | Starts flat, buildings off |
| ?debug=1&maxdpr=2 | Caps the render pixel ratio |
| ?debug=1&nowebgl2=1 | Pretends the device has no WebGL 2: switches to the raster compatibility map |
| ?map=2d | Opens the compatibility map directly, using raster tiles and Canvas2D overlays |
On context loss or shader failure the app rebuilds the map once, retaining its camera. Repeated failure switches to a 2D compatibility map with routes, moving vehicle points, and every city layer — toilets, car parks, waste & recycling sites, water and power facilities with their schematic networks, schools, public housing, religion sites, walking trails, road works, the parish tint, the Grand Prix circuit and car, and historical-map plates — plus the existing time/layer controls. It does not require WebGL; 3D buildings and animated network flows are unavailable. Debugging observes the map's existing context without creating probe contexts, and reports null shader query results separately from false compile status. See recovery behavior and investigation.
public/gltest.html is a page with no app code at all: MapLibre 5.23 or 6.7 straight from a CDN (?v=5|6), the same CARTO basemap and camera, and a synthetic fill-extrusion + circle setData load (&veh=150&hz=30&circles=300; &veh=0&circles=0 for the basemap alone; &theme=light, &dpr=2, &buildings=1, &overscale=off). It separates "MapLibre on this device" from "our app". Both pages are noindex.
Data Pipeline
Transit geometry and other static datasets are pre-generated in public/data/. LRT movement is computed by the Pages Function. The browser loads fixed two-minute state windows through /api/lrt/state?at= and prefetches the next overlapping window for playback and seeking.
Regenerate transit data
cd data
Set up Python environment
uv sync
Run the extraction scripts (there is no single entry point — see below)
uv run python scripts/extract_lrt_osm.py
uv run python scripts/fetch_bus_data.py
uv run python scripts/fetch_dsat_stops.py
uv run python scripts/fetch_bridge_geometry.py
uv run python scripts/extract_bus_data.py
uv run python scripts/patch_bus_bridges.py
uv run python scripts/patch_service_hours.py
uv run python scripts/patch_service_hours_by_day.py
Then, from the repo root, run the Node post-processing chain:
npm run data:routes
This will:
- Extract LRT track geometry from OpenStreetMap (
railway=light_railways) - Fetch motransportinfo.com route/stop reference data, DSAT's authoritative per-direction stop lists and the bridge approach geometry into
data/bus_reference/ - Build bus routes and stops from that reference data, snapping each route to the roads via OSRM (
osrm_route.pyis the helper moduleextract_bus_data.pycalls) - Splice the custom bridge geometry into the cross-harbour routes
- Recompute bus service hours and frequency from the published (weekday / Saturday / Sunday) schedules
npm run data:routespatches the Amaral terminal layout, corrects routes against MO Transport's published GPX traces, and rebuilds the road-speed profile
data/bus_reference/; everything the app loads is written straight to where it is consumed, public/data/, and served as-is. There is no intermediate data/output/ copy to sync.
Flight data sync
Flight schedules are fetched from the AviationStack API and stored as a static JSON file:
cd data
Fetch today's MFM flights (requires API key)
AVIATIONSTACK_API_KEY=your_key uv run python scripts/fetch_flights.py
Fetch a specific date
AVIATIONSTACK_API_KEY=your_key uv run python scripts/fetch_flights.py 2026-04-19
The sync:
- Pulls arrivals and departures for MFM (IATA:
MFM) from the AviationStack flights endpoint - Filters by the target date's active schedule
- Validates aircraft type codes (ICAO format like A320, B738)
- Outputs
public/data/flights.jsonwith times in Macau local (UTC+8)
.github/workflows/update-flights.yml), which runs daily at 07:07 Macau time (UTC+8), plus up to 50 minutes of random delay, and commits updated flight data if changed.
A second workflow (.github/workflows/update-flights-timetable.yml) fetches a 7-day timetable window starting tomorrow into a separate public/data/flights-timetable.json, so browsing a future date with the DateTimePicker shows real scheduled flights beyond what the daily sync covers. The frontend merges both files in buildFlightIndex (src/hooks/useTransitData.ts); today's flights always come from the realtime flights.json.
Ferry schedule scraper
Ferry timetables are scraped from the operator sites and stored as a single static JSON file with 6 routes across two operators (TurboJET and CotaiJet):
cd data
Scrape the current month's schedules for all routes
uv run python scripts/fetch_ferry_schedules.py
The scraper:
- Pulls TurboJET schedules for Hong Kong (Outer Harbour), Hong Kong (Taipa), HKIA, Shenzhen Airport, and Shekou
- Pulls CotaiJet schedule for Hong Kong (Sheung Wan) ↔ Macau Taipa
- Records
fetchedAtUtcandeffectiveAsmetadata so stale data is easy to spot - Outputs
public/data/ferry-schedules.json
.github/workflows/update-ferry-schedules.yml), which runs on the 1st of each month at 00:00 UTC (08:00 Macau) and commits updates if changed.
Data Sources
- LRT tracks & stations — OpenStreetMap (railway=light_rail relations)
- LRT timetables — MLM 澳門輕軌股份有限公司 official per-station timetable publications (Taipa / Seac Pai Van / Hengqin lines), transcribed and checked for timetable-driven playback
- Bus routes & stops — OpenStreetMap + motransportinfo.com curated stop data
- Road-snapped routes — OSRM with custom bridge approach geometry
- Bus timetables — Based on published DSAT service frequencies
- Flight schedules — AviationStack API (MFM arrivals + departures)
- Ferry schedules — TurboJET + CotaiJet official monthly timetables
City layers
- Parishes — OpenStreetMap administrative boundaries (the seven freguesias plus the Cotai reclamation zone) + DSEC 2021 census population and land area by parish (manual refresh)
- Road-works notices — DSAT via data.gov.mo (daily)
- School buildings — DSEDJ school list on data.gov.mo + OpenStreetMap building footprints (manual refresh); founding years hand-transcribed from DSEDJ school profiles, the schools' own sites and zh.wikipedia
- Public housing — 房屋局 (IH) 社會房屋位置分佈 and 經濟房屋位置分佈 lists, plus the bureau's 長者公寓 / 置換房 / 暫住房 / 夾心房屋 pages for the other public-housing programmes, + OpenStreetMap building footprints (manual refresh)
- Public toilets — IAM via data.gov.mo (monthly)
- Walking trails — IAM via data.gov.mo 市政活動場地 (names, entrances, opening hours, temporary closure) and IAM's nature-site trail records (entrance points, suspension dates); lines, distance posts and pavilions from the government GIS layers
ThematicMap/Walk_Trail_laneandWalk_Trail_point(converted locally from the Macao Grid, EPSG:8433), cross-checked every run against OpenStreetMap paths, which also stand in for a line the GIS does not publish; summit heights from DSEC Environmental Statistics (table 2.3, checked against DSSCU's hills table each run), summit positions from DSSCU's geodetic control points, the 好漢坡 climb from OpenStreetMap (monthly) - Religion — OpenStreetMap places of worship (temples, churches, the mosque, the Tou Tei and other folk shrines: positions and names), the IC 文化遺產資料 API via data.gov.mo (every classified temple and church: official GPS and trilingual descriptions, joined to the OSM buildings), and — for the Tou Tei shrines only — Macau Memory's 社區守護神 exhibition map (names, street addresses and record ids only — its positions are Google geocodes of a street name, flagged approximate; no photos or text are copied) (manual refresh)
- Public car parks — DSAT via data.gov.mo (daily) + live vacancy (live, polled by the browser)
- Waste & recycling — IAM 垃圾房 (refuse rooms) + 壓縮式垃圾收集點 (compactors) via data.gov.mo ZIP download, and 全澳垃圾收集設施的資訊列表 via the API gateway (垃圾站 refuse stations only — the feed's other two site types duplicate the ZIP downloads above) (monthly); DSPA 智能回收機 (smart recycling machines), 三色資源回收點 (three-colour recycling), 電腦及通訊設備回收點 (e-waste), and 光管 + 電池回收點 (lamp/battery, merged — identical site lists) via the data.gov.mo API gateway (monthly); DSPA 垃圾焚化中心 monthly statistics (monthly, best-effort — the panel just omits the stats block if the call fails) — eight datasets plus IAM's 環境資訊網 facility map JSON (
facility_c.json, not on data.gov.mo: the 5 玻璃樽公共回收點 glass-bottle and 16 全澳衣物公共回收點 clothing recycling points), ≈1,157 sites total; plus 10 hand-placed DSPA 環保加Fun站 Eco Fun stations, the hand-placed 特殊和危險廢物處理站 hazardous-waste station, and two OpenStreetMap landfill outlines (ways 552848944, 552740242) — the incineration plant's own buildings come from OpenStreetMap throughpower-facilities.jsonalready, no extra dataset. Monthly throughput for four of those — the incinerator, the hazardous-waste station, the construction-waste landfill and the territory's five sewage plants — lands in a separatedspa-stats.json: the incinerator dataset above, plus DSPA's 澳門半島污水處理廠, 氹仔污水處理廠, 路環污水處理廠 and 澳門跨境工業區污水處理站 monthly figures via the API gateway (monthly); the hazardous station's, the landfill's and the airport plant's monthly figures are published only on DSPA's GIS pages, not on data.gov.mo — every series is best-effort,nullin the file (and hidden in the panel) when a call fails - Water supply facilities — Macao Water 供水設施 (the list of 22) + OpenStreetMap footprints, plus 黑沙水庫 Hac Sa Reservoir from OpenStreetMap (a DSAMA government reservoir, not a Macao Water facility); the figures in the panel come from Macao Water's 供澳原水 and 統計數據 pages, re-read on every run (twice a year)
- Electricity grid — CEM 澳電 營運 (the substation list in Chinese and English, the year's generation/import figures and the Guangdong interconnection history, re-read on every run) + OpenStreetMap footprints; the 220/110/66 kV lines between them are our schematic, not CEM's cable routes, which are underground and unmapped (twice a year)
- Grand Prix circuit — OpenStreetMap relation 8877949 Circuito da Guia (the racing line and the pit lane) + the Macau Grand Prix Committee's circuit page (the corner names in three languages, the 6.2 km lap length and the 7 m minimum width); the lap record the car runs at is Wikipedia's figure, flagged as a secondary source in the panel; corner coordinates are ours, derived from the track geometry by a rule the file records (manual refresh)
Historical maps
- 1749, 1780s and 1858 charts; 1912 Taipa and Coloane sheets — Bellin 1749 and the Hogg edition of the 1780s Sketch of the Typa and Macao: Harvard Map Collection scans via Wikimedia Commons (Bellin, Hogg; catalogue records 990125229050203941 and 990125230640203941), public domain, courtesy of Harvard Map Collection, Harvard Library. Admiralty Chart 1290 (surveyed 1804, published 1840, corrected to 1858): the Barry Lawrence Ruderman Antique Maps scan via Wikimedia Commons (PD-UKGov). Taipa and Coloane, 1912: pages 5 and 7 of the BNP Atlas de Macau (Public Domain Mark 1.0). The Hogg chart's islands and parts of the 1912 Taipa and Coloane shores are fitted to DSSCU's 1912 land-extent layer (for the 1780s chart, a later outline used as a proxy)
- Peninsula plates, 1792 – c. 1953 — de Guignes 1792/1808: the Getty Research Institute's copy of the atlas on the Internet Archive (public domain; leaves 141–142 straightened, stitched and georeferenced by
scripts/build-old-maps.mjs), with the BnF catalogue record and the MUST Library's 全球地圖中的澳門 entry as references; Baker 1796: the Library of Congress sheet (Geography and Map Division, no known restrictions) via its Wikimedia Commons mirror, with the MUST Library's entry as reference; Heitor 1889: the Library of Congress sheet (no known restrictions) via its Wikimedia Commons mirror, with the MUST Library's entry as reference; Sauvage 1893: the Arquivo Histórico Ultramarino's DigitArq record PT/AHU/CARTM/062/01433 (digitised under the PRR, CC BY-SA 4.0 — the georeferenced plate carries the same licence), with the MUST Library's entry as reference; Comissão de Cartografia 1912: the BNP Atlas de Macau, peninsula sheet at 1:10,000 (viewer page 3, 4606×6018 scan); Alves / Pires 1927: the BNP city and new harbour plan, 1:4,000 (12484×16318 scan, includes proposed harbour and reclamation works). BNP marks both digitised works Public Domain Mark 1.0; their native IIIF regions are assembled without resizing, the complete sheets, including paper margins, inset maps and the 1927 panorama, are retained in the overlay. Insets keep their original page layout rather than matching the modern basemap beneath them; Aomen Shi quan tu, circa 1953: the Library of Congress sheet (free to use and reuse; credit Library of Congress, Geography and Map Division) via its Wikimedia Commons mirror, with the MUST Library’s entry documenting the alternative 1938–1941 dating; the 1921 Leitão 1:20,000 plan from Tellurologie et Climatologie Médicales de Macao has no accessible scan (the NLA holds it on paper, HathiTrust is search-only outside the US) and is not included — the MUST scans themselves are watermarked and all rights reserved, so they are not used (manual refresh) - 1963 geological sketch — M. J. Lemos de Sousa’s 1:25,000 map covers the peninsula, Taipa and Coloane. The 1000×1386 scan on Mindat, uploaded by Rui Nunes, is marked public domain by the provider; the underlying publication rights have not been independently established. A single affine registration preserves the sheet’s relative shapes using six approximate reservoir and hilltop matches. The scan is enlarged to 2000×2772 with conventional 2× resampling and gentle sharpening, without generating text or geographic features. Small labels remain limited by the source resolution; the overlay is intended for territory-scale comparison, not precise measurement. Imagery is separate from the repository’s MIT code licence.
- 1990 / 1991 survey sheets — The Direcção dos Serviços de Cartografia e Cadastro’s Macau 1:5,000 (1990) and Território de Macau 1:20,000 (1991), from the Library of Congress Geography and Map Division (92684644, 92684636) via Wikimedia Commons, listed with no known copyright restrictions. Both are registered on their printed Macau grid: grid crosses read on the scan, converted from Macao Grid (EPSG:8433) to WGS 84 with EPSG:8438, one affine fit per sheet (
scripts/old-maps-dscc.mjs).
Everything under /data/*.json is fetchable as-is but served with X-Robots-Tag: noindex, nofollow (public/_headers) so the raw files stay out of search results. It is a header rather than a robots.txt Disallow on purpose: a crawler that is disallowed never sees the noindex, and a disallowed URL can still be listed bare when something links to it.
Data freshness & update strategy
Not every layer is equally fresh. LRT and buses are fully simulated from published timetables; flights and ferries are static syncs on their own schedule. None of the transit layers below touch a live feed.
| Layer | Mode | Source | Refresh cadence | Staleness indicator |
|-------|------|--------|-----------------|---------------------|
| LRT | Simulated | OSM geometry + MLM published per-station timetable | Manual updates following official publications | Schedule-specific API; no live vehicle feed |
| Bus | Simulated | OSM geometry + DSAT published service frequencies, dimmed by a daily service-status scrape | Manual regen (routes) · daily (service-status.yml) | DSAT stop snapshot timestamp in data/bus_reference/dsat_stops.json (current: 2026-09-02 Macau) |
| Flights | Static daily sync | AviationStack API | Daily around 07:07–07:57 Macau time — update-flights.yml; plus a 7-day-ahead timetable — update-flights-timetable.yml | fetchedAtUtc embedded in flights.json / flights-timetable.json |
| Ferries | Static monthly sync | TurboJET + CotaiJet timetable pages (scraped) | 1st of month · update-ferry-schedules.yml | fetchedAtUtc + effectiveAs in ferry-schedules.json |
What each mode means
- Simulated — Vehicles are placed on pre-generated polylines and moved by the client clock using the published timetable. They don't reflect any single bus or train's actual position at that moment; they show "what the schedule says should be moving through this segment right now."
- **Static