大部分想到地圖 API 直覺都是 Google Map,但在商業用途上,Google Map API 有使用量要收費的問題。目前可以透過 Leaflet 使用 OpenStreepMap,這部分在商業用途是可以免費使用的。
建立地圖
使用 leaflet 要先 include css 及 js,一開始要先指定地圖的中心點,以下範例定位在台中市政府。過程是用 L.map
建立地圖物件,然後加入 OpenStreetMap 這個 tile layer
建立地圖的範例
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""></script>
<style>
#map { height: 450px; };
</style>
</head>
<body>
<div id="map"></div>
</body>
<script>
var map = null;
function create_map() {
let zoom = 16; // 0 - 18
let taichung_cityhall = [24.1635657,120.6486657]; // 中心點座標
let maptemp = L.map('map',{renderer: L.canvas()}).setView(taichung_cityhall, zoom);
map = maptemp;
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap', // 商用時必須要有版權出處
zoomControl: true, // 是否顯示 - + 按鈕
zoomAnimation:false,
markerZoomAnimation: false,
fadeAnimation: false,
}).addTo(map);
// 強制用 resize window 將所有 tiles 載入
setTimeout(function () {
window.dispatchEvent(new Event('resize'));
}, 1000);
};
create_map();
</script>
</html>
當我們把程式整合到比較複雜的網頁中,會發現地圖的區塊在一開始載入後,會出現灰色的區塊,這一段程式碼是修正這個問題,用意是強制用 js resize window,讓 leaflet 能夠載入所有的地圖區塊。
// 強制用 resize window 將所有 tiles 載入
setTimeout(function () {
window.dispatchEvent(new Event('resize'));
}, 1000);
建立 Marker,放上 Tooltip
因為要使用 bootstrap icon 測試,要先引用 bootstrap-icons,然後加上兩個 css class: icon1, icon2
<link href="
https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.min.css
" rel="stylesheet">
<style>
#map { height: 450px; }
.icon1 {
color: #e41a1c;
}
.icon2 {
color: #377eb8;
}
</style>
這邊加上兩個 Marker 地圖標記,分別在台中市政府及火車站。
我們使用 L.divIcon
,這裡要注意,不能直接在 html 裡面寫上 style
調整 icon 顏色,只能指定 className 套用 css class。css class 很簡單,就只是修改 icon 的顏色
過程是用 L.marker
產生 marker,然後 bindTooltip
,再將 marker 加入地圖中
function test_marker() {
console.log("test_marker")
// ref: https://gis.stackexchange.com/questions/291901/leaflet-divicon-how-to-hide-the-default-square-shadow
// 不要寫 style 要用 className
const icon1 = L.divIcon({
html: '<i class="bi bi-geo-alt-fill"></i>',
iconSize: [20, 20],
className: 'icon1'
});
let taichung_cityhall = [24.1635657,120.6486657];
let taichung_station = [24.136941,120.685056];
let taichung_station2 = [24.1360,120.685056];
let marker1 = L.marker(taichung_cityhall, {
icon: icon1
});
marker1.bindTooltip("test1", {
direction: 'bottom', // right、left、top、bottom、center。default: auto
sticky: false, // true 跟著滑鼠移動。default: false
permanent: false, // 是滑鼠移過才出現,還是一直出現
opacity: 1.0
}).openTooltip();
marker1.addTo(map);
// remove marker
// marker1.remove();
const icon2= L.divIcon({
html: '<i class="bi bi-geo-alt-fill"></i>',
iconSize: [20, 20],
className: 'icon2'
});
let marker2 = L.marker(taichung_station2, {
icon: icon2
});
marker2.bindTooltip("test2", {
direction: 'bottom',
sticky: false,
permanent: false,
opacity: 1.0
}).openTooltip();
marker2.addTo(map);
};
test_marker();
Layer Group, Layer Control
多個 Marker 可以組合成一個 Layer,放到 Layer Group 裡面。再透過右上角 Layer Control,可將該 layer 的標記切換 on/off
function test_polyline() {
console.log("test_polyline");
const icon1 = L.divIcon({
html: '<i class="bi bi-geo-alt-fill"></i>',
iconSize: [20, 20],
className: 'icon1'
});
const layerControl = L.control.layers(null).addTo(map);
var test1_geos = [
[24.136941,120.68],
[24.136941,120.685056],
[24.1360,120.6850]
];
var test1_markers = [];
for (let i = 0; i < test1_geos.length; i++) {
let marker = L.marker([test1_geos[i][0], test1_geos[i][1]], {
icon: icon1
});
marker.bindTooltip("test1", {
direction: 'bottom',
sticky: false,
permanent: false,
opacity: 1.0
}).openTooltip();
test1_markers.push(marker);
}
// marker 座標的連線
var polyline1 = L.polyline(test1_geos, {color: '#e41a1c'});
var test1LayerGroup = L.layerGroup(test1_markers).addLayer(polyline1);
map.addLayer(test1LayerGroup);
layerControl.addOverlay(test1LayerGroup, "test1");
map.panTo(test1_geos[1]);
};
test_polyline();
搭配 vue3 使用的問題
ref: https://www.cnblogs.com/hjyjack9563-bk/p/16856014.html
在測試過程中,常會在 console 發生類似這樣的錯誤
Cannot read properties of null (reading '_latLngToNewLayerPoint')
這是因為 vue3 搭配 leaflet 才會遇到的問題,解決方式是在使用到 map 時,都要用 toRaw()
轉回原本的物件,所有用到 addlayer,removeLayer,clearLayers的方法 都應該用 toRaw(this.map)
ex:
// add layer control
this.layerControl = L.control.layers(null).addTo(toRaw(this.map));
References
OSM + Leaflet 學習筆記 1:建地圖、marker、事件、換圖層 - Front-End - Let's Write
Quick Start Guide - Leaflet - a JavaScript library for interactive maps