by 廖洧杰 / 六角學院校長
2020/02/07
共筆文件:Leaflet + OpenStreetMap (OSM) 特訓班
直播錄影:Leaflet + OpenStreetMap 地圖應用開發
前言
- 使用別人服務的 API,會有個各別的 KEY,依照其 KEY 收費
- Google Maps 要收費,很貴...
- Google Maps, OSM(OpenStreetMap),... 都是地圖應用程式
- SPA => Js 底層應用要很熟:let, const, 立即函式, 閉包...
Leaflet (Js 框架)
Leaflet 是一套開放原始碼的輕量級 JavaScript 網頁地圖函式庫。主要特色:簡單、方便、跨平台
- 載入圖資(地圖資料)
- 標示點(Maker)
- 官網
OpenStreetMap (圖資--地圖資料)
OpenStreetMap (開放街圖,簡稱OSM) 採用類似Wiki的協作編輯以及開放的授權與格式,因而被稱之為「維基版的地圖」,也被視為Google最強大的競爭者。
補充資料:OpenStreetMap 台灣
地圖圖層概念
地圖由多個圖層組成,又每個圖層由多個圖磚(PNG 圖片)所組成
補充資料:圖層示意圖
實作
1. 載入地圖
先載入其 css 與 js
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: '© <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)
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
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
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
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 => 讓 Marker 群組化
<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 群組
var markers = new L.MarkerClusterGroup().addTo(map);;
3.在該圖層上放入各個 Marker
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. 載入真實資料
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
var map = L.map('map', {
center: [22.604799,120.2976256],
zoom: 16
});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <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
參考資料:認識 HTML5 Geolocation API
參考資料:[30apis] Day 2 : Google Map Geolocation API
資料參考
- 示意介面參考(hackmd):https://g0v.hackmd.io/gGrOI4_aTsmpoMfLP1OU4A
- API 網址:https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json