# Leaflet + OSM ###### tags: `JavaScript` `框架` `插件` Leaflet 是一個用來繪製地圖的開源 Javasctipt 函式庫,然而僅僅是繪圖還不夠,我們還需要有地圖的資料(圖資)才能夠完整的呈現出一個有城市、地區、街道等等的地圖。在這裡使用的是來自OpenStreetMap的圖資,它是一個免費讓人使用的開源地圖資料。 ## 圖層概念 ![](https://i.imgur.com/fhABMZv.jpg) 圖片來源: 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: '&copy; <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(); ``` ### 基礎地圖建構完成 ![](https://i.imgur.com/vZpoboa.jpg) ## 自定義新的 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 完成 ![](https://i.imgur.com/8UVdHOW.jpg) ## 地圖效能優化工具 - [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://i.imgur.com/MtJtPjo.jpg) ## 參考資料 https://leafletjs.com/ https://www.openstreetmap.org/#map=8/23.611/120.768