fbpx

為了準備 Bootcamp 畢業前的 Final Project — 製作類似訂餐平台 — 開始接觸和學習 Google Map API,上一篇筆記是從零開始學習,並在 Vue.js 中嘗試使用,透過 Maps JavaScript API 服務實作地圖、地標、訊息視窗功能 — 最終將不同行政區的數間假餐廳展示在地圖上,提升使用者體驗 — 本篇將筆記使用 Maps JavaScript API 的另一項功能「Geocoding Service」


認識 Geocoding

Geocoding 服務能協助將地址轉換成經緯度座標;相反的,若想將座標轉換成地址,則可以使用 Reverse Geocoding 服務

專案預期功能

  1. 餐廳業主可以在表單輸入餐廳名稱與地址
  2. 透過 Geocoding 服務將餐廳地址轉換成經緯度座標
  3. 最終運用上一篇建好的「GMap 元件」將餐廳顯示在地圖上

初始化專案

由於是使用 Maps JavaScript API,專案的建立與初始化與上一篇筆記大同小異,大致流程如下,詳細的內容可以參考上一篇。前期唯一的差異在於本篇使用的 CSS Framework 為 Materialize CSS 而非 Bootstrap,設定會有些差異:

  1. 前往 Google Map Platform 建立新專案:勾選 Maps 服務
  2. 取得專案 API Key:在 index.html 載入 Maps JavaScript API 時放入使用
  3. 在 index.html 中載入 Materialize CSS CDN

熟悉 Geocoding 服務操作

在正式開始實作以前,先熟悉一下如何使用 Geocoding 的功能,更詳細的內容也可以參考 👉 官方文件

使用 Geocoding 服務

在 js 檔案中,透過 google.maps.Geocoder() 物件建構子建立一個geocoder 物件實例,之後就能透過 geocoder 使用 Geocoding 的功能:

https://pse.is/3esl8q

準備要轉換的地址與設定

將要轉換的地址與相關設定放在一個名為 GeocoderRequest 的物件當中 — 需要注意的是,由於 Google Maps API 根據 address 所搜尋到的地點可能不只一個結果,因此能透過其他三個屬性限制或影響搜尋結果:

  • componentRestrictions 屬性:限制搜尋的結果「必須」要符合否些條件,例如搜尋結果必須是特定國家和特定郵遞區號
  • bounds /regoin 屬性:影響搜尋結果「傾向」以設定的範圍或地區為優先
// GeocoderRequest 物件
{
 address: string,
 bounds: LatLngBounds,
 componentRestrictions: GeocoderComponentRestrictions,
 region: string
}

將地址轉為座標

使用geocoder 物件實例上的 geocode() 方法請 Google Maps API 協助將地址轉換成經緯度座標 — 需要提供 (1) GeocoderRequest 物件(2) 一個讓 Google Maps API 在取得結果後執行的 Callback 函式:

geocoder.geocode(GeocoderRequest, callback 函式)

處理回傳結果

Google Maps API 在取得轉換的結果後,將執行 Callback 函式,並帶入以下兩個參數 — 根據 status 能判定轉換是否成功,若成功則可以運用 results 資料;若失敗則依據 status 告知使用者相對應的錯誤訊息:

  1. results:根據地址轉換成經緯度的單筆或多筆結果
  2. status:執行結果的狀態訊息

截至目前為止的程式碼

// 建立 geocoder 物件實例
const geocoder = new google.maps.Geocoder();

// 請 Google Maps API 依據地址轉換成經緯度
geocoder.geocode( { 'address': address}, function(results, status) {
  if (status == 'OK') {
    // 若轉換成功...
  } else {
    // 若轉換失敗...
    console.log(status)
  }
});

 


實作專案

認識完 Geocoding 的使用方式後,接著就開始在 Vue.js 當中使用了

1. 實作表單

首先要建立的是一張表單,讓店家主可以輸入餐廳名稱與地址

  1. 在 views 資料夾中建立一個名為 NewRestaurant.vue 的新檔案
  2. <template>  中建立一張表單:設置名稱與地址欄位和送出按鈕
  3. 建立 geocode()方法:將會處理地址轉換成經緯度座標的功能
  4. 在表單上監聽表單送出事件:將觸發 geocode() 方法轉換地址
