DEV Community

Cover image for OSM + Leaflet 學習筆記 1:建地圖、marker、事件、換圖層
Let's Write
Let's Write

Posted on • Edited on

2 2

OSM + Leaflet 學習筆記 1:建地圖、marker、事件、換圖層

本篇要解決的問題

之前有寫過一系列的 Google Maps API 學習筆記。Google Maps 好用,但過了一定的額度會收費,當時就有人留言問說免費地圖的事,有留上了心。

原本是直接找 OpenStreetMap 的 API,後來看到文件上回來的都是用 XML,實在是懶得處理轉 JSON 這段。之後幸運地找到一個可以接 OpenStreetMap 的 Leaflet,一個用 JavaScript 接 API 的開源碼,就翻了一下文件,整理成這篇。

本篇會示範的功能如下:

  1. 使用者請求地點,建立地圖
  2. 放置 + 客製 marker
  3. Popup
  4. Tooltip
  5. Map click event
  6. 外部 zoom in、zoom out
  7. 改變地圖樣式

本篇主要參考文件: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>
Enter fullscreen mode Exit fullscreen mode

接著在我們要放置地圖的地方,放上一個空 div 並帶上我們指定的 id:

<div id="map"></div>
Enter fullscreen mode Exit fullscreen mode

下一步,給我們的 div 一個高度:

#map {
  height: 180px;
}
Enter fullscreen mode Exit fullscreen mode

高度的部份本篇的 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
Enter fullscreen mode Exit fullscreen mode

少了 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 樣式

客製 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);

我們這邊多放置幾個來看:

客製 Marker

一次放置多個可以用迴圈,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 長這個樣子:

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 長這樣:

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);
Enter fullscreen mode Exit fullscreen mode

數字是一次要 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);

把控制選項放在地圖上,長這樣:

地圖上有 Layer 的控制選項

Hover 時會秀出選單:

秀出 Layer 選單

實際使用,覺得切換的時間有點久,大家看 Demo 時要等一下。

最喜歡的是深色的一款 Stadia.AlidadeSmoothDark,不過這款免費使用下有限制,可以看 官網 說明。

在地圖之外

改變 Layer 主要是這行:

L.tileLayer('layer 的 url').addTo(map);
Enter fullscreen mode Exit fullscreen mode

因此要用地圖外面的按鈕來切換地圖的樣式,只需要寫 click 時執行上面那行就行。


本篇 Demo 及原始碼

最後附上本篇的 Demo 網址,及原始碼的 Github 網址。

Demo:https://letswritetw.github.io/letswrite-leaflet-osm-basic/

Github:https://github.com/letswritetw/letswrite-leaflet-osm-basic

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)