[Mapbox] Tracks the user’s current position and rotates the map up in the direction they are heading.

When you are looking at a map and want to pick up information about your surroundings as you move around, it is very useful to have the ability to always centre and track your position.

This is especially useful when travelling by car, when the map automatically rotates so that the direction you are heading towards is up.

In this article, we describe how to rotate the map so that the direction you are heading towards is up, while tracking the user’s current position.

This article also assumes that Nextjs has already been set up.
The version of Nextjs in this article is “13.5.4”. The library uses react-map-gl, which wraps Mapbox GL JS.

目次

Get current position

Use GeolocateControl to obtain the user’s current position.

Define options for GeolocateControl.

Define options for GeolocateControl.

EnableHighAccuracy to receive the most accurate GPS information possible. However, keep in mind that this may slow down the device’s response time and increase its power consumption.

Set trackUserLocation to true to automatically update the location when the position is moved.
Setting showUserHeading to true will display an arrow in the direction you are currently facing.

const geoLocaleOptions = {
  positionOptions: { enableHighAccuracy: true },
  fitBoundsOptions: { maxZoom: 15 },
  trackUserLocation: true,
  showUserHeading: true
}

Add GeolocateControl.

Add a GeolocateControl to the Map component.

Simply add in the Map component.
At this point, pass the ref and 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>
  )
}

Adding events to GeolocateControl.

The onGeolocate event is called whenever the current position is updated.

      <GeolocateControl
        ref={geoControlRef}
        {...geoLocaleOptions}
        onGeolocate={e => {
          onGeolocate(e)
        }}
      />

Create the onGeolocate function.
As it is called from the GeolocateControl’s onGeolocate event, the current position is obtained by e and the current position and azimuth angle are set in the Mapbox’s easeTo method.

This ensures that the map is always centred on the current position and that the map always rotates up the screen.

  const onGeolocate = (e: GeolocateResultEvent) => {
    map?.easeTo({
      center: { lat: e.coords.latitude, lng: e.coords.longitude },
      bearing: e.coords.heading ?? 0
    })
  }

GPS error handling.

Add the onError event to GeolocateControl.

The onError event is called when some error related to GeolocationAPI occurs.

      <GeolocateControl
        ...
        onError={e => {
          errorMsg(e)
        }}
      />

Call the errorMsg function when detected by the onError event to display an alert in the event of an error.

/**
 * Error alert when geolocation is acquired.
 * @param error
 */
const errorMsg = (error: { code: number }): void => {
  // 0:UNKNOWN_ERROR			
  // 1:PERMISSION_DENIED		
  // 2:POSITION_UNAVAILABLE		
  // 3:TIMEOUT					

  const errorInfo = [
    'An error of unknown cause has occurred.',
    'Location data acquisition is not permitted.',
    'Location information could not be obtained due to signal conditions or other reasons.',
    'Location acquisition took too long and timed out.'
  ]

  const errorMessage = '[Error number: ' + error.code + ']\n' + errorInfo[error.code]
  alert(errorMessage)
}

Source

Call this code with <MapboxComponent /> in page.tsx, for example.
It will then track the user’s location and rotate the map up in the direction they are heading.

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
}

/**
 * Display centred on the user's current position and rotate the map north in the direction they are heading, while tracking
 */
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

/**
 * Error alert when geolocation is acquired.
 * @param error
 */
const errorMsg = (error: { code: number }): void => {
  // 0:UNKNOWN_ERROR			
  // 1:PERMISSION_DENIED		
  // 2:POSITION_UNAVAILABLE		
  // 3:TIMEOUT					

  const errorInfo = [
    'An error of unknown cause has occurred.',
    'Location data acquisition is not permitted.',
    'Location information could not be obtained due to signal conditions or other reasons.',
    'Location acquisition took too long and timed out.'
  ]

  const errorMessage = '[Error number: ' + error.code + ']\n' + errorInfo[error.code]
  alert(errorMessage)
}

Summary

Mapbox is so developer-friendly that it has been called the ‘AWS of maps’.
This GPS-related issue is also easy to implement. Convenience.

よかったらシェアしてね!
目次