昨天我們學了 leaflet
產生地圖&圖標&畫線
今天我們來思考常見的公車地圖如何實作出來
公車路線圖有2種資料
我們都知道2點連成一線,路線其實就是超級多的點連接起來~
因此我們只要有辦法拿到該地區公車的站牌JSON
& 路線JSON
搭配昨天學的方法就可以完成我們的目標!
查了一下發現
"交通部"有建立一個「運輸資料流通服務平臺」TDX(Transport Data eXchange)
實際開發上需要call他們的API(要註冊會員拿到金鑰)
因為我們是demo只要知道JSON即可
所以直接找他們的範例來看:
(筆者撰寫日期: 2024/08/31, 不確定日後會不會改版)
以下拿台中市的289公車為例
進入查詢公車路線的網站
https://ticp.motc.gov.tw/motcTicket/tools?tab=query-tab&city=Taichung&queryType=routeName&keyword=
搜尋 289 >> 點Table的TXG289 (網頁的檢查記得開著)
會看到call了3隻API
第1隻有"Stops"站牌資訊; 第3隻有"Geometry"公車路線資訊
仿造台中公車縮放比例小時只顯示數字
放大時顯示數字和站名
注意
:站牌資料(stopData)&路線資料(routeData) 這邊省略
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bus Route Map</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.css"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.js"></script>
<style>
body {
background-color: black;
padding: 0;
margin: 0;
}
.wrapper {
width: 99dvw;
height: 99dvh;
display: flex;
justify-content: center;
align-items: center;
}
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div class="wrapper">
<div id="map"></div>
</div>
<!-- 引入站牌資料(stopData)&路線資料(routeData) -->
<script src="./stopData.js"></script>
<script src="./routeData.js"></script>
<script>
const myMap = {
map: null,
markers: [],
init() {
this.map = L.map("map").setView(
[24.153940200805668, 120.6743698120117],
14
);
//圖層
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "© OpenStreetMap contributors",
}).addTo(this.map);
},
generateBusStop() {
// 公車站牌資料
const busStops = stopData;
// 產生marker並添加到地圖
busStops.forEach((stop) => {
const marker = L.marker([
stop.StopPosition.PositionLat,
stop.StopPosition.PositionLon,
]).addTo(this.map);
marker
.bindTooltip(stop.StopID, { permanent: true, direction: "top" })
.openTooltip();
this.markers.push({ marker, stop });
});
},
generateBusRoute() {
// 公車路線
const busRoute = {
type: "LineString",
coordinates: parseLineString(routeData.Geometry),
};
L.geoJSON(busRoute, {
style: function (feature) {
return { color: "blue", weight: 5 };
},
}).addTo(this.map);
},
main() {
// 產生路線
this.generateBusRoute();
// 產生站牌
this.generateBusStop();
// 更新標記的顯示內容
const updateMarkers = () => {
const zoomThreshold = 15;
const currentZoom = this.map.getZoom();
this.markers.forEach(({ marker, stop }, index) => {
const labelText =
currentZoom >= zoomThreshold
? `${index + 1}_${stop.StopName.Zh_tw}`
: `${index + 1}`;
marker.setTooltipContent(labelText);
});
};
// 初始化更新marker內容
updateMarkers();
// 當地圖縮放變化時更新marker
this.map.on("zoomend", updateMarkers);
},
};
myMap.init();
myMap.main();
function parseLineString(lineString) {
// 移除 "LINESTRING(" 和 ")" 並分割座標對
const coordinatesString = lineString
.replace("LINESTRING(", "")
.replace(")", "");
const coordinatesArray = coordinatesString.split(",");
// 將每個座標分割為經度和緯度,並轉換為 [經度, 緯度] 格式
const coordinates = coordinatesArray.map((coord) => {
const [lng, lat] = coord.trim().split(" ").map(Number);
return [lng, lat];
});
return coordinates;
}
</script>
</body>
</html>
更改提示框的文字方法
marker.setTooltipContent(要改的文字);
綁定縮放的事件監聽
this.map.on("zoomend", updateMarkers);