DEV Community

Cover image for OSM + Leaflet 學習筆記 3:定位、全螢幕、小地圖、列印、客製選單
Let's Write
Let's Write

Posted on • Updated on • Originally published at letswrite.tw

OSM + Leaflet 學習筆記 3:定位、全螢幕、小地圖、列印、客製選單

本篇要解決的問題

之前寫過二篇 OSM + Leaflet.js 的筆記文,當時總覺得跟 Google Maps 比起來有點陽春。前陣子有一次搭客運到宜蘭,瞄到客運員工開了一個網頁在看各車次到了哪裡,使用的就是 OpenStreetMap(OSM),當時看到就覺得,原來 OSM 還可以這樣子用呀(筆記)~

前幾天回頭看官方文件,發現文件裡有一頁是「Plugins」,裡面洋洋灑灑地列出了一堆套件,讓使用地圖的人可以直接套用,減少開發時間。

本篇就是先看了幾個套件,選出幾個覺得未來可能會到的,並實作出一頁 Demo 來,把這些套件都套進地圖裡。

選出的套件有:

  • 定位
  • 全螢幕
  • 右下方同步小地圖
  • 列印
  • 含 collapse 功能的客製選單

還有幾個套件也覺得好用,但五個套件在一篇文章裡有點多了,就看以後有沒有機會再寫第四篇學習筆記文。

OSM + Leaflet.js 的基本使用,像是繪製地圖、放 markder 等,請看前二篇筆記文,這邊不會有詳細的解釋。


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 裡:

將要轉出的檔案拉進 mapshaper

2 點擊右上角的 Console,輸入 -proj wgs84 後按下 enter:

console 裡輸入指令

3 點右上角的 Export,格式選 GeoJSON:

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

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

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

小地圖: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
});
Enter fullscreen mode Exit fullscreen mode

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

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、事件、換圖層

OSM + Leaflet 學習筆記 2:移動中心點、抓目前地點

OSM + Leaflet 學習筆記 3:定位、全螢幕、小地圖、列印、客製選單

Top comments (0)