OSM + Leaflet 學習筆記 3:定位、全螢幕、小地圖、列印、客製選單
Let's Write
Posted on August 20, 2022
本篇要解決的問題
之前寫過二篇 OSM + Leaflet.js 的筆記文,當時總覺得跟 Google Maps 比起來有點陽春。前陣子有一次搭客運到宜蘭,瞄到客運員工開了一個網頁在看各車次到了哪裡,使用的就是 OpenStreetMap(OSM),當時看到就覺得,原來 OSM 還可以這樣子用呀(筆記)~
前幾天回頭看官方文件,發現文件裡有一頁是「Plugins」,裡面洋洋灑灑地列出了一堆套件,讓使用地圖的人可以直接套用,減少開發時間。
本篇就是先看了幾個套件,選出幾個覺得未來可能會到的,並實作出一頁 Demo 來,把這些套件都套進地圖裡。
選出的套件有:
- 定位
- 全螢幕
- 右下方同步小地圖
- 列印
- 含 collapse 功能的客製選單
還有幾個套件也覺得好用,但五個套件在一篇文章裡有點多了,就看以後有沒有機會再寫第四篇學習筆記文。
OSM + Leaflet.js 的基本使用,像是繪製地圖、放 markder 等,請看前二篇筆記文,這邊不會有詳細的解釋。
- OSM + Leaflet 學習筆記 1:建地圖、marker、事件、換圖層
- OSM + Leaflet 學習筆記 2:移動中心點、抓目前地點
SHP 轉 GeoJSON,座標轉 WGS84
這次先從政府資料開放平臺裡,選出「國家公園、國家森林遊樂區及國家風景區範圍內之觀光景點(本島)」當地圖上的點。
結果,抓出來的資料格式是 SHP,需要轉成 GeoJSON 後才能使用。
然後就踩坑了。
主要的坑就是,當用了 mapshaper 轉成 GeoJSON 後,因為政府提供的資料裡,座標是真的座標,但我們用的 OSM 是使用麥卡托投影法,座標必須轉成 WGS84 的格式,放上地圖時才會正確。
之前有寫過一篇「D3.js、Vue 畫一個台灣地圖」,裡面是靠 D3.js 原有的 function 去處理,而這邊是用 Leaflet.js,需要另尋出路。
Leaflet.js 文件上有看到一個「coordsToLatLng」的 function,但不論試了各種寫法都沒有轉成功。
後來再 Google 查了一下,發現地圖投影法真的是另一個小宇宙,有夠複雜,投影法的介紹可以看這篇「地理小課堂:那些被錯付的地圖投影法」,這邊就用最簡單的方式來做轉換。
這個簡單的方式就是:在 mapshaper 轉成 GeoJSON 時,輸出前先將座標轉為 WGS84。
這個方式主要來自於這篇:How to make your coordinates WGS84 with mapshaper.org
1 將要轉出的檔案拉進 mapshaper 裡:
2 點擊右上角的 Console,輸入 -proj wgs84
後按下 enter:
3 點右上角的 Export,格式選 GeoJSON:
以上,這樣匯出的 JSON 檔,裡面的座標就會是 WGS84 格式,可以正確放上地圖。
定位:Leaflet.Locate
官方文件:https://github.com/domoritz/leaflet-locatecontrol
官方示例:https://domoritz.github.io/leaflet-locatecontrol/demo/
這個套件蠻好用的,除了有定位光點的樣式,還給了目前朝哪的方向。
不過遇到一個很奇怪的狀況,JS 檔如果是引用 CDN 的,就會一直抓不準定位,誤差距離會到二公里以上,只有直接從 GitHub 上把 JS 下載下來使用,定位才正常。
實測了好幾次都是這樣,所以大家如果要用,建議直接從 GitHub 裡抓 JS。
CSS 則可以引用 CDN,CSS 裡引用圖檔的部份就不用再另外下載。
Leaflet.Locate 使用方式很簡單,當地圖渲染完成後,JS 如下:
L.control.locate({
position: 'topleft',
locateOptions: {
enableHighAccuracy: true
},
strings: {
title: '定位我的位置',
metersUnit: '公尺',
feetUnit: '英尺',
popup: '距離誤差:{distance}{unit}以內'
},
clickBehavior: {
inView: 'stop',
outOfView: 'setView',
inViewNotFollowing: 'inView'
}
}).addTo(map);
position
:定位按鈕要放地圖的哪個位置。
enableHighAccuracy
:要不要啟用精準定位。
strings
:顯示文字的部份。這邊是 August 自行翻譯的,其中 popup
內容跟原本的不太一樣,但覺得說明誤差距離會比較好懂意思。
clickBehavior
:點擊執行按鈕時,不同狀況要給的不同動作。
這套使用起來很簡單,文件中也列出了一堆參數,想看還有什麼可以使用的參數請自行上官方文件查看。
全螢幕:Leaflet.Control.FullScreen
官方文件:https://github.com/brunob/leaflet.fullscreen
官方示例:https://brunob.github.io/leaflet.fullscreen/
一般嵌入地圖在網頁上,很少會是全螢幕的嵌入,因為當地圖是全螢幕時,滑鼠或手勢的操作就會全被地圖的操作給吃掉,頁面的其它部份就不容易被看到。
不過如果是只嵌在網頁上的一小塊,桌機時還好,畢竟螢幕夠大的話還是可以看見地圖內容。
但手機就不方便了,像是本篇的 Demo 加了一堆按鈕上去,地圖本身的內容就會被擋住一大部份。
這時有個全螢幕按鈕讓地圖是全螢幕呈現,就很方便。
使用方式很簡單,當地圖渲染完成後,JS 如下:
L.control.fullscreen({
position: 'topleft',
title: '進入全螢幕',
titleCancel: '離開全螢幕',
content: '<img class="p-1" src="dist/size-fullscreen.svg">',
forceSeparateButton: true,
forcePseudoFullscreen: true,
fullscreenElement: false
}).addTo(map);
position
:全螢幕按鈕要放地圖的哪個位置。
content
:全螢幕按鈕裡的 HTML,這邊是放一張 svg 的圖檔。
forceSeparateButton
:全螢幕按鈕要不要跟縮放按鈕分開來。
forcePseudoFullscreen
:全螢幕要玩真的還是玩假的 XD~
true
代表要執行的是假的全螢幕、false
代表是真的全螢幕。
假的 就是讓地圖的寬高塞滿頁面而已,瀏覽器上的那些網址列或書籤列都還看得到。
真的 就是像 Youtube 上我們點全螢幕一樣,會看到一個滿滿的地圖大平台。
fullscreenElement
:看了文件看不懂意思,只知道當設為 true
時,就必須要在點擊按鈕後再執行某個 callback,不然全螢幕功能會失效。
套件裡還給了二個事件:enterFullscreen
(進入全螢幕)、exitFullscreen
(離開全螢幕)。
本篇的 Demo 有使用到這二個事件,拿來做全螢幕按鈕上的圖片切換:
const fullscreenBtn =
document.querySelector('.leaflet-control-zoom-fullscreen');
map.on('enterFullscreen', () => {
fullscreenBtn.innerHTML =
'<img src="dist/fullscreen-exit.svg">';
});
map.on('exitFullscreen', () => {
fullscreenBtn.innerHTML =
'<img src="dist/size-fullscreen.svg">';
});
小地圖:Leaflet.MiniMap
官方文件:https://github.com/Norkart/Leaflet-MiniMap
這個套件就是在地圖的右下角再放一個小地圖,可以同步大地圖顯示目前我們選擇的地方。
它的作法其實就是嵌入第二個地圖,因此我們在寫渲染地圖的部份,可以把相同的參數設成變數。
// 相同的參數存成變數
const center = {地圖中心點};
const zoom = {縮放值};
const map = L.map('map').setView(center, zoom);
const osmUri = {Tiles 的檔按路徑};
const attribution = {版權宣告內容};
// 主要地圖
L.tileLayer(osmUri, {
attribution: attribution
}).addTo(map);
// 小地圖
const miniOSM = new L.TileLayer(osmUri, {
minZoom: 0, maxZoom: 13,
attribution: attribution
});
列印:leaflet-easyPrint
官方文件:https://github.com/rowanwins/leaflet-easyPrint
官方示例:https://rowanwins.github.io/leaflet-easyPrint/
這個……其實也不確定功能的實用性,但 Google Maps 有分享地圖的功能,而 OSM 目前還沒看到分享功能之類的套件,就先拿列印功能來充數,至少可以讓其它人知道我們看到的地圖長什麼樣子。
L.easyPrint({
title: '列印地圖',
position: 'topleft',
sizeModes: ['Current', 'A4Portrait', 'A4Landscape']
}).addTo(map);
sizeModes
是要列印成哪些格式,預設有三種,也可以另外自己去設定寬高。
完整的參數就請自行看文件囉。
客製選單:sidebar-v2
官方文件:https://github.com/turbo87/sidebar-v2/
官方示例:https://turbo87.github.io/sidebar-v2/examples/index.html
這套用起來稍微麻煩的地方,就是必須要調整 HTML,它的作法就是另外寫好一個 collapse 的 HTML,然後塞進地圖裡。
如果想要自己寫一個不一樣的選單,是可以自己花時間寫,用套件單純就是為了節省開發時間。
程式碼的部份,可以看官方提供的:
https://github.com/Turbo87/sidebar-v2/blob/master/examples/index.html
本篇 Demo 因為有寫了讓地圖維持在 16:9、4:3 的比例,所以 HTML、CSS 都有再做修改,跟官方提供的程式碼有不同,各位如果要使用,建議是看原本的程式碼,複製貼上後再針對各自的頁面去修改樣式。
除了 sidebar-v2,也有蠻多客製按鈕的套件很不錯,以下列出,可自行看示例選擇要不要使用:
本篇 Demo 及原始碼
最後附上本篇的 Demo 網址,及原始碼的 Github 網址。
Demo:https://letswritetw.github.io/letswrite-leaflet-plugins/
GitHub:https://github.com/letswritetw/letswrite-leaflet-plugins
OSM + Leaflet 學習筆記系列
OSM + Leaflet 學習筆記 1:建地圖、marker、事件、換圖層
Posted on August 20, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.