# Leaflet + OSM
###### tags: `JavaScript` `框架` `插件`
Leaflet 是一個用來繪製地圖的開源 Javasctipt 函式庫,然而僅僅是繪圖還不夠,我們還需要有地圖的資料(圖資)才能夠完整的呈現出一個有城市、地區、街道等等的地圖。在這裡使用的是來自OpenStreetMap的圖資,它是一個免費讓人使用的開源地圖資料。
## 圖層概念

圖片來源: https://www.androidpit.com/google-maps-gesture-controls
在我們看到的地圖的城市、街道等等的畫面都是經由一片一片的圖片磚塊組合而成的一個 View ,同樣的地圖上的 Marker 也是一樣的概念,若要建立多個 Marker 的話,就必須在 map 的圖層建立一個又一個的圖層並把他加疊上去。
## 開始使用 Leaflet
> 在這裡以 CDN 的方式加載 : Leaflet 的 CSS 與 Javascript ([Download](https://leafletjs.com/download.html))
```
<LINK rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin=""/>
<SCRIPT src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"
integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew=="
crossorigin=""></SCRIPT>
```
## 創建 id
> 在 HTML 中創建一個帶有 *id* 的 HTML Tag 並指定他的寬高
### HTML
```
<DIV id="map"></DIV>
```
### CSS
```
HTML, BODY {
width: 100%;
height: 100%;
}
#map {
width: 100%;
height: 100%;
}
```
## 創建一個地圖最底層的 View
> 建立一個地圖並指定到 HTML 中指定的 *id* 上
### JAVASCRIPT
```
let map = L.map('map', {
center: [22.6613099, 120.2936813],
zoom: 16
});
```
## 把地圖資料放到地圖底層上
### JAVASCRIPT
```
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
maxZoom: 18
}).addTo(map);
```
## 創建一個 Maker
### JAVASCRIPT
```
L.marker([22.6613099, 120.2936813]).addTo(map)
.bindPopup('A pretty CSS3 popup.<br> Easily customizable.')
.openPopup();
```
### 基礎地圖建構完成

## 自定義新的 MakerIcon ([Icon 資料來源](https://github.com/pointhi/leaflet-color-markers))
### JAVASCRIPT
> 需放在建立 marker 之前
```
var iconGreen = new L.Icon({
iconUrl: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
shadowUrl:'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
var iconRed = new L.Icon({
iconUrl: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
shadowUrl:'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
```
> 將建立好的 Icon 加到地圖中
```
// 在座標後加入 , {icon: iconGreen} 即可
L.marker([22.6613099, 120.2936813], {icon: iconGreen}).addTo(map)
.bindPopup('A pretty CSS3 popup.<br> Easily customizable.')
.openPopup();
```
### 更新地圖 Icon 完成

## 地圖效能優化工具 - [Leaflet.markercluster](https://github.com/Leaflet/Leaflet.markercluster) (由 [Leaflet](https://leafletjs.com/plugins.html) 網站提供)
> 由於資料量龐大又無優化,在頁面瀏覽時將會給與效能上帶來極大的負擔,因此在這裡使用 Leaflet 提供的效能優化工具去把地圖上面的 Makers 給群組化
### 加載 Leaflet.markercluster
```
// CSS
<LINK rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.4.1/MarkerCluster.css">
<LINK rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.4.1/MarkerCluster.Default.css">
// JAVASCRIPT
<SCRIPT src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.4.1/leaflet.markercluster.js"></SCRIPT>
```
### Leaflet.markercluster 使用語法
```
var markers = L.markerClusterGroup();
markers.addLayer(L.marker(getRandomLatLng(map)));
... Add more layers ...
map.addLayer(markers);
```
## 利用 AJAX 從遠端撈出 JSON 資料,並搭配優化工具將資料套用至地圖上
> [JSON 資料來源](https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json)(由: [kiang](https://github.com/kiang) 從衛福部所提供資料轉成 JSON 格式)
```
// 開啟一個網路請求
let xhr = new XMLHttpRequest();
// 準備跟伺服器要取資料
xhr.open('GET', 'https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json?fbclid=IwAR3qcxfUqiu0ZeKsKSyxtswszifzwpW_Ft9DlA99qyUJ1TbmniqDFHqGMFI')
// 執行要資料的動作
xhr.send();
// 拿到資料後觸發下面的語法
xhr.onload = function(){
let data = JSON.parse(xhr.responseText).features;
let dataLen = data.length;
let markers = L.markerClusterGroup().addTo(map);
for (let i = 0; i < dataLen; i++) {
let mark;
// 判斷數量是否為 0
if (data[i].properties.mask_adult == 0 && data[i].properties.mask_child == 0) {
mark = iconRed; // 若 =0 Icon 為紅色
} else {
mark = iconGreen; // 若 !=0 Icon 為綠色
}
markers.addLayer(
L.marker([data[i].geometry.coordinates[1], data[i].geometry.coordinates[0]], {
icon: mark
}).bindPopup(data[i].properties.name + '<br />' + '成人口罩:' + data[i].properties.mask_adult +'<br />' + '兒童口罩:' + data[i].properties.mask_child)
);
map.addLayer(markers);
}
}
```
### 獲取資料並將 Markers 群組化完成

## 參考資料
https://leafletjs.com/
https://www.openstreetmap.org/#map=8/23.611/120.768