<template>
  <div class="container">
    <div class="row">
      <div class="card col s6 offset-s3">
        <form class="card-content" @submit.prevent="geocode">
          <h5 class="center">餐廳資訊</h5>
          <div class="row">
            <div class="input-field col s12">
              <label for="name">餐廳名稱</label>
              <input type="text" id="name" v-model="restaurant.name" />
            </div>
            <div class="input-field col s12">
              <label for="address">餐廳地址</label>
              <input type="text" id="address" v-model="restaurant.address" />
            </div>
            <!--顯示錯誤訊息-->
            <p v-if="errormsg" class="center red-text">{{errormsg}}</p>
            <div class="input-field col s12 center">
              <button type="submit" class="btn waves-light waves-effect">submit</button>
            </div>
          </div>
        </form>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      restaurant: {
        address: "",
        name: ""
      },
      errormsg: null,
    };
  }
  methods: {
    geocode() {
      // 處理表單送出後步驟
    }
  }
};
</script>

 

2. 將地址轉換為經緯度座標

  1. 建立一個 initGeocoder() 方法 — 透過 google.maps.Geocoder() 物件建構子建立一個geocoder 物件實例,並在元件 mounted() 階段就先呼叫,把 geocoder 物件實例先準備好。
  2. 當監聽到使用者送出表單事件時,再透過 geocode()方法,取得使用者輸入的地址,請 Google Maps API 轉換為座標經緯度
  3. 當轉換成功時,將第一筆結果的經緯度存取起來;失敗時則顯示錯誤原因
  4. 根據轉換結果,更改 hasGeo 狀態:true 為已成功獲得經緯度、false 則為未成功獲得經緯度
<script>
export default {
  data() {
    return {
      restaurant: {
        address: "",
        name: "",
        // 用來存放餐廳經緯度
        lat: "",
        lng: ""
      },
      errormsg: null,
      // 存放 geocoder 物件實例
      geocoder: null,
      // 存放目前是否已獲得座標的狀態
      hasGeo: false,
    };
  },
  mounted() {
    this.initGeocoder();
  },
  methods: {
    // 透過 google.maps.Geocoder() 物件建構子建立一個 geocoder 物件實例
    initGeocoder() {
      this.geocoder = new google.maps.Geocoder();
    },
    geocode() {
      // 取得餐廳地址
      const address = this.restaurant.address;
      this.geocoder.geocode(
        // GeocoderRequest 物件: 帶入要轉換的地址與相關設定
        {
          // 地址
          address: address,
          // 限制轉換的結果必須是在台灣的範圍
          componentRestrictions: {
            country: "TW"
          }
        },
        // 轉換完成後呼叫的 callback 函式
        (results, status) => {
          if (status === "OK") {
            // 當轉換成功時,將第一筆結果的經緯度存取起來
            this.restaurant.lat = results[0].geometry.location.lat();
            this.restaurant.lng = results[0].geometry.location.lng();
            // 更新狀態為已經獲得經緯度
            this.hasGeo = true;
          } else {
            // 更新狀態為未經獲得經緯度
            this.hasGeo = false;
            // 當轉換失敗時,顯示錯誤原因
            this.errormsg = status;
          }
        }
      );
    }
  }
};
</script>

❗️回傳內容

成功轉換時,回傳的內容可能包含一筆或多筆結果,如有多筆資料時,越符合的結果排序越前面,每筆結果中可能帶有以下資料,詳細回傳結果可以參考 👉官方文件:

1. formatted_address: 通常和郵件地址相同

2. place_id: 一個地點所對應的獨特 ID,可在其他 Google API服務中使用

3. geometry: 其中的 location 屬性帶有包含轉換後的經緯度結果的

4. partial_match: 一個布林值,代表地址可能有誤,因此在只能成功轉換部分的地址

3. 呈現經緯度座標

<template>  當中,表單下方設計一個區塊:在表單送出,且收到 Google Maps API 回傳經緯度座標後 — hasGeo 狀態為 true 時 — 將餐廳名稱和座標呈現在此區塊供業主確認:

<template>
  <div class="container">
    <div class="row">
      <div class="card col s6 offset-s3">
        <form class="card-content" @submit.prevent="geocode">
          <h5 class="center">餐廳資訊</h5>
          <div class="row">
            <div class="input-field col s12">
              <label for="name">餐廳名稱</label>
              <input type="text" id="name" v-model="restaurant.name" />
            </div>
            <div class="input-field col s12">
              <label for="address">餐廳地址</label>
              <input type="text" id="address" v-model="restaurant.address" />
            </div>
            <!--顯示錯誤訊息-->
            <p v-if="errormsg" class="center red-text">{{errormsg}}</p>
            <div class="input-field col s12 center">
              <button type="submit" class="btn waves-light waves-effect">submit</button>
            </div>
          </div>
        </form>
      </div>
      <!--當成功獲得經緯度時,顯示餐廳資訊及經緯度資訊-->
      <div class="card col s6 offset-s3" v-if="hasGeo">
        <div class="card-content">
          <h5 class="center">確認資訊</h5>
        </div>
        <div class="card-action">
          <p class="grey-text">餐廳名稱:{{restaurant.name}}</p>
          <p class="grey-text">餐廳緯度: {{restaurant.lat}}</p>
          <p class="grey-text">餐廳經度: {{restaurant.lng}}</p>
        </div>
      </div>
    </div>
  </div>
