# [筆記]Web地圖開發工作坊 #### by 廖洧杰 / 六角學院校長 ##### 2020/02/07 ###### tags: `w3HexSchool`, `線上直播` #### 共筆文件:[Leaflet + OpenStreetMap (OSM) 特訓班](https://quip.com/vdqYAiFHHkaV) #### 直播錄影:[Leaflet + OpenStreetMap 地圖應用開發](https://www.youtube.com/watch?v=pUizu62dlnY) --- ## 前言 * 使用別人服務的 API,會有個各別的 KEY,依照其 KEY 收費 * Google Maps 要收費,很貴... * [科技防疫|自製「超商口罩地圖」的工程師:地圖上線6小時,我收到60萬Google帳單](https://futurecity.cw.com.tw/article/1239?fbclid=IwAR37iKIX0O8pdEf-bf2_AnvOsbVhamgNMPVAtd4ipIQWo0zyloMulWaASjc) * Google Maps, OSM(OpenStreetMap),... 都是地圖應用程式 * SPA => Js 底層應用要很熟:let, const, 立即函式, 閉包... ## Leaflet (Js 框架) Leaflet 是一套開放原始碼的輕量級 JavaScript 網頁地圖函式庫。主要特色:簡單、方便、跨平台 * 載入圖資(地圖資料) * 標示點(Maker) * [官網](https://leafletjs.com/) ## OpenStreetMap (圖資--地圖資料) OpenStreetMap (開放街圖,簡稱OSM) 採用類似Wiki的協作編輯以及開放的授權與格式,因而被稱之為「維基版的地圖」,也被視為Google最強大的競爭者。 ##### 補充資料:[OpenStreetMap 台灣](https://osm.tw/) ### 地圖圖層概念 地圖由多個圖層組成,又每個圖層由多個圖磚(PNG 圖片)所組成 ##### 補充資料:[圖層示意圖](https://www.androidpit.com/google-maps-gesture-controls) ## 實作 ### 1. 載入地圖 先載入其 css 與 js ```javascript= var map = L.map('map', { center: [22.604799,120.2976256], zoom: 3 }); // 設定地圖,把map定位在 #map,先定位在 center 座標,其縮放等級為 3 L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); ``` * L 表示 Leaflet的所有應用服務 * 使用 map 這個函式,傳入兩個參數:1. id為map的字串;2. 物件{地圖中心:[緯度, 經度], 縮放等級} * tileLayer 內放入要用誰的圖資 ### 2. 建立 UI Layer Marker #### 建立圖標 (Marker) ```javascript= L.marker([22.604799,120.2976256]).addTo(map) .bindPopup('<h1>測試藥局</h1><p>成人口罩:50<br>兒童口罩:50</p>') .openPopup(); // 我要加上一個 marker,並設定他的座標,同時將這個座標放入對應的地圖裡 ``` * 把圖標(Marker)加入圖層中 * 彈跳方框(bindPopup)內可放入 HTML 標籤 * openPopup => 滑鼠滑過去就會顯示 Popup #### 改變 Marker 顏色 https://github.com/pointhi/leaflet-color-markers ```javascript= var greenIcon = 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] }); L.marker([51.5, -0.09], {icon: greenIcon}).addTo(map); ``` #### 兩個點以上 Marker ```javascript= L.marker([22.604799,120.2976256], {icon: greenIcon}).addTo(map) .bindPopup('<h1>測試藥局</h1><p>成人口罩:50<br>兒童口罩:50</p>') L.marker([22.6066728,120.3015429]).addTo(map) .bindPopup('<h1>IKEA</h1><p>成人口罩:50<br>兒童口罩:50</p>') ``` 建立兩個 marker 物件 #### 利用for迴圈建立多個 Marker ```javascript= var data = [ {'name':'軟體園區',lat:22.604799,lng:120.2976256}, {'name':'ikea',lat:22.6066728,lng:120.3015429} ] for(var i =0;data.length>i;i++){ L.marker([data[i].lat,data[i].lng], {icon: greenIcon}).addTo(map) .bindPopup('<h1>'+ data[i].name +'</h1>') } ``` #### 效能處理 1. 載入額外的Js插件 [Leaflet.markercluster](https://github.com/Leaflet/Leaflet.markercluster) => 讓 Marker 群組化 ```htmlmixed= <link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" /> <link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.4.1/MarkerCluster.css"></link> <link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.4.1/MarkerCluster.Default.css"></link> <div id="map"></div> <script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.4.1/leaflet.markercluster.js"></script> ``` 2. 新增一圖層,這圖層專門放 Marker 群組 ```javascript= var markers = new L.MarkerClusterGroup().addTo(map);; ``` 3. 在該圖層上放入各個 Marker ```javascript= for(let i =0;data.length>i;i++){ console.log(data[i].name) markers.addLayer(L.marker([data[i].lat,data[i].lng], {icon: greenIcon})); // add more markers here... // L.marker().addTo(map) // ) } map.addLayer(markers); ``` ### 3. 載入真實資料 ```javascript= var markers = new L.MarkerClusterGroup().addTo(map);; // 開啟一個網路請求 var xhr = new XMLHttpRequest(); // 準備跟某伺服器要什麼資料 xhr.open("get","https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json"); // 執行要資料的動作 xhr.send(); // 當資料回傳時,下面語法就會自動觸發 xhr.onload = function(){ // 把字串String轉成物件陣列的Json格式,我要的是features內的陣列資料 var data = JSON.parse(xhr.responseText).features // 依序把 marker 放入圖層內 for(let i =0;data.length>i;i++){ markers.addLayer(L.marker([data[i].geometry.coordinates[1],data[i].geometry.coordinates[0]], {icon: greenIcon}).bindPopup(data[i].properties.name)); } map.addLayer(markers); } ``` * ajax撈回來的資料都是字串String格式,所以一定要做 JSON.parse ### 4. 下if判斷式 下判斷,若無剩餘口罩,就顯示為紅色Marker,若還有則是綠色Marker ```javascript= var map = L.map('map', { center: [22.604799,120.2976256], zoom: 16 }); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); var greenIcon = 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 redIcon = 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] }); var markers = new L.MarkerClusterGroup().addTo(map);; var xhr = new XMLHttpRequest(); xhr.open("get","https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json"); xhr.send(); xhr.onload = function(){ var data = JSON.parse(xhr.responseText).features for(let i =0;data.length>i;i++){ var mask; if(data[i].properties.mask_adult == 0){ mask = redIcon; }else{ mask = greenIcon; } markers.addLayer(L.marker([data[i].geometry.coordinates[1],data[i].geometry.coordinates[0]], {icon: mask}).bindPopup('<h1>'+data[i].properties.name+'</h1>'+'<p>成人口罩數量'+data[i].properties.mask_adult+'</p>')); // add more markers here... // L.marker().addTo(map) // ) } map.addLayer(markers); } ``` ### 5. Geolocation (地理位置定位) HTML5 提供了 Geolocation API 讓使用者可以由 Web Apps 取得目前的位置。而基於隱私權的考量,這些 Web Apps 均必須取得使用者的許可之後,才能發佈位置資訊。 ##### 參考資料:[MDN Web APIs-Geolocation](https://developer.mozilla.org/zh-TW/docs/Web/API/Geolocation/Using_geolocation) ##### 參考資料:[認識 HTML5 Geolocation API](https://sites.google.com/site/edreamer/html5-specialtraining/geolocation-google-maps-api) ##### 參考資料:[[30apis] Day 2 : Google Map Geolocation API](https://ithelp.ithome.com.tw/articles/10192680) ### 資料參考 * 示意介面參考(hackmd):https://g0v.hackmd.io/gGrOI4_aTsmpoMfLP1OU4A * API 網址:https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json ### 額外補充 * [示範:使用 Vuejs 結合 Open Street Map 製作口罩地圖](https://www.youtube.com/watch?v=7CXnNMVMXeo)