LoginSignup
5

More than 3 years have passed since last update.

posted at

updated at

Organization

Google Map

本日から3日間は外部APIや media element を利用した変わりダネを紹介します。今日は Google Map を埋め込み、現在地を表示するサンプルです。$ yarn 1219 しただけでは挙動確認が出来ないので、以下手順でセットアップしてからお試し下さい。
code: github / $ yarn 1219

1219-1.jpg 1219.jpg

setup

  • Maps JavaScript API の APIキーを取得
  • キーにアプリケーションの制限をかける(http://localhost:1234/など)
  • キーにAPIの制限をかける(Maps JavaScript API)
  • .env に GOOGLE_MAP_API_KEY=${YOUR_API_KEY} を記述

Google Map API を利用するにあたり、api_key が必要になります。環境変数から script tag に api_key を挿入しています。リポジトリルートに .env を作成し、取得した api_key を置きます。

GOOGLE_MAP_API_KEY=XXXXXX

サンプルには parcel を使用しており、react-async-script で 環境変数から script タグを生成(parcelの場合)

app.tsx
import * as React from 'react'
import { render } from 'react-dom'
import Root from './components/index'
// @ts-ignore
import makeAsyncScriptLoader from 'react-async-script'
const URL = `https://maps.googleapis.com/maps/api/js?key=${
  process.env.GOOGLE_MAP_API_KEY
}`
const AsyncScriptRoot = makeAsyncScriptLoader(URL)(Root)
render(<AsyncScriptRoot />, document.getElementById('app'))

進行状況に応じた View 分岐

geolocation API を利用し、ユーザーの現在位置を利用します。現在位置取得出来るまで、3つの View に分岐します。

components/index.tsx
export default () => (
  <GeolocationWrapper
    renderProcessView={() => <ProcessView />}
    renderSuccessView={coords => (
      <GoogleMapView coords={coords} />
    )}
    renderErrorView={() => <ErrorView />}
  />
)

navigator.geolocation.getCurrentPosition を call すると、現在位置取得の許可が求められます。現在位置もエラーも null なら進行中の View を、エラーがあればエラー View を、現在位置取得が出来たら GoogleMap API 利用の View をマウントします。

components/geolocationWrapper.tsx
export default (props: Props) => {
  const [coords, setCoords] = useState<Coordinates | null>(
    null
  )
  const [error, setError] = useState<PositionError | null>(
    null
  )
  useEffect(() => {
    navigator.geolocation.getCurrentPosition(
      ({ coords }) => {
        setCoords(coords)
      },
      setError
    )
  }, [])
  return (
    <>
      {useMemo(
        () => {
          if (coords === null) return
          return props.renderSuccessView(coords)
        },
        [coords]
      )}
      {useMemo(
        () => {
          if (error === null) return
          return props.renderErrorView(error)
        },
        [error]
      )}
      {useMemo(
        () => {
          if (!(error === null && coords === null)) return
          return props.renderProcessView()
        },
        [coords, error]
      )}
    </>
  )
}

useEffect と GoogleMap API

GoogleMap API に則り、マウント時に取得した現在位置 coords をもって API を叩きます。地図をマウントするタグは ref で参照し、API callback 時に useState の更新関数で再描画させます。

components/googleMapView.tsx
const View = (props: Props) => {
  const [addressLabel, setAddressLabel] = useState(
    '...取得しています'
  )
  const ref = useRef({} as HTMLDivElement)
  useEffect(
    () => {
      if (ref.current === null) return
      const { latitude, longitude } = props.coords // 取得した現在位置
      const center = { lat: latitude, lng: longitude }
      const mapOptions: google.maps.MapOptions = {
        center,
        zoom: 16
      }
      const map = new google.maps.Map(
        ref.current,
        mapOptions
      )
      const markerOptions: google.maps.MarkerOptions = {
        position: center,
        map
      }
      new google.maps.Marker(markerOptions)
      const geocoder = new google.maps.Geocoder()
      geocoder.geocode({ location: center }, function(
        results,
        status
      ) {
        if (status == google.maps.GeocoderStatus.OK) {
          setAddressLabel(results[1].formatted_address)
        } else {
          setAddressLabel('取得に失敗しました')
        }
      })
    },
    [props.coords]
  )
  return (
    <div className={props.className}>
      <div className="map" ref={ref} />
      <p className="addressLabel">
        現在住所
        {addressLabel}
      </p>
    </div>
  )
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
5