Nextのページで、react-google-maps/apiを利用して、オーバーレイマップを作る
例によって、今回も備忘録です。
まず完成したものは、こちら
このようなネイティブアプリを作ろうと思い地図を作りましたが、データを一回載せてみてみたくて、自分のサイトに乗っけてみました。
ローカルでの実装はテンプレ通りすぐできます。が、ビルドするためにエラーを消すのに一苦労でした。
環境
Next.js, TypeScriptのサイト
Google Cloud PlatformでAPIを発行する
GCPでMaps JavaScript APIを発行し、Keyを.envに入れる
NEXT_PUBLIC_GOOGLE_MAP_API_KEY=コピったあなたのAPI Key
react-google-mapsで、実装したいモデルを探す
Reactのバージョンが新しいと使えるコンポネントが違うので、一応読みましょう。
さっきコピったAPIKeyを入れれば、サンプルを見れます。
今回は、古地図のデータをSVGで用意し、マップ上に張り付けて透過させるアプリを作るので、Ground Overlayを使用します。
Overlayを入れてみる
まずinstall
yarn add @react-google-maps/api
Componentを作りましょう
import React from 'react'
import { GoogleMap, LoadScriptNext, GroundOverlay } from '@react-google-maps/api'
const key = process.env.NEXT_PUBLIC_GOOGLE_MAP_API_KEY
// Google Mapを描画するサイズ
const containerStyle = {
width: '90vw',
height: '58vh',
}
// マップの中央(Google Mapでポイントすれば出てくる)
const center = {
lat: 40.513103, // 緯度
lng: 141.4897, // 経度
}
// オーバーレイする画像の右上と左下の点
const bounds = {
north: 40.52,
south: 40.4942,
east: 141.513105,
west: 141.4666
}
const OverlayMap = () => {
return (
<LoadScriptNext googleMapsApiKey={key}>
<GoogleMap
mapContainerStyle={containerStyle}
center={center}
zoom={16}
// マップのスタイル
options={{
gestureHandling: 'greedy',
streetViewControl: false,
fullscreenControl: false,
}}
>
<GroundOverlay
key={'url'}
url="載せたい画像のURL"
bounds={bounds}
opacity={0.5}
/>
</GoogleMap>
</LoadScriptNext>
)
}
おそらくこの時点で、localhostではオーバーレイが反映されてると思います。
ただboundにエラーが出てると思います。
とりあえず、無視して、地図の不透明度をスライダーで変える処理をしましょう。
react slider componentをinstallして、onChangeで<Overlay>に不透明度の値を渡す
yarn add @mui/material/Box @mui/material/Slider
import Box from '@mui/material/Box';
import Slider from '@mui/material/Slider';
const OverlayMap = () => {
const [inputValue, setInputValue] = React.useState(0.5)
const handleChange = (e) => {
setInputValue(e.target.value)
}
return (
<>
<LoadScriptNext googleMapsApiKey={key}>
<GoogleMap>
<GroundOverlay
key={'url'}
url="載せたい画像のURL"
bounds={bounds}
opacity={inputValue}
/>
</GoogleMap>
</LoadScriptNext>
<Box sx={{ width: '80vw' }}>
<Slider
aria-label="opacity"
defaultValue={0.5}
step={0.1}
marks
min={0.0}
max={1.0}
onChange={(e) => handleChange(e)}
/>
</Box>
</>
)
}
イベントハンドラーは、こちらを参考にしました。
これで、不透明度をスライダーで変更できたと思います。
では、ビルドするために、boundsのエラーを直しましょう。
boundsはgoogle.maps.LatLngとする
-const bounds = {
- north: 40.52,
- south: 40.4942,
- east: 141.513105,
- west: 141.4666
-}
+ const sw = new google.maps.LatLng(40.4942, 141.4666)
+ const ne = new google.maps.LatLng(40.52, 141.513105)
+ const bounds = new google.maps.LatLngBounds(sw, ne)
するとエラーメッセは消えました。
ではyarn buildしてみましょう。
google is not definedを解決する
あれ、、、
調べると、new google.map.LatLng()ではなく、
new window.google.map.LatLng()
としなきゃならないらしいです。
そしたらなんと、windowがnot definedと言われました。
これは、NextがSSRであって、サーバーサイドで処理するときに、windowというブラウザ側にしかないグローバルオブジェクトを参照しようとするからエラーが起こるようです。
なので、windowがundefinedであるときの処理をifで括ってしまいます。
window is not definedを解決
windowを用いる処理は全てif内に入れます。
const OverlayMap = () => {
const [inputValue, setInputValue] = React.useState(0.5)
const handleChange = (e) => {
}
const OverlayData = () => {
if (typeof window !== 'undefined') {
const sw = new window.google.maps.LatLng(40.4942, 141.4666)
const ne = new window.google.maps.LatLng(40.52, 141.513105)
const bounds = new window.google.maps.LatLngBounds(sw, ne)
return (
<GroundOverlay
key={'url'}
url="載せたい画像のURL"
bounds={bounds}
opacity={inputValue}
/>
)
}
}
return (
<div>
<LoadScriptNext googleMapsApiKey={key}>
<GoogleMap>
<OverlayData />
</GoogleMap>
</LoadScriptNext>
<p>古地図不透明度</p>
<Box>
</Box>
</div>
)
}
このように、関数の中に、windowのif文を定義した関数を入れ込みます。
<GroundOverlay>もwindowを参照するので、ifのreturnで返して、<OverlayData />として本文のJSXに入れます。
完成
これでyarn buildすれば、無事doneすると思います!
ただ、react-google-mapsは4年前から更新されてないので、ちゃんとした製品にするなら、違う方法のほうが無難かも。
githubはこちら。
Discussion