建立屬於你的 Google Map 地圖標記(三) - 地址輸入與座標取得


Posted by mumu892101 on 2022-08-09

當我們有了地圖元件後,要怎麼讓標記渲染在指定位置呢?從上一篇文章就可以知道,在地圖上要指定中心點,必須依賴座標;所以在地圖標記的設定上,也是要指定一個座標,然後放置標記。



取得地址座標

那麼該如何取得指定地址的座標,這篇文章會提到使用 Google Map 中的 Geocoding APIplaces API 執行以下需求:

輸入地址執行搜尋

用戶輸入地址完畢後,直接按下搜尋按鈕,該地址透過 Geocoding API 的 geocode 搜尋後如果收到回傳 status GeocoderStatus.OK === true ,則能取得回傳的座標,相反地則是搜尋不成功,前端可以顯示錯誤訊息在頁面上。

輸入部分地址,由 Google Map API 提供選項

用戶輸入部分地址,透過 debounce 判斷使用者停止輸入後,透過 places API 的 AutocompleteService 中的 getPlacePredictions,提供使用者最多五項關於該輸入地址的預測資料,如果收到回傳 places.PlacesServiceStatus.OK === true,則顯示選項給使用者,反之則不提供選項,使用者可以選擇重新調整內容或透過搜尋按鈕執行搜尋功能。



輸入地址執行搜尋

這邊參考了Geocoder 文件 ,要 new 一個 instance 來進行操作:
const geocoder = new mapApi.Geocoder()
接下來文件中提到,使用 geocode method 可以進行相關操作,第一個參數是 request,第二個參數是 callback,
request 的部分,因為需求是地址查找,所以帶入 address 參數。
而回傳的 callback 中也帶色兩個參數,一個是回傳的 result, 另外則是 透過常數表示狀態的 status,在這邊有有效回應的 status 是 OK,其他則是不同情況下的錯誤或無效狀態,因此我們判斷當 status === mapApi.GeocoderStatus.OK 時,去取得回傳的 result,透過當中的座標渲染標記,並且將座標和 place_id 存於 state 中,當作表單資料的一部分。
相反的,如果 status !== mapApi.GeocoderStatus.OK ,則顯示錯誤訊息於頁面上。

  const handleSearch = useCallback(() => {
    const geocoder = new mapApi.Geocoder()
    geocoder.geocode(
      { address: addressInputRef.current.value },
      (results, status) => {
        if (status === mapApi.GeocoderStatus.OK) {
          const { location } = results[0].geometry
          mapInstance.setCenter(location)
          renderMarker(mapInstance, mapApi, location, isMarker)
          setCoordsResult({
            lat: location.lat(),
            lng: location.lng()
          })
          setPlaceId(results[0].place_id)
          return setIsMarker(true)
        }
        return setAddressErrMsg('請輸入有效地址') // 錯誤訊息設定
      }
    )
  }, [isMarker, mapApi, mapInstance, renderMarker])


輸入部分地址,由 Google Map API 提供選項


在這個功能,需要記錄的有兩個部分,第一個是使用 places API 的 Places Autocomplete Service,然後透過其中 getPlacePredictions 的 method,同樣透過第一個 request 參數和第二個 callback 參數進行操作。

  const handleAutocomplete = useCallback(() => {
      const service = new mapApi.places.AutocompleteService()
      const request = {
        input: addressSearchInput
      }
      service.getPlacePredictions(request, (results, status) => {
        if (status === mapApi.places.PlacesServiceStatus.OK) {
          return setAutoResults(results)
        } 
        console.log('自動完成沒有結果')
        return setAutoResults(null)
      })
    }
  , [mapApiLoaded, addressSearchInput, mapApi, isSearched])

而為了避免在輸入途中就觸發地點自動完成功能,多次發送還不具意義的字串進行請求,讓 Google-Map-API 的請求次數過多,這邊使用 debounce(去抖動)的處理方式,判斷當使用者停止進行相同行為經過一定時間後,才觸發指定功能。
可以看到 handleInput 當中,設定 700 毫秒為時間單位,當未達時間內使用者持續觸發輸入行為,會刷新計時器,當停止達到 700 毫秒,才會將 ref value 存於 addressSearchInput state,並觸發 useEffect 依賴條件更改,執行 handleAutocomplete function。

  const handleInput = debounce(() => 
    setAddressSearchInput(addressInputRef.current.value)
  , 700}

  const onInputChange = useCallback(
    (e) => {
      handleInput()
      setValue('storeAddress', e.target.value) // react-hook-form
    },
    [setValue, handleInput]
  )

  useEffect(() => {
    // 非搜尋狀態且有地址搜尋字詞時,執行自動完成功能
    if (addressSearchInput && !isSearched) handleAutocomplete()

    // 清空input欄位時,視為非搜尋狀態並清空自動搜尋結果
    if (!addressInputRef?.current?.value) {
      setAutoResults(null)
      setIsSearched(false)
    }
  }, [addressSearchInput, handleAutocomplete, isSearched])

這樣我們就完成兩種透過地址搜索的功能,接下來將紀錄如何將得到的座標資料渲染成標記在指定的位置上,並且透過拖曳和點擊可以重新渲染標記,取得新的座標和 placeId。


#React #GoogleMap #Google-Maps-APIs #google-map-react #React Hook







Related Posts

Fibonacci sequence

Fibonacci sequence

Vue.js 學習旅程Mile 12 – Conditional Rendering 條件渲染篇:v-if、v-show

Vue.js 學習旅程Mile 12 – Conditional Rendering 條件渲染篇:v-if、v-show

 網站部署 --AWS EC2 雲端主機 + LAMP Server + phpMyAdmin

網站部署 --AWS EC2 雲端主機 + LAMP Server + phpMyAdmin


Comments