---
# System prepended metadata

title: 初探 Google Maps API

---

# 初探 Google Maps API

當專案需要用到地圖時，應該首選都是 Google Map 提供的 API 服務。本文會先從 API 的介紹開始，慢慢進入如何應用到專案，接著詳細介紹在專案中使用到的 API，最後以一個概論作結。

## Google Maps JavaScript API V3 介紹

因應不同的需求，主要可以分成七大類 API：

| API                                                                                                                                              | 說明                                                                                                                                   | 範例                                 |
| ------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| [❶Maps](https://developers.google.com/maps/documentation/javascript/reference/map)                                                               | 顧名思義就是呼喚出那張地圖時所要使用的。自訂地圖樣式等。                                                                               | ![](https://i.imgur.com/nWprZ4w.png) |
| [❷Drawing on the map](https://developers.google.com/maps/documentation/javascript/reference/marker)                                              | 想在地圖上顯示出指定地點的 Marker 或甚至點擊 Marker 後要跳出一個 Info Window 嗎？內容都在這裡。                                        | ![](https://i.imgur.com/ioUFTSI.png) |
| [❸Street View](https://developers.google.com/maps/documentation/javascript/reference/street-view)                                                | 街景服務                                                                                                                               | 略                                   |
| [❹Places](https://developers.google.com/maps/documentation/javascript/reference/places-widget)                                                   | 取得地點詳細資訊、經緯度變換地點都得用這組 API，也是本次會`重點介紹的項目`！                                                           | 詳見文章以下介紹                     |
| [❺Routes](https://developers.google.com/maps/documentation/javascript/reference/directions)                                                      | 導航路線相關。                                                                                                                         | 略                                   |
| [❻Local Context (beta)](https://developers.google.com/maps/documentation/javascript/reference/local-context-map-view#LocalContextMapViewOptions) | Local Context 將地圖、路徑規劃、地點 (Maps, Routes, Places) 功能，透過一支 API 全部整合，一次提供 3 種功能，似乎很讚，但還在 beta 中。 | 略                                   |
| [❼Journey Sharing (beta)](https://developers.google.com/maps/documentation/javascript/reference/journey-sharing-map-view)                        | 顧名思義就是呼喚出那張地圖時所要使用的。自訂地圖樣式等。                                                                               | 略                                   |

在之前接觸的專案中，主要使用的主要是 ❶、❷，也就是使用 Google 地圖，並把相關店家的資訊（例如車咕嚕中洗車場地點）放置 `Marker` 在地圖上，點擊後會跳出店家詳細資訊及預約的 `Info Window`。
而本文主要想著重介紹的是 ❹，也是最近偉士牌需求中的購車頁優化，會需要依照使用者的 input 去移動到對應的城市，並顯示城市的名稱，其實就是模擬 Google Map 的搜尋功能，但實際做起來需要熟悉 API 的混用。

## 需求

1. 輸入 Enter 後將地圖定位至該縣市／區域並在列表顯示該搜尋區域**所在縣市**的名稱及所有分店。
1. 移動地圖至其他縣市時，地圖：顯示可見地區的分店地標；列表：顯示地圖中心**所在縣市**的所有分店

我們來拆解一下這個需求的實作步驟看看。

### 需求 ❶ 拆解 — [影片](https://drive.google.com/file/d/1wqfCXx_kfyBHZPa1c4wtlrocI-saMhrh/view?usp=sharing)

1. 使用者輸入欲搜尋的區域，例如「豐原」、「綠島」
2. 把這個 input 傳給 API，得到 response
3. response 應該就有郵遞區號，直接用這個去 mapping 城市表找出豐原是在臺中市；綠島是在臺東縣
4. 地圖上呈現以豐原為中心的畫面、列表上印出臺中市

### 需求 ❷ 拆解 — [影片](https://drive.google.com/file/d/1O1sOXF4bphrm2tjHh0mSXQAcW62xAU4x/view?usp=sharing)

1. 使用者從 A 縣市拖曳到 B 縣市
2. 監聽 DragEnd，發現地圖中心有改變就取得現在中心的經緯度丟到 API
3. 從 response 整理出目前是在哪一個縣市
4. 畫面改變

## 實作

首先，遵循 [guide](https://developers.google.com/maps/documentation/javascript/get-api-key) 取得一組 API Key。
由於 Google Maps Platform 只提供原生 JS 或 TS，我們可以直接使用好心人士包裝成 [React 專用的套件](https://www.npmjs.com/package/@react-google-maps/api)。

### 基礎建設

**※ 程式碼會省略 input 的 component**

```javascript=
import React, { useState } from 'react'
import { GoogleMap, useLoadScript } from '@react-google-maps/api'

const CustomGoogleMap = () => {
  // 載入 Places API 所需要的 libraries
  const [libraries] = useState(['places'])

  const { isLoaded, loadError } = useLoadScript({
    googleMapsApiKey: GOOGLE_MAP_API_KEY,
    libraries,
  })

  // 定義地圖的 style、各種控制等
  const renderMap = () => {
    const options = {
      disableDefaultUI: true,
      zoomControl: true,
      scaleControl: true,
      styles: mapStyle,
    }

   //todo1 處理使用者輸入的資料，實現需求❶
   //todo2：處理拖曳結束後的事件，實現需求❷

    return (
      <>
        <GoogleMap
          mapContainerStyle={{
            width: '100%',
            height: '320px',
          }}
          center={center}
          zoom={12}
          options={options}
          onLoad={handleLoad}
          onDragEnd={handleCenterChanged}
        />
      </>
    )
  }

  if (loadError) {
    return <h1>Map cannot be loaded right now, sorry.</h1>
  }

  return isLoaded ? renderMap() : null
}

```

<!-- ![](https://i.imgur.com/JblBhuc.png) -->

### 需求 ❶

做好前置作業後，來實作需求 ❶ 吧！這邊我們需要運用到 Places 裡 [Autocomplete()](https://developers.google.com/maps/documentation/javascript/reference/places-autocomplete-service) 來取得預測值，接著再將這個值丟給 [Geocoder()](https://developers.google.com/maps/documentation/javascript/reference/geocoder) 轉換成經緯度，才能夠將中心點設為 input 的區域。如下圖 1：

```javascript=
// 處理使用者輸入的資料，實現需求❶
const handleKeyPress = event => {
	if (event.key === 'Enter') {
		const maps = window.google.maps;
		const sessionToken = new maps.places.AutocompleteSessionToken();
		const service = new maps.places.AutocompleteService();
		const request = {
			input: userInput,
			sessionToken,
			language: 'zh-TW', // 限定回傳語言為臺灣繁體中文
			types: [
				'administrative_area_level_1',
				'administrative_area_level_2',
				'administrative_area_level_3',
			], // 限定回傳區域為 1~3 級行政區
		};
		service.getPlacePredictions(request, predictions => {
			const geocoder = new window.google.maps.Geocoder();
			geocoder.geocode({ placeId: predictions[0].place_id }, responses => {
				// 取得 input data 的經緯度後將地圖中間設為該值
				setCenter({
					lat: responses[0].geometry.location.lat(),
					lng: responses[0].geometry.location.lng(),
				});

				// 取得郵遞區號以用來 mapping 城市，例如 110 則對應到臺北市信義區
				setZipCode(responses[0].address_components.slice(-1)[0].long_name);
			});
		});
		// 使用 webview 開啟時需再按下 enter 後使用 blur() 以讓 portable device 鍵盤自動收起
		event.target.blur();
	}
};
```

<!-- ![1](https://i.imgur.com/HAZuMTH.png) -->

這樣第一個需求就完成囉～可以回顧上面的影片。

### 需求 ❷

做完第一項之後，這個就很好理解了！直接看 code。

```javascript=
const handleCenterChanged = () => {
	// ❶將拖曳結束後的中心點放到 Geocoder(） 取得經緯度
	// ❷將資料處理後得到郵遞區號去做城市 mapping
};

//....

<GoogleMap onDragEnd={handleCenterChanged}>
```

<!-- ![](https://i.imgur.com/eQLlQe6.png) -->

🎉🎉🎉🎉🎉 完成囉！ 🎉🎉🎉🎉🎉

## 可以再優化的部分

- 當 response 沒有回傳郵遞區號時，該如何去找到對應的城市？
- 如果使用者輸入非 1~3 級行政區（例如：陽明山），是否需提示 Alert？

## 結語

當初這個功能其實摸索了很久，主要在於 Google ~~很有商業頭腦地~~把每一隻 API 回傳的 data 區分的很細，導致你要去組合兩三隻才能夠得到你要的結果。

每一隻 API 都是[分開計費](https://mapsplatform.google.com/intl/ja_ALL/pricing/)的！如有專案需求可能會需要在開發前與 PM 討論，確認客戶接不接受收費方式，畢竟如果每一個拖曳都要打 API，即使有豐沛的免費額度也不得不注意～

## 參考資料

> [Google Maps JavaScript API V3 Reference](https://developers.google.com/maps/documentation/javascript/reference) > [Places Autocomplete Service](https://developers.google.com/maps/documentation/javascript/reference/places-autocomplete-service) > [Geocoder](https://developers.google.com/maps/documentation/javascript/reference/geocoder) > [@react-google-maps/api](https://www.npmjs.com/package/@react-google-maps/api)

**PS: 以上程式碼如果凌亂或不符合大家的 code 標準請多多指教 🙏**