</template>

功能呈現

4. 將餐廳呈現在地圖上

一般人通常不會知道自己的店家的經緯度座標,因此若能將店家資訊呈現在地圖上,就能讓店家業主更方便確認位置的正確性:

引用已建立好的元件

由於在上一篇 Google Maps API 練習時已經實作過地圖、地標、訊息視窗,並且在文章最後將「地圖產生的功能」獨立成一個可被重複使用的元件,這邊就直接引用該元件來使用:

  1. 在 components 資料夾中建立一個 GMap.vue 檔案
  2. 前往👉 GitHub 專案資料夾 找到在上一篇已經製作好的元件
  3. 將程式碼複製到目前專案的 /components/GMap.vue 檔案中

根據坐標產生地圖

接著在 NewRestaurant.vue 檔案中的 :

  1. 引入 GMap 元件
  2. 在 components 屬性上註冊 GMap 元件

在 NewRestaurant.vue 檔案中的 :

  1. 呈現餐廳資訊的區塊擺放顯示地圖的 GMap 元件
  2. 將產生地圖、地標、訊息視窗所需的資料帶入 GMap 元件當中

<template>
  <div class="container">
    <div class="row">
      <div class="card col s6 offset-s3">
        <form class="card-content" @submit.prevent="geocode">
          <!--輸入餐廳資訊的表單...-->
        </form>
      </div>
      <div class="card col s6 offset-s3" v-if="hasGeo">
        <div class="card-content">
          <h5 class="center">確認資訊</h5>
        </div>
        <div class="card-action">
          <!--使用 GMap 元件,並將餐廳和設定資訊帶入-->
          <GMap
            :center="{lat: restaurant.lat, lng: restaurant.lng}"
            :restaurants="[restaurant]"
            :streetViewControl="false"
            :mapTypeControl="false"
            :fullscreenControl="true"
            :zoomControl="true"
          ></GMap>
        </div>
        <div class="card-action">
          <p class="grey-text">餐廳名稱:{{restaurant.name}}</p>
          <p class="grey-text">餐廳緯度: {{restaurant.lat}}</p>
          <p class="grey-text">餐廳經度: {{restaurant.lng}}</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
// 引入 GMap 元件
import GMap from "../components/GMap";
export default {
  components: {
    GMap
  },
  data() {
    return {
      restaurant: {
        address: "",
        name: "",
        lat: 25.0325917,
        lng: 121.562499
      },
      hasGeo: false,
      errormsg: null,
      geocoder: null
    };
  },
  mounted() {
    this.initGeocoder();
  },
  methods: {
    initGeocoder() {
      this.geocoder = new google.maps.Geocoder();
    },
    geocode() {
      // 轉換地址為最標...
    }
  }
};
</script>

<style scoped>
/* 為地圖增加寬度和高度 */
.google-map {
  width: 100%;
  height: 300px;
}
</style>

功能呈現

 

結語

本篇因為是延續前一篇實作,因此直接使用 Maps JavaScript API 當中地址轉地標的 Geocoding Service 功能。若單純想透過地址轉換地標,並運用回傳的資料時,也可以直接使用 Geocoding web service 服務,在相同 $200 美金的額度下,可以載入的次數也比較多。

上一篇學習和實作了地圖、地標、訊息視窗,本篇的練習則專注在使用 Geocoding 的服務 — 模擬將餐廳業者所提供的地址轉換成地標,並將獲取的地標呈現在地圖上。非常期待接下來能將這兩次所學習和實作的功能運用在 Final Project 上!


專案資料夾

本篇完成的功能與程式碼將一併上傳至 GitHub 以供參考!

好文轉自作者Mike Huang–[筆記] 從零接觸 Google Map API 2:在 Vue.js 中使用Geocoding將地址轉換成地標

如果你喜歡他的文章,歡迎回到他的Medium【麥克的半路出家筆記】看更多。如果你想要知道更多關於Vue.js課程,快樂學程式也推出了實作專案在此

如果你的入門還在單打獨鬥,歡迎來到快樂學程式找到志同道合的夥伴,你的自學之路不孤單。

Leave a Reply