本系列文章已出版實體書籍:
「你的地圖會說話?WebGIS 與 JavaScript 的情感交織」(博碩文化)
WebGIS啟蒙首選✖五家地圖API✖近百個程式範例✖實用簡易口訣✖學習難度分級✖補充ES6小知識
這篇開始之前,一波三折。我一直在想秀出展點資料,資料來源從何而來?
如果自己建置資料庫、寫Query,再自己建API,過程可能有點繁瑣,
而且後端並不是這一系列文章要講的重點。
於是乎,找了Google API來取得資料,可是我直接被同源政策給打趴 (躺
,以往面對CORS的解決辦法都是透過後端proxy取得資料後再回應給前端,
可是如果後端寫一段proxy程式不就又都在講後端程式了嗎?
然而,找了各種前端解決CORS的辦法都不奏效,研究半天VSCode Live Server設定,
亂改config,試圖找到設定CORS的方法依舊沒有成功(汗,
山不轉路轉,最後決定直接把json資料載下來,用我大前端讀取資料吧?!
學藝不精,如果有高手知道有什麼只靠前端或是VSCode設定可以解決的辦法, 拜託教教我!

↑ 持續困擾我的同源政策
這次使用的API為place nearbysearch,輸入中心點座標、半徑、查詢類別、關鍵字,即可取得Google資料庫中,相符的json資料。
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=[緯度],[經度]&radius=[半徑m]&type=[查詢類別]&keyword=[搜尋關鍵字]%key=[yourkey]
Google API 範例: Place Search隨便來查個日料~
↑ json資料格式如下
    <div id="div_upload">
        <input type="file" id="upload" />
    </div>
    <div id="hmap"></div>
        var data;
        var inputFile = document.getElementById("upload");
        function LoadJSON(callback) {
            var file = inputFile.files[0];  // 預設只上傳一個檔案
            var reader = new FileReader();
            console.log(`檔案名稱: ${file.name},檔案大小: ${file.size}。`);
            reader.readAsText(file);
            reader.onload = function () {
                let result = JSON.parse(this.result);
                data = result;
            };
        }
        inputFile.addEventListener("change", LoadJSON);  
↑ 利用JS取得dom後,呼叫FileReader的readAsText方法,即可解讀json檔案。新增change事件於檔案上傳時觸發讀取json。
        var platform = new H.service.Platform({
            'apikey': yourkey
        });
        var defaultLayers = platform.createDefaultLayers();
        var HMap = new H.Map(
            document.getElementById('hmap'),
            defaultLayers.vector.normal.map,
            {
                zoom: 7,
                center: { lat: 23.5, lng: 121 },
                pixelRatio: 1
            });
        var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(HMap));
        var ui = H.ui.UI.createDefault(HMap, defaultLayers);
↑ 初始化地圖, 注意!Here API初始化時需呼叫H.mapevents.Behavior,方能開啟地圖事件,不然可能連地圖縮放都不能呦!
        var marker = new H.map.Marker({ lat: 23.8567, lng: 121.3508 });
        HMap.addObject(marker);
↑ 新增marker,並將它加入地圖中。
        marker.setData('<div class="infoWindow">Hi,我是資訊視窗!</div>');
        marker.addEventListener('tap', function (evt) {
            var bubble = new H.ui.InfoBubble(evt.target.getGeometry(), {
                content: evt.target.getData()
            });
            ui.addBubble(bubble);
        });
↑ 新增資訊視窗。在Here API中,資訊視窗名為InfoBubble,很特別的是,'click'事件用'tap'事件來代替,如果寫click事件會沒反應。
讓我們把json上傳跟秀出展點結合吧!
         var OrganizeData = (data) => {
            let arr = data.results.map((item) => {
                return {
                    x: item.geometry.location.lng,
                    y: item.geometry.location.lat,
                    name: item.name,
                    icon: item.icon,
                    photo: item.photos[0].html_attributions[0],
                    address: item.vicinity
                }
            });
            return arr;
        }
↑ 用console.log(arguments)觀察資料格式,找到想要取出的資料,並用Array.prototype.map方法進行重組陣列。
        var ShowMultiPoint = (dataList = [], map) => {
            if (dataList.length > 0) {
                dataList.forEach(item => {
                    let marker = new H.map.Marker({ lat: item.y, lng: item.x });
                    map.addObject(marker);
                    marker.setData(`<div class="infoWindow">
                        <h2>${item.name}</h2>
                        <p>經度: ${item.x}</p>
                        <p>緯度: ${item.y}</p>
                        <p>地址: ${item.address}</p>
                    </div>
                `);
                    marker.addEventListener('tap', (evt) => {
                        let bubble = new H.ui.InfoBubble(evt.target.getGeometry(), {
                            content: evt.target.getData()
                        });
                        ui.addBubble(bubble);
                    });
                });
            }
        }
↑ 新增ShowMultiPoint,把點資料陣列的每筆資料展點並且給予每個點資訊視窗以及事件。
        ShowMultiPoint(OrganizeData(data), HMap);
↑ 呼叫

↑ 結果如上,可以找出101附近不錯的餐廳啦!大家快去吃爆!
大家有沒有很好奇?為什麼我們監聽事件內的區域變數可以在外部環境存取呢?
區域可以存取全域?有這種事?
明天,會介紹JS的作用域所面臨的問題以及他們的解決方法。
敬請期待~