### Run Project with Node.js
Source: https://github.com/jermen592/hk-dashboard/blob/master/README.md
Use the serve package to host the project locally.
```bash
npx serve .
```
--------------------------------
### Fetch Parking Vacancy and Information
Source: https://context7.com/jermen592/hk-dashboard/llms.txt
Integrates parking vacancy and static information APIs. Use window.Parking.refresh() to load data and window.Parking.applyFilter() to apply UI filters.
```javascript
// data.gov.hk 停車場 API
const API_VACANCY = 'https://api.data.gov.hk/v1/carpark-info-vacancy?data=vacancy&lang=zh_TW';
const API_INFO = 'https://api.data.gov.hk/v1/carpark-info-vacancy?data=info&lang=zh_TW';
// 同時獲取空位及停車場資料
const [vacRes, infoRes] = await Promise.all([
fetch(API_VACANCY),
fetch(API_INFO)
]);
const vacData = await vacRes.json();
const infoData = await infoRes.json();
// 空位數據結構
// {
// results: [{
// park_Id: 'tdc-cwb-cps-00001',
// privateCar: [{ vacancy: 45, lastupdate: '2025-04-04T17:30:00' }],
// motorCycle: [{ vacancy: 10 }],
// LGV: [{ vacancy: 5 }]
// }]
// }
// 停車場資料結構
// {
// results: [{
// park_Id: 'tdc-cwb-cps-00001',
// name: '銅鑼灣中心停車場',
// district: 'WC', // 地區代碼
// latitude: '22.2775',
// longitude: '114.1836',
// displayAddress: '銅鑼灣道XX號'
// }]
// }
// 空位顏色標示
function vacancyTag(vac) {
if (vac === null) return '—';
if (vac === 0) return '滿';
if (vac <= 10) return '' + vac + '';
if (vac <= 50) return '' + vac + '';
return '' + vac + '';
}
// 使用方式
window.Parking.refresh(); // 載入全部停車場
window.Parking.applyFilter(); // 應用篩選條件
```
--------------------------------
### Git Contribution Workflow
Source: https://github.com/jermen592/hk-dashboard/blob/master/README.md
Standard commands for contributing to the repository.
```bash
# Fork 這個 repo
# 建立你的 feature branch
git checkout -b feature/amazing-feature
# Commit 你的改動
git commit -m 'Add some amazing feature'
# Push 到 branch
git push origin feature/amazing-feature
# 開一個 Pull Request
```
--------------------------------
### Project Directory Structure
Source: https://github.com/jermen592/hk-dashboard/blob/master/README.md
Overview of the file organization for the dashboard application.
```text
hk-dashboard/
├── index.html # 主頁面(15個分頁)
├── manifest.json # PWA 設定
├── sw.js # Service Worker(離線緩存)
├── css/
│ ├── tokens.css # CSS 設計 tokens(顏色/字體/間距)
│ └── base.css # 基礎樣式 + 響應式
└── js/
├── core.js # 主題切換、時鐘、農曆、導航
├── weather.js # 天氣模組(HKO)
├── transport.js # 交通模組(MTR/LRT)
├── bus.js # 巴士 ETA(KMB/CTB/GMB)
├── tides.js # 潮汐 + 地震 + 天氣展望
├── health.js # 急症室等候時間
├── environment.js # AQHI 空氣質素
├── parking.js # 停車場空位
├── cctv.js # 道路快拍
├── ferry.js # NLB 嶼巴路線
├── holidays.js # 公眾假期 + 節氣
├── climate.js # 氣候數據
├── beach.js # 泳灘水質
├── map.js # 互動地圖(Leaflet.js)
├── finance.js # 恒指 + 匯率
├── waste.js # 回收資訊
└── app.js # 主入口(初始化 + 自動刷新)
```
--------------------------------
### 初始化互動地圖 (Leaflet.js)
Source: https://context7.com/jermen592/hk-dashboard/llms.txt
MapView 模組封裝了地圖初始化與圖層載入邏輯,支援根據主題切換底圖並透過 fetch 載入停車場數據。
```javascript
// 初始化地圖 (使用 Leaflet.js)
const MapView = (function() {
let _map = null;
function createMap(container) {
// 使用 CartoDB 深色/淺色地圖底圖
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
const darkTile = 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png';
const lightTile = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png';
_map = L.map(container, {
center: [22.3193, 114.1694], // 香港中心
zoom: 11
});
L.tileLayer(isDark ? darkTile : lightTile, {
attribution: '© OSM © CARTO',
maxZoom: 18
}).addTo(_map);
}
// 停車場圖層
async function loadParkingLayer() {
const [vacRes, infoRes] = await Promise.all([
fetch('https://api.data.gov.hk/v1/carpark-info-vacancy?data=vacancy&lang=zh_TW'),
fetch('https://api.data.gov.hk/v1/carpark-info-vacancy?data=info&lang=zh_TW')
]);
// 建立 markers
const markers = [];
vacData.results.forEach(vac => {
const info = infoMap[vac.park_Id];
const marker = L.circleMarker([info.latitude, info.longitude], {
radius: 8,
fillColor: getVacancyColor(vac.privateCar?.[0]?.vacancy),
fillOpacity: 0.85
});
marker.bindPopup(`${info.name}
空位: ${vac.privateCar?.[0]?.vacancy || '—'}`);
markers.push(marker);
});
L.layerGroup(markers).addTo(_map);
}
return { refresh, switchLayer };
})();
// 使用方式
window.MapView.refresh(); // 初始化/刷新地圖
window.MapView.switchLayer('parking'); // 切換至停車場圖層
window.MapView.switchLayer('aqhi'); // 切換至 AQHI 圖層
window.MapView.switchLayer('beach'); // 切換至泳灘圖層
window.MapView.switchLayer('aed'); // 切換至 AED 圖層
```
--------------------------------
### Fetch Tide and Earthquake Data
Source: https://context7.com/jermen592/hk-dashboard/llms.txt
Retrieves meteorological data from the Hong Kong Observatory. Supports station switching and earthquake monitoring.
```javascript
// 潮汐數據 (香港天文台)
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
// 獲取屯門測潮站數據
const tideUrl = `https://data.weather.gov.hk/weatherAPI/opendata/opendata.php?dataType=HHOT&station=TMW&year=${year}&month=${month}&rformat=json`;
const tideRes = await fetch(tideUrl);
const tideData = await tideRes.json();
// 返回數據結構
// {
// fields: ['MM', 'DD', '01', '02', ..., '24'], // 月、日、1-24時
// data: [
// ['04', '01', '1.06', '0.74', '0.58', ...], // 4月1日各小時潮汐高度(米)
// ['04', '02', '1.12', '0.81', '0.62', ...]
// ]
// }
// 地震數據
const eqUrl = 'https://data.weather.gov.hk/weatherAPI/opendata/earthquake.php?dataType=qem&lang=tc';
const eqRes = await fetch(eqUrl);
const eqData = await eqRes.json();
// 返回數據結構
// {
// latitude: '22.15',
// longitude: '114.20',
// mag: '3.2', // 黎克特制震級
// depth: '10', // 深度(公里)
// region: '香港東南面約50公里',
// ptime: '2025-04-04 15:23'
// }
// 香港有感地震
const feltUrl = 'https://data.weather.gov.hk/weatherAPI/opendata/earthquake.php?dataType=feltearthquake&lang=tc';
// 使用方式
window.Tides.refresh(); // 刷新所有數據
window.Tides.changeStation(); // 切換測潮站 (讀取 #tide-station 值)
```
--------------------------------
### 獲取巴士與小巴到站時間
Source: https://context7.com/jermen592/hk-dashboard/llms.txt
整合九巴、城巴及專線小巴 API,提供路線詳情、站點資訊及實時到站時間查詢。
```javascript
// KMB 九巴 API
const KMB_BASE = 'https://data.etabus.gov.hk/v1/transport/kmb';
// 1. 獲取路線資料
const routeRes = await fetch(`${KMB_BASE}/route/60X/outbound/1`);
const routeData = await routeRes.json();
// { data: { route: '60X', orig_tc: '屯門市中心', dest_tc: '旺角東站' } }
// 2. 獲取路線站點列表
const stopsRes = await fetch(`${KMB_BASE}/route-stop/60X/outbound/1`);
const stops = await stopsRes.json();
// { data: [{ route: '60X', stop: 'B3B0EF2BC688751D', seq: 1 }] }
// 3. 獲取站點名稱
const stopRes = await fetch(`${KMB_BASE}/stop/B3B0EF2BC688751D`);
const stopInfo = await stopRes.json();
// { data: { name_tc: '屯門市中心總站', lat: '22.3908', long: '114.0068' } }
// 4. 獲取到站時間
const etaRes = await fetch(`${KMB_BASE}/eta/B3B0EF2BC688751D/60X/1`);
const eta = await etaRes.json();
// {
// data: [
// { eta: '2025-04-04T17:35:00+08:00', dest_tc: '旺角東站', rmk_tc: '' },
// { eta: '2025-04-04T17:50:00+08:00', dest_tc: '旺角東站', rmk_tc: '' }
// ]
// }
// CTB 城巴 API
const CTB_BASE = 'https://rt.data.gov.hk/v2/transport/citybus';
const ctbEta = await fetch(`${CTB_BASE}/eta/CTB/001939/962X`);
const ctbData = await ctbEta.json();
// { data: [{ eta: '...', dest_tc: '銅鑼灣', rmk_tc: '' }] }
// GMB 專線小巴 API
const GMB_BASE = 'https://data.etagmb.gov.hk';
// 獲取新界區路線列表
const gmbRoutes = await fetch(`${GMB_BASE}/route/NT`);
// 獲取路線詳情
const gmbRoute = await fetch(`${GMB_BASE}/route/NT/403`);
// 獲取到站時間
const gmbEta = await fetch(`${GMB_BASE}/eta/route-stop/403/1`);
// 使用方式
window.Bus.refresh(); // 刷新預設常用站點
window.BusSearch.search(); // 搜尋路線 (讀取 #bus-search-input 值)
window.Bus_GMB.loadRoutes(); // 載入小巴路線
```
--------------------------------
### 自動刷新機制與離線偵測
Source: https://context7.com/jermen592/hk-dashboard/llms.txt
利用 setInterval 定時觸發模組刷新,並監聽瀏覽器在線狀態以自動恢復數據同步。
```javascript
// 初始化載入所有數據
async function loadAllData() {
await Promise.allSettled([
Weather.refresh(),
Transport.refresh(),
Health.refresh(),
Environment.refresh(),
Finance.refresh() // 恒生指數 + 匯率
]);
}
// 自動刷新間隔
function startAutoRefresh() {
// 天氣、醫療、環境 — 每 60 秒
setInterval(() => {
Weather.refresh();
Health.refresh();
Environment.refresh();
}, 60000);
// 交通 — 每 10 秒
setInterval(() => Transport.refresh(), 10000);
// 巴士預設站點 — 每 45 秒
setInterval(() => Bus.refresh(), 45000);
// 停車場 — 每 5 分鐘 (僅在停車場頁面)
setInterval(() => {
if (window._currentPage === 'parking') {
Parking.refresh();
}
}, 300000);
}
// 離線/在線偵測
window.addEventListener('offline', () => {
document.getElementById('offline-banner').style.display = 'block';
});
window.addEventListener('online', () => {
document.getElementById('offline-banner').style.display = 'none';
loadAllData(); // 重新載入數據
});
```
--------------------------------
### 獲取香港天文台天氣數據
Source: https://context7.com/jermen592/hk-dashboard/llms.txt
使用香港天文台 API 獲取實時天氣、九天天氣預報及警告摘要。數據以 JSON 格式返回,包含溫度、濕度、紫外線指數及預報資訊。
```javascript
// 獲取當前天氣數據
const WX_BASE = 'https://data.weather.gov.hk/weatherAPI/opendata/weather.php';
// 實時天氣 (rhrread)
const response = await fetch(`${WX_BASE}?dataType=rhrread&lang=tc`);
const data = await response.json();
// 返回數據結構
// {
// temperature: { data: [{ place: '香港天文台', value: 28, unit: 'C' }] },
// humidity: { data: [{ value: 75 }] },
// icon: [51], // 天氣圖標代碼
// uvindex: { data: [{ value: 6, desc: '高' }] },
// rainfall: { data: [{ place: '屯門', max: 0.5 }] },
// warningMessage: ['黃色暴雨警告現正生效']
// }
// 九天天氣預報 (fnd)
const forecastRes = await fetch(`${WX_BASE}?dataType=fnd&lang=tc`);
const forecast = await forecastRes.json();
// 返回數據結構
// {
// weatherForecast: [{
// forecastDate: '20250404',
// week: '星期五',
// forecastMaxtemp: { value: 29 },
// forecastMintemp: { value: 24 },
// ForecastIcon: 53,
// PSR: '中高' // 降雨概率
// }],
// seaTemp: { value: 23 },
// generalSituation: '一道低壓槽會在今明兩日為華南沿岸帶來驟雨...'
// }
// 天氣警告摘要 (warnsum)
const warnRes = await fetch(`${WX_BASE}?dataType=warnsum&lang=tc`);
const warnings = await warnRes.json();
// 返回: { WRAIN: { name: '暴雨警告', code: 'WAMB' }, WTCSGNL: { code: 'T8' } }
// 使用方式
window.Weather.refresh(); // 刷新所有天氣數據
```
--------------------------------
### 查詢港鐵及輕鐵班次
Source: https://context7.com/jermen592/hk-dashboard/llms.txt
通過港鐵 API 獲取指定路線與車站的實時班次資訊。輕鐵查詢需使用 station_id 參數。
```javascript
// MTR 港鐵班次 API
const MTR_API = 'https://rt.data.gov.hk/v1/transport/mtr/getSchedule.php';
// 查詢屯馬線屯門站班次
const response = await fetch(`${MTR_API}?line=TML&sta=TUM`);
const data = await response.json();
// 返回數據結構
// {
// status: 1, // 1=正常, 0=服務受阻
// data: {
// 'TML-TUM': {
// UP: [ // 上行列車
// { dest: 'WKS', ttnt: '3', plat: '1' }, // 往烏溪沙, 3分鐘後
// { dest: 'WKS', ttnt: '10', plat: '1' }
// ],
// DOWN: [] // 下行(屯門為總站無下行)
// }
// }
// }
// 輕鐵班次 API
const LRT_API = 'https://rt.data.gov.hk/v1/transport/mtr/lrt/getSchedule';
// 查詢屯門碼頭站 (站號 001)
const lrtRes = await fetch(`${LRT_API}?station_id=001`);
const lrtData = await lrtRes.json();
// 返回數據結構
// {
// platform_list: [{
// platform_id: '1',
// route_list: [
// { route_no: '507', dest_ch: '田景', time_ch: '3 分鐘' },
// { route_no: '610', dest_ch: '元朗', time_ch: '即將抵達' }
// ]
// }]
// }
// 使用方式
window.Transport.refresh(); // 刷新港鐵及輕鐵數據
window.fetchMTRCustom(); // 根據選擇的路線/車站查詢
window.fetchLRTCustom(); // 根據選擇的輕鐵站查詢
```
--------------------------------
### 頁面導航與主題切換
Source: https://context7.com/jermen592/hk-dashboard/llms.txt
透過 DOM 操作管理頁面顯示狀態,並支援透過 URL hash 進行深淺色主題切換。
```javascript
// 頁面導航
function showPage(name) {
// 隱藏所有頁面
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
// 顯示目標頁面
document.getElementById(`page-${name}`).classList.add('active');
// 更新導航高亮
document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active'));
document.querySelector(`.nav-tab[data-page="${name}"]`).classList.add('active');
// 追蹤當前頁面
window._currentPage = name;
}
// 可用頁面: home, weather, transport, bus, tides, health, environment,
// parking, beach, map, ferry, holidays, climate, cctv, waste
// 主題切換 (支援 URL hash: #dark / #light)
document.querySelector('[data-theme-toggle]').addEventListener('click', function() {
const current = document.documentElement.getAttribute('data-theme');
const next = current === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
history.replaceState(null, '', '#' + next);
});
// 農曆日期 API
async function loadLunarDate() {
const now = new Date();
const url = `https://data.weather.gov.hk/weatherAPI/opendata/lunardate.php?date=${now.toISOString().slice(0,10).replace(/-/g,'')}`;
const data = await fetch(url).then(r => r.json());
// { LunarYear: '乙巳年', LunarMonth: '三月', LunarDate: '初七' }
}
// 使用方式
window.showPage('weather'); // 切換至天氣頁面
```
--------------------------------
### Fetch Environmental Protection Department AQHI Forecast
Source: https://context7.com/jermen592/hk-dashboard/llms.txt
Retrieves air quality health index forecasts. The riskClass function maps risk strings to CSS classes.
```javascript
// 環保署 AQHI 預報
const AQHI_URL = 'https://datagovhk.blob.core.windows.net/dataset/aqhi/aqhi-forecast.json';
const response = await fetch(AQHI_URL);
const data = await response.json();
// 返回數據結構 (陣列)
// [
// {
// type: 'general', // 一般監測站
// date: '2025-04-04',
// time: 'AM',
// health_risk_min: 'Low',
// health_risk_max: 'Moderate',
// publish_date: '2025-04-04'
// },
// {
// type: 'roadside', // 路邊監測站
// date: '2025-04-04',
// time: 'PM',
// health_risk_min: 'Moderate',
// health_risk_max: 'High'
// }
// ]
// AQHI 等級顏色
function riskClass(str) {
const n = riskToNum(str); // Low=1, Moderate=5, High=7, Very High=8
if (n <= 3) return 'tag-green'; // 低
if (n <= 6) return 'tag-yellow'; // 中
return 'tag-red'; // 高/甚高/嚴重
}
// 使用方式
window.Environment.refresh();
```
--------------------------------
### Register Service Worker for PWA
Source: https://github.com/jermen592/hk-dashboard/blob/master/index.html
Registers a service worker for Progressive Web App (PWA) functionality. This code should be placed in your main JavaScript file or an entry point that runs on page load.
```javascript
// Register Service Worker (PWA) if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js').then(reg => {
console.log('Saving Service Worker registered:', reg.scope);
}).catch(err => {
console.log('Saving SW registration failed:', err);
});
});
}
```
--------------------------------
### Fetch Hospital Authority AED Wait Times
Source: https://context7.com/jermen592/hk-dashboard/llms.txt
Retrieves real-time emergency department wait times. Use window.Health.refresh() to update the data.
```javascript
// 醫院管理局急症室等候時間
const AED_URL = 'https://www.ha.org.hk/opendata/aed/aedwtdata2-tc.json';
const response = await fetch(AED_URL);
const data = await response.json();
// 返回數據結構
// {
// updateTime: '2025-04-04T17:30:00',
// waitTime: [
// {
// hospName: '屯門醫院',
// t3p50: '1 小時', // T3 甲類急症等候中位數
// t45p50: '3 小時', // T4/T5 乙丙類急症等候中位數
// t2wt: '30 分鐘' // T2 危急等候時間
// },
// { hospName: '瑪嘉烈醫院', t3p50: '45 分鐘', t45p50: '2 小時' }
// ]
// }
// 醫院地區映射
const HOSP_REGION = {
'屯門醫院': '新界',
'廣華醫院': '九龍',
'瑪麗醫院': '港島'
};
// 使用方式
window.Health.refresh(); // 刷新急症室數據
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.