Yuan のノート
心之所至,隨意亂書
使用 Leaflet 地圖
Wed Aug 4, 2021
🏷️

前言

最近剛好要更新地圖應用。先前是直接使用 Mapbox ,但覺得它的 Marker 操作起來不是很彈性。因緣際會下聽說了 Leaflet ,就來試看看吧。

維基百科是這樣說的。

Leaflet是一個開源的JavaScript庫,用於構建Web地圖應用。首次發布於2011年,2它支持大多數移動和桌面平台,支持HTML5和CSS3。

主要內容

安裝方式

本文撰寫時會以 CDN 的方式引入 Leaflet.js,實際使用時筆者是會使用套件管理工具進行安裝。 安裝方式:

1
yarn add leaflet

使用 CDN 的方式:

1
2
3
4
5
6
7
8
 <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
   integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
   crossorigin=""/>
   
<!-- Make sure you put this AFTER Leaflet's CSS -->
 <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
   integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
   crossorigin=""></script>

起手式

在網頁中加入

1
<div id="map" style="width:95vw;height:95vh" />

初始化地圖

1
2
3
4
5
6
var map = L.map('map').setView([34.985851028839406, 135.75788488621308], 10);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}',
  {
    foo: 'bar', 
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
  }).addTo(map);
我們的第一張地圖

我們的第一張地圖

如果我們想要使用 Mapbox 的圖資,在已取得存取金鑰之後(Access Toekn),可在建立 titleLayer 時改用下列方式初始化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var map = L.map('map').setView([34.985851028839406, 135.75788488621308], 10);
L.tileLayer(''https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}',
  {
    attribution: '&copy; <a href="https://www.mapbox.com/about/maps/">Mapbox</a> contributors'
    maxZoom: 18,
    zoomOffset: -1,
    tileSize: 512,
    id: 'mapbox/streets-v11',
    accessToken: token
  }).addTo(map);
使用 Mapbox 圖資

使用 Mapbox 圖資

如果想要移除預設的 zoom controller 可以在初始化的時候加上 zoomControl: false

1
var map = L.map('map', {zoomControl: false}).setView([34.985851028839406, 135.75788488621308], 10);

加上 Marker

在 Leaflet 中加上 marker 就跟呼吸一樣自然。

1
2
3
L.marker([35.04074994371372, 135.72932367775914]).addTo(map);
L.marker([34.97374817523019, 135.77195253085293]).addTo(map);
L.marker([34.99936852552379, 135.7854861479551]).addTo(map);
加上 marker

加上 marker

如果想要改變樣式,可以透過 icon 或是 iconDiv 來更改。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
var myIcon = L.icon({
    iconUrl: 'marker.png',
    iconSize: [100, 95],
    iconAnchor: [22, 94],
    popupAnchor: [-3, -76],
    shadowUrl: 'my-icon-shadow.png',
    shadowSize: [68, 95],
    shadowAnchor: [22, 94]
});
L.marker([34.70432671595862, 135.50096236284378], {
	icon: myIcon
}).addTo(map);
1
2
3
4
5
6
7
var myDivIcon = L.divIcon({
    className:'my-div-icon-wrapper',
    html:`<div>這是 Div Icon </div>`
});
L.marker([34.68985107822455, 135.5253549268327], {
	icon: myDivIcon
}).addTo(map);
1
2
3
4
5
6
.my-div-icon-wrapper div{
    width: 100px;
    background-color: #060390;
    color: #eee;
    text-align: center;
}
加上 marker

加上 marker

來畫線吧

1
2
3
4
5
6
7
8
9
var latlngs = [
    [35.02537491062854, 135.7438607946139],
    [34.88948810597932, 135.8076289149232],
    [34.961035819215525, 135.65613226663768],
    [34.976863645786004, 135.82695459531024],
    [34.88036360232042, 135.7002729085305],
    [35.02537491062854, 135.7438607946139],
];
L.polyline(latlngs, {color: 'red'}).addTo(map);
來個封印陣吧!

來個封印陣吧!

群組化並加上控制項

我們可以把想要歸在一起的東西放到同一個群組,這樣在接下來要分層顯示的時候,會更簡便一些。

讓我們稍微調整一下程式碼。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
...

let layer1 = L.layerGroup([
    L.marker([35.04074994371372, 135.72932367775914]),
    L.marker([34.97374817523019, 135.77195253085293]),
    L.marker([34.99936852552379, 135.7854861479551])
]).addTo(map);

...

let layer2 = L.layerGroup([
    L.marker([34.70432671595862, 135.50096236284378], { icon: myIcon }),
    L.marker([34.68985107822455, 135.5253549268327], { icon: myDivIcon })
]).addTo(map);

...

let layer3= L.layerGroup([
    L.polyline(latlngs, {color: 'red'})
]).addTo(map);

將我們群組化後的 Layer 加入控制項中。

1
2
3
4
5
let controller = L.control.layers().addTo(map);
controller.addOverlay(layer1,"Marker");
controller.addOverlay(layer2,"自定的Marker");
controller.addOverlay(layer3,"封印陣");
controller.expand();
加入控制項

加入控制項

補充一下,如果想移除各個 Layer 的話可以透過下列方式,移除

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
map.value.eachLayer((layer) => {
    if (layer instanceof L.Marker) {
        map.value.removeLayer(layer);
    }
});


// or 

layer1.remove();

20210808 新增 - geoJSON Layer

測試資料可以到政府開放資料平台取得。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

let data = {"type":"FeatureCollection", "features": [
"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[121.543841724,25.0449066970000,...................... ]]]
]}

function onEachFeature(feature, layer) {
  if (feature.properties && feature.properties.TOWNNAME) {
    layer.bindPopup(feature.properties.TOWNNAME);
  }
}

L.geoJSON(data, {
    onEachFeature: onEachFeature,
    filter: function(feature, layer) {
        return feature.properties.TOWNNAME == '大安區';
    },
    style: function(feature) {
        switch (feature.properties.TOWNNAME) {
            case '大安區': return {color: "#00ff00",weight:1};
            default: return {color: "#333333",weight:1,opacity:0.5 };
        }
    }
}).addTo(map);
使用 geolayer

使用 geolayer

成果

小結

使用 Leaflet 之後,不管是在操作 Marker 還是要建立 Path 都變得更加容易了。只能說是相見恨晚!

參考連結