切換客製化地標
此範例示範如何切換不同主題地標,包含以下功能:
- 切換主題圖示:點擊太陽/月亮按鈕切換時,移除舊的 marker 資料並更換 icon
- 點擊 marker 顯示資訊:在卡片中顯示 marker 的標題與座標資訊
info
詳細使用參數說明請參考「地標」。
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>切換客製化地標</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://kw3dmap.localking.com.tw/openapi/loader/mapPlus-1.3.9.loader.js" crossorigin="anonymous" referrerpolicy="origin"></script>
<!-- Bootstrap CDN -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9"
crossorigin="anonymous"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css"
/>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm"
crossorigin="anonymous"
></script>
<style>
body { margin: 0; padding: 0; }
#map { width: 100vw; height: 100vh; }
#toolbar {
position: absolute;
top: 16px;
right: 16px;
width: fit-content;
height: fit-content;
z-index: 10;
}
</style>
</head>
<body>
<div id="map">
<aside
id="toolbar"
class="d-flex flex-column align-items-center gap-3 bg-white p-2 pb-3 rounded-2 shadow"
>
<div class="pb-2 border-bottom border-secondary-subtle">
<img
src="https://kw3dmap.autoking.com.tw/kingway-logo.png"
alt="Kingway map"
width="44"
height="44"
/>
</div>
<div class="btn-group-vertical" role="group">
<input type="radio" class="btn-check" name="btnradio" id="sunBtn" autocomplete="off" />
<label
class="btn btn-outline-primary"
for="sunBtn"
data-bs-toggle="tooltip"
data-bs-placement="left"
data-bs-title="太陽"
>
<i class="bi bi-sun-fill"></i>
</label>
<input type="radio" class="btn-check" name="btnradio" id="moonBtn" autocomplete="off" />
<label
class="btn btn-outline-primary"
for="moonBtn"
data-bs-toggle="tooltip"
data-bs-placement="left"
data-bs-title="月亮"
>
<i class="bi bi-moon-fill"></i>
</label>
</div>
</aside>
</div>
<script type="module">
const map = await new mapPlus(document.getElementById("map"), {
accessKey: 'get_your_key',
accessToken: 'get_your_token',
style: 'https://kw3dmap.localking.com.tw/openapi/map/kwmap.etxt',
center: [121.5251052025908, 25.044282178792372],
pitch: 50,
bearing: 25,
zoom: 14,
maxZoom: 20,
minZoom: 8,
});
// Bootstrap 提示訊息初始化
const tooltipList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
[...tooltipList].map((tooltip) => new bootstrap.Tooltip(tooltip));
let controller;
let popupElement;
const markerStore = [];
/* ══════════════════════════════════════════════════════════════════════════
* 切換太陽/月亮主題圖示:
- 調整 sunIcon/moonIcon 的字串值可更換為其他 base64 SVG 地標圖案。
- 調整 title 可設置卡片中的地標標題顯示。
- 調整 coordinates 可設置地標的經緯度座標。
══════════════════════════════════════════════════════════════════════════ */
// 太陽地標 icon 來源
const sunIcon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNS4xIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZmlsbD0iI0ZGRDQzQiIgZD0iTTM2MS41IDEuMmM1IDIuMSA4LjYgNi42IDkuNiAxMS45TDM5MSAxMjFsMTA3LjkgMTkuOGM1LjMgMSA5LjggNC42IDExLjkgOS42czEuNSAxMC43LTEuNiAxNS4yTDQ0Ni45IDI1Nmw2Mi4zIDkwLjNjMy4xIDQuNSAzLjcgMTAuMiAxLjYgMTUuMnMtNi42IDguNi0xMS45IDkuNkwzOTEgMzkxIDM3MS4xIDQ5OC45Yy0xIDUuMy00LjYgOS44LTkuNiAxMS45cy0xMC43IDEuNS0xNS4yLTEuNkwyNTYgNDQ2LjlsLTkwLjMgNjIuM2MtNC41IDMuMS0xMC4yIDMuNy0xNS4yIDEuNnMtOC42LTYuNi05LjYtMTEuOUwxMjEgMzkxIDEzLjEgMzcxLjFjLTUuMy0xLTkuOC00LjYtMTEuOS05LjZzLTEuNS0xMC43IDEuNi0xNS4yTDY1LjEgMjU2IDIuOCAxNjUuN2MtMy4xLTQuNS0zLjctMTAuMi0xLjYtMTUuMnM2LjYtOC42IDExLjktOS42TDEyMSAxMjEgMTQwLjkgMTMuMWMxLTUuMyA0LjYtOS44IDkuNi0xMS45czEwLjctMS41IDE1LjIgMS42TDI1NiA2NS4xIDM0Ni4zIDIuOGM0LjUtMy4xIDEwLjItMy43IDE1LjItMS42ek0xNjAgMjU2YTk2IDk2IDAgMSAxIDE5MiAwIDk2IDk2IDAgMSAxIC0xOTIgMHptMjI0IDBhMTI4IDEyOCAwIDEgMCAtMjU2IDAgMTI4IDEyOCAwIDEgMCAyNTYgMHoiLz48L3N2Zz4="
// 月亮地標 icon 來源
const moonIcon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzODQgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNS4xIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZmlsbD0iI0IxOTdGQyIgZD0iTTIyMy41IDMyQzEwMCAzMiAwIDEzMi4zIDAgMjU2UzEwMCA0ODAgMjIzLjUgNDgwYzYwLjYgMCAxMTUuNS0yNC4yIDE1NS44LTYzLjRjNS00LjkgNi4zLTEyLjUgMy4xLTE4LjdzLTEwLjEtOS43LTE3LTguNWMtOS44IDEuNy0xOS44IDIuNi0zMC4xIDIuNmMtOTYuOSAwLTE3NS41LTc4LjgtMTc1LjUtMTc2YzAtNjUuOCAzNi0xMjMuMSA4OS4zLTE1My4zYzYuMS0zLjUgOS4yLTEwLjUgNy43LTE3LjNzLTcuMy0xMS45LTE0LjMtMTIuNWMtNi4zLS41LTEyLjYtLjgtMTktLjh6Ii8+PC9zdmc+"
// 太陽地標清單
const sunList = [
{
// 地標標題,將傳入卡片中做為內容的顯示
title: "太陽 A",
// 地標的經緯度座標
coordinates: [121.51673619120999, 25.051163278902497]
},
{
title: "太陽 B",
coordinates: [121.51683369483514, 25.044902626729968]
},
{
title: "太陽 C",
coordinates: [121.52343221722336, 25.044752212110357]
},
];
// 月亮地標清單
const moonList = [
{
// 地標標題,將傳入卡片中做為內容的顯示
title: "月亮 A",
// 地標的經緯度座標
coordinates: [121.53673619120999, 25.052163278902497]
},
{
title: "月亮 B",
coordinates: [121.52683369483514, 25.054902626729968]
},
{
title: "月亮 C",
coordinates: [121.53343221722336, 25.034752212110357]
},
];
const addMarkerByList = (iconSource, markerList) => {
popupElement?.remove();
removeAllMarker();
controller = new AbortController();
markerList.forEach((markerOption) => {
addMarker(iconElement(iconSource), markerOption);
});
};
const sunBtn = document.getElementById("sunBtn");
sunBtn.addEventListener("click", () => {
addMarkerByList(
sunIcon,
sunList
);
});
const moonBtn = document.getElementById("moonBtn");
moonBtn.addEventListener("click", () => {
addMarkerByList(
moonIcon,
moonList
);
});
const markerOnClick = ({ title, coordinates: [lng, lat] }) => {
const mapElement = document.getElementById("map");
popupElement?.remove();
popupElement = document.createElement("div");
popupElement.className = "position-absolute d-flex flex-column bg-white p-3 rounded-2 shadow";
popupElement.style.top = "16px";
popupElement.style.left = "16px";
popupElement.style.width = "fit-content";
popupElement.style.height = "fit-content";
popupElement.style.zIndex = 10;
mapElement.append(popupElement);
const h1 = document.createElement("h1");
h1.className = "text-dark fs-4 fw-bold mb-3";
h1.innerText = title;
popupElement.append(h1);
const pLng = document.createElement("p");
pLng.className = "text-secondary fs-6";
pLng.innerText = "經度: " + lng;
popupElement.append(pLng);
const pLat = document.createElement("p");
pLat.className = "text-secondary fs-6";
pLat.innerText = "緯度: " + lat;
popupElement.append(pLat);
};
const iconElement = (iconSource) => {
const icon = document.createElement("div");
icon.className =
"bg-white p-4 border border-5 border-light rounded-circle shadow-sm";
icon.style.backgroundImage = "url(" + iconSource + ")";
icon.style.backgroundSize = "70%";
icon.style.backgroundRepeat = "no-repeat";
icon.style.backgroundPosition = "center";
icon.style.cursor = "pointer";
return icon;
};
const addMarker = (icon, markerOption) => {
const marker = new mapPlus.Marker({
position: markerOption.coordinates,
icon,
});
markerStore.push(marker);
marker.addListener(
"click",
() => markerOnClick(markerOption),
{ signal: controller.signal }
);
};
const removeAllMarker = () => {
controller?.abort();
markerStore.forEach((marker) => marker.remove());
markerStore.length = 0;
};
map.on("style.load", () => {
sunBtn.click();
map.getCanvas().addEventListener("click", () => {
popupElement?.remove();
});
});
</script>
</body>
</html>
<script src="https://kw3dmap.localking.com.tw/openapi/loader/mapPlus-1.3.9.loader.js" crossorigin="anonymous" referrerpolicy="origin"></script>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm"
crossorigin="anonymous"
></script>
<script type="module">
const map = await new mapPlus(document.getElementById("map"), {
accessKey: 'get_your_key',
accessToken: 'get_your_token',
style: 'https://kw3dmap.localking.com.tw/openapi/map/kwmap.etxt',
center: [121.5251052025908, 25.044282178792372],
pitch: 50,
bearing: 25,
zoom: 14,
maxZoom: 20,
minZoom: 8,
});
// Bootstrap 提示訊息初始化
const tooltipList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
[...tooltipList].map((tooltip) => new bootstrap.Tooltip(tooltip));
let controller;
let popupElement;
const markerStore = [];
/* ══════════════════════════════════════════════════════════════════════════
* 切換太陽/月亮主題圖示:
- 調整 sunIcon/moonIcon 的字串值可更換為其他 base64 SVG 地標圖案。
- 調整 title 可設置卡片中的地標標題顯示。
- 調整 coordinates 可設置地標的經緯度座標。
══════════════════════════════════════════════════════════════════════════ */
// 太陽地標 icon 來源
const sunIcon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNS4xIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZmlsbD0iI0ZGRDQzQiIgZD0iTTM2MS41IDEuMmM1IDIuMSA4LjYgNi42IDkuNiAxMS45TDM5MSAxMjFsMTA3LjkgMTkuOGM1LjMgMSA5LjggNC42IDExLjkgOS42czEuNSAxMC43LTEuNiAxNS4yTDQ0Ni45IDI1Nmw2Mi4zIDkwLjNjMy4xIDQuNSAzLjcgMTAuMiAxLjYgMTUuMnMtNi42IDguNi0xMS45IDkuNkwzOTEgMzkxIDM3MS4xIDQ5OC45Yy0xIDUuMy00LjYgOS44LTkuNiAxMS45cy0xMC43IDEuNS0xNS4yLTEuNkwyNTYgNDQ2LjlsLTkwLjMgNjIuM2MtNC41IDMuMS0xMC4yIDMuNy0xNS4yIDEuNnMtOC42LTYuNi05LjYtMTEuOUwxMjEgMzkxIDEzLjEgMzcxLjFjLTUuMy0xLTkuOC00LjYtMTEuOS05LjZzLTEuNS0xMC43IDEuNi0xNS4yTDY1LjEgMjU2IDIuOCAxNjUuN2MtMy4xLTQuNS0zLjctMTAuMi0xLjYtMTUuMnM2LjYtOC42IDExLjktOS42TDEyMSAxMjEgMTQwLjkgMTMuMWMxLTUuMyA0LjYtOS44IDkuNi0xMS45czEwLjctMS41IDE1LjIgMS42TDI1NiA2NS4xIDM0Ni4zIDIuOGM0LjUtMy4xIDEwLjItMy43IDE1LjItMS42ek0xNjAgMjU2YTk2IDk2IDAgMSAxIDE5MiAwIDk2IDk2IDAgMSAxIC0xOTIgMHptMjI0IDBhMTI4IDEyOCAwIDEgMCAtMjU2IDAgMTI4IDEyOCAwIDEgMCAyNTYgMHoiLz48L3N2Zz4="
// 月亮地標 icon 來源
const moonIcon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzODQgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNS4xIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZmlsbD0iI0IxOTdGQyIgZD0iTTIyMy41IDMyQzEwMCAzMiAwIDEzMi4zIDAgMjU2UzEwMCA0ODAgMjIzLjUgNDgwYzYwLjYgMCAxMTUuNS0yNC4yIDE1NS44LTYzLjRjNS00LjkgNi4zLTEyLjUgMy4xLTE4LjdzLTEwLjEtOS43LTE3LTguNWMtOS44IDEuNy0xOS44IDIuNi0zMC4xIDIuNmMtOTYuOSAwLTE3NS41LTc4LjgtMTc1LjUtMTc2YzAtNjUuOCAzNi0xMjMuMSA4OS4zLTE1My4zYzYuMS0zLjUgOS4yLTEwLjUgNy43LTE3LjNzLTcuMy0xMS45LTE0LjMtMTIuNWMtNi4zLS41LTEyLjYtLjgtMTktLjh6Ii8+PC9zdmc+"
// 太陽地標清單
const sunList = [
{
// 地標標題,將傳入卡片中做為內容的顯示
title: "太陽 A",
// 地標的經緯度座標
coordinates: [121.51673619120999, 25.051163278902497]
},
{
title: "太陽 B",
coordinates: [121.51683369483514, 25.044902626729968]
},
{
title: "太陽 C",
coordinates: [121.52343221722336, 25.044752212110357]
},
];
// 月亮地標清單
const moonList = [
{
// 地標標題,將傳入卡片中做為內容的顯示
title: "月亮 A",
// 地標的經緯度座標
coordinates: [121.53673619120999, 25.052163278902497]
},
{
title: "月亮 B",
coordinates: [121.52683369483514, 25.054902626729968]
},
{
title: "月亮 C",
coordinates: [121.53343221722336, 25.034752212110357]
},
];
const addMarkerByList = (iconSource, markerList) => {
popupElement?.remove();
removeAllMarker();
controller = new AbortController();
markerList.forEach((markerOption) => {
addMarker(iconElement(iconSource), markerOption);
});
};
const sunBtn = document.getElementById("sunBtn");
sunBtn.addEventListener("click", () => {
addMarkerByList(
sunIcon,
sunList
);
});
const moonBtn = document.getElementById("moonBtn");
moonBtn.addEventListener("click", () => {
addMarkerByList(
moonIcon,
moonList
);
});
const markerOnClick = ({ title, coordinates: [lng, lat] }) => {
const mapElement = document.getElementById("map");
popupElement?.remove();
popupElement = document.createElement("div");
popupElement.className = "position-absolute d-flex flex-column bg-white p-3 rounded-2 shadow";
popupElement.style.top = "16px";
popupElement.style.left = "16px";
popupElement.style.width = "fit-content";
popupElement.style.height = "fit-content";
popupElement.style.zIndex = 10;
mapElement.append(popupElement);
const h1 = document.createElement("h1");
h1.className = "text-dark fs-4 fw-bold mb-3";
h1.innerText = title;
popupElement.append(h1);
const pLng = document.createElement("p");
pLng.className = "text-secondary fs-6";
pLng.innerText = "經度: " + lng;
popupElement.append(pLng);
const pLat = document.createElement("p");
pLat.className = "text-secondary fs-6";
pLat.innerText = "緯度: " + lat;
popupElement.append(pLat);
};
const iconElement = (iconSource) => {
const icon = document.createElement("div");
icon.className =
"bg-white p-4 border border-5 border-light rounded-circle shadow-sm";
icon.style.backgroundImage = "url(" + iconSource + ")";
icon.style.backgroundSize = "70%";
icon.style.backgroundRepeat = "no-repeat";
icon.style.backgroundPosition = "center";
icon.style.cursor = "pointer";
return icon;
};
const addMarker = (icon, markerOption) => {
const marker = new mapPlus.Marker({
position: markerOption.coordinates,
icon,
});
markerStore.push(marker);
marker.addListener(
"click",
() => markerOnClick(markerOption),
{ signal: controller.signal }
);
};
const removeAllMarker = () => {
controller?.abort();
markerStore.forEach((marker) => marker.remove());
markerStore.length = 0;
};
map.on("style.load", () => {
sunBtn.click();
map.getCanvas().addEventListener("click", () => {
popupElement?.remove();
});
});
</script>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9"
crossorigin="anonymous"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css"
/>
<style>
body { margin: 0; padding: 0; }
#map { width: 100vw; height: 100vh; }
#toolbar {
position: absolute;
top: 16px;
right: 16px;
width: fit-content;
height: fit-content;
z-index: 10;
}
</style>