2 minutes
初探 Google Maps API
當專案需要用到地圖時,應該首選都是 Google Map 提供的 API 服務。本文會先從 API 的介紹開始,慢慢進入如何應用到專案,接著詳細介紹在專案中使用到的 API,最後以一個概論作結。
Google Maps JavaScript API V3 介紹
因應不同的需求,主要可以分成七大類 API:
| API | 說明 | 範例 |
|---|---|---|
| ❶Maps | 顧名思義就是呼喚出那張地圖時所要使用的。自訂地圖樣式等。 | ![]() |
| ❷Drawing on the map | 想在地圖上顯示出指定地點的 Marker 或甚至點擊 Marker 後要跳出一個 Info Window 嗎?內容都在這裡。 | ![]() |
| ❸Street View | 街景服務 | 略 |
| ❹Places | 取得地點詳細資訊、經緯度變換地點都得用這組 API,也是本次會重點介紹的項目! |
詳見文章以下介紹 |
| ❺Routes | 導航路線相關。 | 略 |
| ❻Local Context (beta) | Local Context 將地圖、路徑規劃、地點 (Maps, Routes, Places) 功能,透過一支 API 全部整合,一次提供 3 種功能,似乎很讚,但還在 beta 中。 | 略 |
| ❼Journey Sharing (beta) | 顧名思義就是呼喚出那張地圖時所要使用的。自訂地圖樣式等。 | 略 |
在之前接觸的專案中,主要使用的主要是 ❶、❷,也就是使用 Google 地圖,並把相關店家的資訊(例如車咕嚕中洗車場地點)放置 Marker 在地圖上,點擊後會跳出店家詳細資訊及預約的 Info Window。
而本文主要想著重介紹的是 ❹,也是最近偉士牌需求中的購車頁優化,會需要依照使用者的 input 去移動到對應的城市,並顯示城市的名稱,其實就是模擬 Google Map 的搜尋功能,但實際做起來需要熟悉 API 的混用。
需求
- 輸入 Enter 後將地圖定位至該縣市/區域並在列表顯示該搜尋區域所在縣市的名稱及所有分店。
- 移動地圖至其他縣市時,地圖:顯示可見地區的分店地標;列表:顯示地圖中心所在縣市的所有分店
我們來拆解一下這個需求的實作步驟看看。
需求 ❶ 拆解
- 使用者輸入欲搜尋的區域,例如「豐原」、「綠島」
- 把這個 input 傳給 API,得到 response
- response 應該就有郵遞區號,直接用這個去 mapping 城市表找出豐原是在臺中市;綠島是在臺東縣
- 地圖上呈現以豐原為中心的畫面、列表上印出臺中市
需求 ❷ 拆解
- 使用者從 A 縣市拖曳到 B 縣市
- 監聽 DragEnd,發現地圖中心有改變就取得現在中心的經緯度丟到 API
- 從 response 整理出目前是在哪一個縣市
- 畫面改變
實作
首先,遵循 guide 取得一組 API Key。 由於 Google Maps Platform 只提供原生 JS 或 TS,我們可以直接使用好心人士包裝成 React 專用的套件。
基礎建設
※ 程式碼會省略 input 的 component
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
}
需求 ❶
做好前置作業後,來實作需求 ❶ 吧!這邊我們需要運用到 Places 裡 Autocomplete() 來取得預測值,接著再將這個值丟給 Geocoder() 轉換成經緯度,才能夠將中心點設為 input 的區域。如下圖 1:
// 處理使用者輸入的資料,實現需求❶
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();
}
};
這樣第一個需求就完成囉~可以回顧上面的影片。
需求 ❷
做完第一項之後,這個就很好理解了!直接看 code。
const handleCenterChanged = () => {
// ❶將拖曳結束後的中心點放到 Geocoder() 取得經緯度
// ❷將資料處理後得到郵遞區號去做城市 mapping
};
//....
<GoogleMap onDragEnd={handleCenterChanged}>
🎉🎉🎉🎉🎉 完成囉! 🎉🎉🎉🎉🎉
可以再優化的部分
- 當 response 沒有回傳郵遞區號時,該如何去找到對應的城市?
- 如果使用者輸入非 1~3 級行政區(例如:陽明山),是否需提示 Alert?
結語
當初這個功能其實摸索了很久,主要在於 Google 很有商業頭腦地把每一隻 API 回傳的 data 區分的很細,導致你要去組合兩三隻才能夠得到你要的結果。
每一隻 API 都是分開計費的!如有專案需求可能會需要在開發前與 PM 討論,確認客戶接不接受收費方式,畢竟如果每一個拖曳都要打 API,即使有豐沛的免費額度也不得不注意~
參考資料
Google Maps JavaScript API V3 Reference > Places Autocomplete Service > Geocoder > @react-google-maps/api
PS: 以上程式碼如果凌亂或不符合大家的 code 標準請多多指教 🙏
2024-01-06 18:44

