地図を見ている時に、移動しながら周りの情報を拾いたい時に、自分の位置を常に中心にしてトラッキングする機能があるととても便利です。
車で移動するときは特に、自分が向かっている方向が上になっているように地図が自動的に回転すると、非常に便利です。
今回は、ユーザーの現在位置をトラッキングしつつ、向かっている方向を上に地図を回転する方法を記載します。
また、Nextjsはセットアップ済みの前提で解説します。
本記事のNextjsのバージョンは”13.5.4″となります。ライブラリはMapbox GL JSをラップしたreact-map-glを利用します。
現在位置を取得する
ユーザーの現在位置を取得するために、GeolocateControlを使用します。
GeolocateControlのオプションを定義する
GeolocateControlのオプションを定義します。
enableHighAccuracyで可能な限り正確なGPS情報を受け取るようにします。ただし、これによってデバイスの応答時間が遅くなったり消費電力が上がることも念頭に置いておきます。
trackUserLocationをtrueにすることで位置を移動すると、位置を自動的に更新してくれます。
showUserHeadingをtrueにすることで現在向いている方向に矢印を表示します。
const geoLocaleOptions = {
positionOptions: { enableHighAccuracy: true },
fitBoundsOptions: { maxZoom: 15 },
trackUserLocation: true,
showUserHeading: true
}
GeolocateControlを追加する
MapコンポーネントにGeolocateControlを追加します。
Mapコンポーネントの中に<GeolocateControl>を追加するだけです。
この時に、refとoptionsを渡します。
const Mapbox4 = () => {
const { map } = useMap()
const geoControlRef = useRef<mapboxgl.GeolocateControl>(null)
return (
<Map
id='map'
initialViewState={{
longitude: 139.636814,
latitude: 35.443098,
zoom: 15
}}
style={{ width: '100%', height: '100vh' }}
mapStyle={'mapbox://styles/xxx/yyy'}
mapboxAccessToken={"Mapbox AccessToken"}
onLoad={onLoad}
>
<GeolocateControl
ref={geoControlRef}
{...geoLocaleOptions}
/>
</Map>
)
}
GeolocateControlにイベントを追加する
onGeolocateイベントは、現在位置が更新されるたびに呼び出されます。
<GeolocateControl
ref={geoControlRef}
{...geoLocaleOptions}
onGeolocate={e => {
onGeolocate(e)
}}
/>
onGeolocate関数を作成します。
GeolocateControlのonGeolocateイベントから呼びだされるため、現在位置をeで取得し、MapboxのeaseToメソッドに現在位置と方位角度を設定します。
これにより、常に現在位置を中心にマップを表示し、画面を上に常にマップが回転するようになります。
const onGeolocate = (e: GeolocateResultEvent) => {
map?.easeTo({
center: { lat: e.coords.latitude, lng: e.coords.longitude },
bearing: e.coords.heading ?? 0
})
}
GPSのエラーハンドリング
GeolocateControlにonErrorイベントを追加します。
onErrorイベントはGeolocationAPIに関わる何かしらのエラーが発生した時に呼び出されます。
<GeolocateControl
...
onError={e => {
errorMsg(e)
}}
/>
onErrorイベントで検知したときにerrorMsg関数を呼んで、エラーの時にアラートを表示します。
/**
* geolocation取得時のエラーアラートを表示
* @param error
*/
const errorMsg = (error: { code: number }): void => {
// 0:UNKNOWN_ERROR 原因不明のエラー
// 1:PERMISSION_DENIED 利用者が位置情報の取得を許可しなかった
// 2:POSITION_UNAVAILABLE 電波状況などで位置情報が取得できなかった
// 3:TIMEOUT 位置情報の取得に時間がかかり過ぎた
const errorInfo = [
'原因不明のエラーが発生しました。',
'位置情報の取得が許可されていません。',
'電波状況などで位置情報が取得できませんでした。',
'位置情報の取得に時間がかかり過ぎてタイムアウトしました。'
]
const errorMessage = '[エラー番号: ' + error.code + ']\n' + errorInfo[error.code]
alert(errorMessage)
}
コード全体
このコードをpage.tsxなどで<MapboxComponent />
で呼び出します。
すると、ユーザーの位置情報をトラッキングし、向かっている方向を上に地図を回転してくれます。
import Map, { GeolocateControl, GeolocateResultEvent, useMap } from 'react-map-gl'
import 'mapbox-gl/dist/mapbox-gl.css'
import { useRef } from 'react'
const geoLocaleOptions = {
positionOptions: { enableHighAccuracy: true },
fitBoundsOptions: { maxZoom: 15 },
trackUserLocation: true,
showUserHeading: true
}
/**
* ユーザーの現在位置を中心に表示し、トラッキングしつつ、向かっている方向を北にマップを回転させる
*/
const Mapbox4 = () => {
const { map } = useMap()
const geoControlRef = useRef<mapboxgl.GeolocateControl>(null)
const onGeolocate = (e: GeolocateResultEvent) => {
map?.easeTo({
center: { lat: e.coords.latitude, lng: e.coords.longitude },
bearing: e.coords.heading ?? 0
})
}
const onLoad = () => {
if (!geoControlRef.current?.trigger()) {
geoControlRef.current?.trigger()
}
}
return (
<Map
id='map'
initialViewState={{
longitude: 139.636814,
latitude: 35.443098,
zoom: 15
}}
style={{ width: '100%', height: '100vh' }}
mapStyle={'mapbox://styles/xxx/yyy'}
mapboxAccessToken={"Mapbox AccessToken"}
onLoad={onLoad}
>
<GeolocateControl
ref={geoControlRef}
{...geoLocaleOptions}
onGeolocate={e => {
onGeolocate(e)
}}
onError={e => {
errorMsg(e)
}}
/>
</Map>
)
}
export default Mapbox4
/**
* geolocation取得時のエラーアラートを表示
* @param error
*/
const errorMsg = (error: { code: number }): void => {
// 0:UNKNOWN_ERROR 原因不明のエラー
// 1:PERMISSION_DENIED 利用者が位置情報の取得を許可しなかった
// 2:POSITION_UNAVAILABLE 電波状況などで位置情報が取得できなかった
// 3:TIMEOUT 位置情報の取得に時間がかかり過ぎた
const errorInfo = [
'原因不明のエラーが発生しました。',
'位置情報の取得が許可されていません。',
'電波状況などで位置情報が取得できませんでした。',
'位置情報の取得に時間がかかり過ぎてタイムアウトしました。'
]
const errorMessage = '[エラー番号: ' + error.code + ']\n' + errorInfo[error.code]
alert(errorMessage)
}
Summary
Mapboxは「地図のAWS」と呼ばれるほど、開発者フレンドリーです。
今回のGPS関係も簡単に実装ができる。便利です。