本篇要解決的問題
之前有寫過一系列的 Google Maps API 學習筆記。Google Maps 好用,但過了一定的額度會收費,當時就有人留言問說免費地圖的事,有留上了心。
原本是直接找 OpenStreetMap 的 API,後來看到文件上回來的都是用 XML,實在是懶得處理轉 JSON 這段。之後幸運地找到一個可以接 OpenStreetMap 的 Leaflet,一個用 JavaScript 接 API 的開源碼,就翻了一下文件,整理成這篇。
本篇會示範的功能如下:
- 使用者請求地點,建立地圖
- 放置 + 客製 marker
- Popup
- Tooltip
- Map click event
- 外部 zoom in、zoom out
- 改變地圖樣式
本篇主要參考文件:Leaflet Quick Start Guide
後續各段會用到的參考文件,將寫在各段之中。
本篇最後會完成的 Demo:
https://letswritetw.github.io/letswrite-leaflet-osm-basic/
請求地點,建立地圖
跟 Google Maps API 一樣,Leaflet 在建立地圖時需要一個中心點。
跟使用者請求所在位置座的座標,需要使用者同意,因此也要先預設一個當使用者拒絕時,或是所擁有的裝置不支援提供所在座標時的地點。
本篇預設地點設為台北市立動物園~ 想去看水豚君啊~~~
關於找地點的座標,August 用的是比較簡單的方法,如果有人有更好的方法歡迎留言提供。
首先進到 Google Maps 的網站,搜尋想要知道座標的地點,我們這邊搜尋「動物園」後,選到動物園,接著按下資料卡片的「分享」:
接著點擊「複製連結」:
然後把複製的網址貼到瀏覽器上打開,短網址會回復成原網址,就能從網址中看見座標,像這樣:
這是 August 知道不用接任何 API 就能找到一個地點座標的方式。
跟使用者要權限
當我們跟使用者要權限時,使用者會看到這樣的請求:
當使用者按下了「允許」,我們就可以得到使用者的座標,程式碼如下:
// 跟使用者要位置 | |
function successGPS(position) { | |
const lat = position.coords.latitude; | |
const lng = position.coords.longitude; | |
center = [lat, lng]; | |
// 接著寫確認了座標後要執行的事 | |
}; | |
function errorGPS() { | |
window.alert('無法判斷您的所在位置,無法使用此功能。預設地點將為 台北市動物園'); | |
// 接著寫使用者「封鎖」位置資訊請求後要執行的事 | |
} | |
if(navigator.geolocation) { | |
navigator.geolocation.getCurrentPosition(successGPS, errorGPS); | |
} else { | |
window.alert('您的裝置不具備GPS,無法使用此功能'); | |
// 接著寫使用者裝置不支援位置資訊時要執行的事 | |
} |
建立地圖
不管有沒有取到使用者的所在位置,都一樣可以建立 OpenStreepMap 的地圖。
首先先引用 CSS、JS:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.8.0/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.8.0/dist/leaflet.js"></script>
接著在我們要放置地圖的地方,放上一個空 div 並帶上我們指定的 id:
<div id="map"></div>
下一步,給我們的 div 一個高度:
#map {
height: 180px;
}
高度的部份本篇的 Demo 是用 50vh
,實際應用時依各頁面的狀況再作修改。
最後就是用 Leaflet 來建立地圖:
// *** 放置地圖 | |
let zoom = 17; // 0 - 18 | |
let center = [xxx, xxx]; // 中心點座標 | |
let map = L.map('map').setView(center, zoom); | |
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { | |
attribution: '© OpenStreetMap', // 商用時必須要有版權出處 | |
zoomControl: true , // 是否秀出 - + 按鈕 | |
}).addTo(map); |
Options 的部份可自行參考 文件,這邊僅列出 August 覺得會用到的。
L.tileLayer
這個是指地圖要讀取哪一個樣式,文件中的預設樣式就是這個:
https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
少了 tileLayer
地圖會是一片灰。
開啟頁面後,地圖就出來了:
放置 Marker
放置 marker 的方式就是新增一個出來後,add 到 map 裡,程式碼如下:
const center = [xxx, xxx]; | |
const marker = L.marker(center, { | |
title: '跟 <a> 的 title 一樣', // 跟 <a> 的 title 一樣 | |
opacity: 1.0 | |
}).addTo(map); |
center
就是要放置的座標。
預設的 Maker 樣式如下:
客製 Maker 圖示
Marker 可以用客製的 icon,如下:
const customIcon = L.icon({ | |
iconUrl: '圖片的 URL', | |
iconSize: [42, 42], | |
}); | |
const marker = L.marker(center, { | |
icon: customIcon, | |
title: '跟 <a> 的 title 一樣', // 跟 <a> 的 title 一樣 | |
opacity: 1.0 | |
}).addTo(map); |
我們這邊多放置幾個來看:
一次放置多個可以用迴圈,Demo 的原始檔會附上。
Marker 加上 Popup
Marker 能加上 Popup 跟 Tooltip。
Popup 可以預設打開。
const center = [xxx, xxx]; | |
const marker = L.marker(center, { | |
title: '跟 <a> 的 title 一樣', // 跟 <a> 的 title 一樣 | |
opacity: 1.0 | |
}).addTo(map); | |
// 加上 Popup | |
marker.bindPopup("<b>Hello world!</b><br>I am a popup.").openPopup(); |
openPopup()
就是打開 Popup。
Popup 長這個樣子:
Marker 加上 Tooltip
Tooltip 就是我們常見的 Tooltip(嗯?有寫跟沒寫一樣)。
一樣可以預設要不要打開,也可以設定顯示的位置:
marker.bindTooltip("my tooltip text", { | |
direction: 'bottom', // right、left、top、bottom、center。default: auto | |
sticky: true, // true 跟著滑鼠移動。default: false | |
permanent: false, // 是滑鼠移過才出現,還是一直出現 | |
opacity: 1.0 | |
}).openTooltip(); |
Tooltip 長這樣:
Map 點擊事件
Leaflet 可以監聽 Map 的點擊。
在官方的範例就有示範點擊地圖後,加上一個 Popup 寫著此處座標,這邊 August 改寫一下:
const popup = L.popup(); | |
function onMapClick(e) { | |
let lat = e.latlng.lat; // 緯度 | |
let lng = e.latlng.lng; // 經度 | |
popup | |
.setLatLng(e.latlng) | |
.setContent(`緯度:${lat}<br/>經度:${lng}`) | |
.openOn(map); | |
} | |
map.on('click', onMapClick); |
隨便點擊地可上任一地方,就會出現寫著座標的 Popup:
zoom in, zoom out
除了地圖上預設的功能鍵,也可以用地圖外的按鈕,用 function 執行 zoom in、zoom out。
map.zoomIn(1);
map.zoomOut(1);
數字是一次要 zoom 多少,實際測了一下,zoom 的值是 0 – 18。
0 是世界地圖,18 就是到街景了。
改變地圖樣式 Layer
參考:How to change base layer using JS and leaflet layers control
除了預設的地圖樣式,還有蠻多種樣式可以使用的,在 Leaflet Provider Demo 上可以看到多種,大部份也都有附上 Layer 的網址。
不過,有些不確定是否有收費,因為看到需要 token。
改變地圖樣式的按鈕,可以放在地圖之中,也可以在地圖之外放按鈕控制。
在地圖之內
在地圖之內,總覺得樣式有點陽春 XD~
但勝在方便。
var baselayers = { | |
'OpenStreetMap.Mapnik': L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'), | |
'OpenStreetMap.DE': L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png'), | |
'OpenStreetMap.CH': L.tileLayer('https://tile.osm.ch/switzerland/{z}/{x}/{y}.png'), | |
'OpenStreetMap.France': L.tileLayer('https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png'), | |
'OpenStreetMap.HOT': L.tileLayer('https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png'), | |
'OpenStreetMap.BZH': L.tileLayer('https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png'), | |
'OpenTopoMap': L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png') | |
}; | |
var overlays = {}; | |
L.control.layers(baselayers, overlays).addTo(map); | |
baselayers['OpenStreetMap.Mapnik'].addTo(map); |
把控制選項放在地圖上,長這樣:
Hover 時會秀出選單:
實際使用,覺得切換的時間有點久,大家看 Demo 時要等一下。
最喜歡的是深色的一款 Stadia.AlidadeSmoothDark,不過這款免費使用下有限制,可以看 官網 說明。
在地圖之外
改變 Layer 主要是這行:
L.tileLayer('layer 的 url').addTo(map);
因此要用地圖外面的按鈕來切換地圖的樣式,只需要寫 click 時執行上面那行就行。
本篇 Demo 及原始碼
最後附上本篇的 Demo 網址,及原始碼的 Github 網址。
Demo:https://letswritetw.github.io/letswrite-leaflet-osm-basic/
Github:https://github.com/letswritetw/letswrite-leaflet-osm-basic
Top comments (0)