iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 6
1
Modern Web

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

[3-1] 打造你的美食地圖!用Here Maps API 秀出Google API餐廳資訊

本系列文章已出版實體書籍:
「你的地圖會說話?WebGIS 與 JavaScript 的情感交織」(博碩文化)
WebGIS啟蒙首選✖五家地圖API✖近百個程式範例✖實用簡易口訣✖學習難度分級✖補充ES6小知識

CORS 一波三折

這篇開始之前,一波三折。我一直在想秀出展點資料,資料來源從何而來?
如果自己建置資料庫、寫Query,再自己建API,過程可能有點繁瑣,
而且後端並不是這一系列文章要講的重點。

於是乎,找了Google API來取得資料,可是我直接被同源政策給打趴 (躺
,以往面對CORS的解決辦法都是透過後端proxy取得資料後再回應給前端,
可是如果後端寫一段proxy程式不就又都在講後端程式了嗎?

然而,找了各種前端解決CORS的辦法都不奏效,研究半天VSCode Live Server設定,
亂改config,試圖找到設定CORS的方法依舊沒有成功(汗,
山不轉路轉,最後決定直接把json資料載下來,用我大前端讀取資料吧?!

學藝不精,如果有高手知道有什麼只靠前端或是VSCode設定可以解決的辦法, 拜託教教我!

https://ithelp.ithome.com.tw/upload/images/20200921/20130604cEs1z0Ql4k.jpg
↑ 持續困擾我的同源政策

Google Map API 環域查詢

這次使用的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
隨便來查個日料~
https://ithelp.ithome.com.tw/upload/images/20200921/201306048JrKYGa5st.jpg
↑ json資料格式如下

  • 建個div,檔案上傳以及地圖。

    <div id="div_upload">
        <input type="file" id="upload" />
    </div>
    <div id="hmap"></div>

json檔案上傳

        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。

Here Maps API Marker

        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上傳 & 展點

讓我們把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);

↑ 呼叫

https://ithelp.ithome.com.tw/upload/images/20200921/20130604ODd2971L5Q.jpg
↑ 結果如上,可以找出101附近不錯的餐廳啦!大家快去吃爆!


今日重點

  • Google Map API place nearbysearch
  • 檔案上傳前端讀取json格式資料
  • Here Maps API 地標、展點及資訊視窗

大家有沒有很好奇?為什麼我們監聽事件內的區域變數可以在外部環境存取呢?
區域可以存取全域?有這種事?
明天,會介紹JS的作用域所面臨的問題以及他們的解決方法。
敬請期待~/images/emoticon/emoticon12.gif


上一篇
[2-2] JS 的 Function X Arguments 與 ES6寫法介紹
下一篇
[3-2] Scope Chain & IIFE 問題與解法 - 以Here Maps API展點為例
系列文
《你的地圖會說話? WebGIS與JavaScript的情感交織》30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言