iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 12
0
Modern Web

《你的地圖會說話? WebGIS與JavaScript的情感交織》系列 第 12

[5-1] 環域與繪圖工具 - 以Leaflet Draw實現

今天要來跟大家介紹環域查詢的前置作業。
使用者在圖台網站上想要查詢一塊區域內的資料,首先,
要先決定查詢的範圍可能是圓、長方形、不規則多邊形等等,
有兩種方式。

  • 輸入座標
    刻一個介面給使用者自行輸入座標,或者是上傳excel檔案讀取檔案內的座標組。如果是圓的話只要讀取圓心及半徑,如果是長方形或是不規則形的話,則要一連串座標。適用於有一批現成資料要做查詢與分析。
  • 繪圖工具
    刻一個繪圖工具的介面,讓使用者可以直接使用圖台的繪圖工具去畫出想要的圓或是多邊形,這個方式要去處理滑鼠事件,以及繪圖完畢後圖資的處理及儲存。適用於即時分析。
    今天我們就要來使用Leaflet API來實現繪圖工具。

初始化地圖

很久沒使用leaflet了,來幫大家複習一下。
↓ 先引入圖台基本的css與js

    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>

↓ 建一個存放地圖的div

    <div id="lmap"></div>

↓ 今天附上簡易的css,存放地圖div的高度記得要設定,不然地圖會顯現不出來!

        #lmap {
            height: 100%;
        }
        html,
        body {
            height: 100%;
            margin: 0;
            padding: 0;
        }

↓ 初始化地圖

        var LMap = L.map(document.getElementById('lmap'), {
            center: [23.5, 121],  // 中心點
            zoom: 7,  // 縮放層級
            crs: L.CRS.EPSG3857,  // 座標系統
        });
        L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
            maxZoom: 18,
            id: 'mapbox.streets'
        }).addTo(LMap);  // 新增底圖

繪圖工具

Leaflet API有一個很不錯的繪圖工具,繪圖完成後可以直接畫好在地圖上,並且可以儲存、編輯、返回上一個繪圖動作。

操作說明

https://ithelp.ithome.com.tw/upload/images/20200927/2013060408Y13OgiAx.jpg
↑ 繪圖工具

https://ithelp.ithome.com.tw/upload/images/20200927/20130604JogyIoYsPo.jpg
↑ 點擊

https://ithelp.ithome.com.tw/upload/images/20200927/20130604fnnaDr1Sel.jpg
↑ 完成

https://ithelp.ithome.com.tw/upload/images/20200927/201306049AutRcxPnH.jpg
↑ 返回上一個動作

建立繪圖工具

↓ 引入繪圖的css與js

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.2/leaflet.draw.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.2/leaflet.draw.js"></script>

↓ 建立一個FeatureGroup,用以存放繪圖的圖資

        var drawItem = new L.FeatureGroup();
        LMap.addLayer(drawItem);

↓ 新增繪圖的控制項

        var option = {
            position: 'topleft',
            collapsed: true,
            edit: {
                featureGroup: drawItem,
            },
        };
        var drawControl = new L.Control.Draw(option);
        LMap.addControl(drawControl);

↓ 新增繪圖的事件,在繪圖建立完的時候發生的callback function。

        LMap.on(L.Draw.Event.CREATED, function (e) {
             var layer = e.layer;
             drawItem.addLayer(layer);  // 必須將畫完的圖層加入
             console.log(arguments);
        });
        
        // 另一種寫法,以字串的方式寫事件名稱
        LMap.on('draw:created', function (e) {
             var layer = e.layer;
             console.log(arguments);
             drawItem.addLayer(layer);            
        });

Leaflet 繪圖事件一覽
https://ithelp.ithome.com.tw/upload/images/20200927/20130604rBGmFZvqwo.jpg
↓ 讓我們來看看繪圖建立完成的callback function的arguments
https://ithelp.ithome.com.tw/upload/images/20200927/2013060406e5eyCUuH.jpg
↓ 可以找到layer,並利用layer的方法找到繪製的半徑與中心點座標。並用layerType判斷圖形是哪一種形狀。

        LMap.on(L.Draw.Event.CREATED, function (e) {
            var layer = e.layer;
            var type = e.layerType;
            drawItem.addLayer(layer);
            console.log(type);

            if (type === 'circle') {
                var center = layer.getLatLng();
                var radius = layer.getRadius();
                console.log(`經度: ${center.lng}, 緯度: ${center.lat}`);
                console.log(`半徑: ${radius} (m)`);
            }
        });

↓ 結果
https://ithelp.ithome.com.tw/upload/images/20200927/20130604b6b19tz7AM.jpg

↓ 如果今天針對不同種圖形,要做相對應的事情,就要用if...else...來判斷type

            if (type === 'circle') {
                var center = layer.getLatLng();
                var radius = layer.getRadius();
                console.log(`經度: ${center.lng}, 緯度: ${center.lat}`);
                console.log(`半徑: ${radius} (m)`);
                
            } else if (type === 'marker'){
                var point = layer.getLatLng();
                console.log(`經度: ${point.lng}, 緯度: ${point.lat}`);
                
            } else if (type === 'rectangle'){
                var str ="";
                var arr = layer.getLatLngs();
                arr = arr[0].forEach(function(item, index){
                   str += `${index} => 經度: ${item.lng}, 緯度: ${item.lat}`
                });

                console.log(str);

            } else if (type === 'polygon'){
                var str ="";
                var arr = layer.getLatLngs();
                arr = arr[0].map(function(item, index){
                   return{
                       x: item.lng,
                       y: item.lat
                   }
                });

                console.log(arr);
            }

if...else...越寫越長,越寫越多,越寫越不直觀。
有沒有什麼方法?在不寫if...else...的情況下來因應不同種的情形,
不但不寫判斷,還要讓每一個function獨立只作業一件事情。


下一篇及下下篇,要來跟大家介紹callback與promise,
並且用adapter design pattern(配接器)的設計模式,
來重新寫callback function!
並達到最簡化判斷式的效果。

最近每天上班寫程式處理雜事,下班寫程式寫文章,腦袋越來越容易打結QAQ/images/emoticon/emoticon70.gif


上一篇
request的方式? ajax & fetch & axios
下一篇
[5-2] Callback & Promise - 解決request非同步的四種解法
系列文
《你的地圖會說話? WebGIS與JavaScript的情感交織》30

尚未有邦友留言

立即登入留言