React(Next.js) と Google Map の Tips
こんにちは!この記事は GameWith アドベントカレンダー5日目の記事です!
React(Next.js) で Google Map を表示したり、いろいろ操作をしたのでまとめて行こうと思います
Google Map の表示
今回利用したのは google-map-react
というライブラリです
他にも google-maps-react
などといった似たような名前のライブラリなどもありますが、ダウンロード数やメンテナンスの状況などを見て、google-map-react
を選びました
表示するだけなら、defaultCenter
に表示したい緯度経度を渡せば簡単に表示ができます
import GoogleMapReact from 'google-map-react';
export default function Create() {
const defaultLatLng = {
lat: 35.7022589,
lng: 139.7744733,
};
return (
<div style={{ height: '300px', width: '300px' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: process.env.NEXT_PUBLIC_GOOGLE_MAP_KEY }}
defaultCenter={defaultLatLng}
defaultZoom={16}
/>
</div>
);
}
マーカーの表示
google-map-react
ではマーカーを表示する機能はないため、Google Map API を利用しマーカーを表示させます
まず、Google Map API を利用するために、onGoogleApiLoaded
を利用し、map
, maps
を取得します
その後、Marker
を利用しマーカーを表示させます
import GoogleMapReact from 'google-map-react';
export default function Create() {
const defaultLatLng = {
lat: 35.7022589,
lng: 139.7744733,
};
const handleApiLoaded = ({ map, maps }) => {
new maps.Marker({
map,
position: defaultLatLng,
});
};
return (
<div style={{ height: '300px', width: '300px' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: process.env.NEXT_PUBLIC_GOOGLE_MAP_KEY }}
defaultCenter={defaultLatLng}
defaultZoom={16}
onGoogleApiLoaded={handleApiLoaded}
/>
</div>
);
}
地図のクリックと緯度経度の取得
google-map-react
にクリックを取れる仕組みがあるので利用します
import GoogleMapReact from 'google-map-react';
export default function Create() {
const defaultLatLng = {
lat: 35.7022589,
lng: 139.7744733,
};
const setLatLng = ({ x, y, lat, lng, event }) => {
console.log(lat);
console.log(lng);
};
return (
<div style={{ height: '300px', width: '300px' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: process.env.NEXT_PUBLIC_GOOGLE_MAP_KEY }}
defaultCenter={defaultLatLng}
defaultZoom={16}
onClick={setLatLng}
/>
</div>
);
}
クリックした位置にマーカーを置く
map
, maps
, marker
を保持しておく必要があるので、useState
を利用します
クリック時に marker.setMap(null)
を実行することで、マーカーが無限に増えていくのを防止します(クリックした箇所全てにマーカーを置きたい場合は実行しないでください)
map.panTo(latLng)
は地図の中心を引数の緯度経度に移動させるために実行しています(オプションです
import { useState } from 'react';
import GoogleMapReact from 'google-map-react';
export default function Create() {
const [map, setMap] = useState(null);
const [maps, setMaps] = useState(null);
const [marker, setMarker] = useState(null);
const defaultLatLng = {
lat: 35.7022589,
lng: 139.7744733,
};
// map, maps で受け取ると変数が被るので object で受け取っています
const handleApiLoaded = (object) => {
setMap(object.map);
setMaps(object.maps);
};
const setLatLng = ({ x, y, lat, lng, event }) => {
if (marker) {
marker.setMap(null);
}
const latLng = {
lat,
lng,
};
setMarker(new maps.Marker({
map,
position: latLng,
}));
map.panTo(latLng);
};
return (
<div style={{ height: '300px', width: '300px' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: process.env.NEXT_PUBLIC_GOOGLE_MAP_KEY }}
defaultCenter={defaultLatLng}
defaultZoom={16}
onClick={setLatLng}
onGoogleApiLoaded={handleApiLoaded}
/>
</div>
);
}
複数マーカーの表示&画面内に収まるように表示
先にスクショを貼ります
このように複数マーカーを表示させ、全マーカーが画面内に収まるように自動的に調整し表示させます
LatLngBounds
を利用し、マーカー作成後 bounds.extend(marker.position)
を実行し、最後に map.fitBounds(bounds)
を利用して画面内に収まるように調整させます
import GoogleMapReact from 'google-map-react';
export default function Create() {
const defaultLatLng = {
lat: 35.7022589,
lng: 139.7744733,
};
const items = [
{
lat: 36.7022589,
lng: 140.7744733,
},
{
lat: 37.7022589,
lng: 141.7744733,
},
{
lat: 38.7022589,
lng: 142.7744733,
},
];
const handleApiLoaded = ({ map, maps }) => {
const bounds = new maps.LatLngBounds();
items.forEach((item) => {
const marker = new maps.Marker({
position: {
lat: item.lat,
lng: item.lng,
},
map,
});
bounds.extend(marker.position);
});
map.fitBounds(bounds);
};
return (
<div style={{ height: '300px', width: '300px' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: process.env.NEXT_PUBLIC_GOOGLE_MAP_KEY }}
defaultZoom={16}
defaultCenter={defaultLatLng}
onGoogleApiLoaded={handleApiLoaded}
/>
</div>
);
}
地名で検索
Geocoding Service を利用して地名の検索をします
Geocoder
を作成後、geocode
関数に address
を渡して検索を実行します
results
の先頭から緯度経度の情報を取り出して map.setCenter
を利用し中心に表示し、マーカーを表示させます
export default function Create() {
const [map, setMap] = useState(null);
const [maps, setMaps] = useState(null);
const [geocoder, setGeocoder] = useState(null);
const [address, setAddress] = useState(null);
const [marker, setMarker] = useState(null);
const defaultLatLng = {
lat: 35.7022589,
lng: 139.7744733,
};
const handleApiLoaded = (obj) => {
setMap(obj.map);
setMaps(obj.maps);
setGeocoder(new obj.maps.Geocoder());
};
const search = () => {
geocoder.geocode({
address,
}, (results, status) => {
if (status === maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
if (marker) {
marker.setMap(null);
}
setMarker(new maps.Marker({
map,
position: results[0].geometry.location,
}));
console.log(results[0].geometry.location.lat());
console.log(results[0].geometry.location.lng());
}
});
};
return (
<div>
<input type="text" onChange={(e) => setAddress(e.target.value)} />
<button type="button" onClick={search}>Search</button>
</div>
<div style={{ height: '300px', width: '300px' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: process.env.NEXT_PUBLIC_GOOGLE_MAP_KEY }}
defaultCenter={defaultLatLng}
defaultZoom={16}
onGoogleApiLoaded={handleApiLoaded}
/>
</div>
)
}
最後に
地図の操作は思ったより楽しいですね
他にも新しい Tips があれば随時追加していこうと思います!
Discussion
素晴らしい記事をありがとうございます!!大変参考になりました。
1点だけ、大変些細なことで申し訳ないのですが、複数マーカーの緯度経度が全て一緒になっているので、適当な値に直されたらより良いと思います。ご検討よろしくお願いします。
ご指摘ありがとうございます!
こちら修正させていただきました🙏
React に明るくないので原因がわかっていないですが、こちらのサンプルコードを参考にすると、以下の
key
の所でエラーが発生してしまいますね。発生しているエラーは下記のようになります。悩ましい。
Next.js のバージョンの違いで起こるのだろうか…