Daniel Paaske
Daniel Paaske

Reputation: 95

Mapbox GL JS Bearing

Is it possible in Mapbox GL JS to get the users bearing? I would like to show the direction in which the user is facing, to assist them in navigating to nearby POI. I understand that it is possible to set the bearing of the map and also get the current bearing of it, but i need the actual real life bearing of the user. Kind of the same thing as on Google Maps: enter image description here

The service is intended to run as an Ionic app on iOS and Android, and the assistance in bearing is a key feature in helping them locate nearby POI on a well populated map.

Upvotes: 3

Views: 5777

Answers (4)

solutegrate
solutegrate

Reputation: 1

Following up on this. Here's an example to incorporate 'following'.

var button = document.createElement('button');

var control = new mapboxgl.GeolocateControl({
    positionOptions: {
        enableHighAccuracy: true
    },
    trackUserLocation: true,
    showUserHeading: true,
    showAccuracyCircle: false
})

map.addControl(control);

var ready = setInterval(function() {
    if (!control._geolocateButton) return;
    control._geolocateButton.style.display = 'none';
    return clearInterval(ready);
}, 1);


button.addEventListener('click', function(e) {
    e.preventDefault();
    e.stopPropagation();
    
    const button = e.target;

    // ADD CSS STYLING FOR ICON WITH THE BELOW CLASSES

    button.classList.remove('mapboxgl-ctrl-geolocate-waiting');
    button.classList.remove('mapboxgl-ctrl-geolocate-active');
    button.classList.remove('mapboxgl-ctrl-geolocate-active-error');
    button.classList.remove('mapboxgl-ctrl-geolocate-background');
    button.classList.remove('mapboxgl-ctrl-geolocate-background-error');
    button.classList.remove('mapboxgl-ctrl-geolocate-follow');

    if (control._watchState === 'ACTIVE_LOCK') {
        if (control._follow) {
            button.classList.add('mapboxgl-ctrl-geolocate-waiting');
            map.dragPan.enable();
            control._follow = false;
            return control.trigger();
        }
        
        button.classList.add('mapboxgl-ctrl-geolocate-follow');
        map.dragPan.disable()
        return control._follow = true;
    } else if (control._watchState === 'BACKGROUND') {
        if (control._follow) {
            control._watchState = 'ACTIVE_LOCK';
            button.classList.add('mapboxgl-ctrl-geolocate-waiting');
            control._follow = false;
            map.dragPan.enable();
            return control.trigger();
        }

        map.dragPan.enable();
        control._follow = false;
    }

    control.trigger();

    if (control._watchState === 'OFF') {
        map.dragPan.enable();
        control._follow = false;
    }

    switch (control._watchState) {
    case 'WAITING_ACTIVE':
        button.classList.add('mapboxgl-ctrl-geolocate-waiting');
        button.classList.add('mapboxgl-ctrl-geolocate-active');
        break;
    case 'ACTIVE_LOCK':
        button.classList.add('mapboxgl-ctrl-geolocate-active');
        break;
    case 'ACTIVE_ERROR':
        button.classList.add('mapboxgl-ctrl-geolocate-waiting');
        button.classList.add('mapboxgl-ctrl-geolocate-active-error');
        break;
    case 'BACKGROUND':
        button.classList.add('mapboxgl-ctrl-geolocate-background');
        break;
    case 'BACKGROUND_ERROR':
        button.classList.add('mapboxgl-ctrl-geolocate-waiting');
        button.classList.add('mapboxgl-ctrl-geolocate-background-error');
        break;
    case 'OFF':
        break;
    default:
        
    }
}, true);


window.addEventListener("deviceorientation", function (event) {
    const heading = control._heading;
    if (!heading) return;

    if (control._follow) map.setBearing(heading);
}, true);

Upvotes: -1

Metabolic
Metabolic

Reputation: 2904

For the users coming here after 2020 (what an year lol), mapbox gl js now supports geolocation which not only provides user's heading but also a bunch of other helpful data:

const geolocate = map.addControl(
 new mapboxgl.GeolocateControl({
   positionOptions: {
      enableHighAccuracy: true
   },
   trackUserLocation: true
 })
)

then listen for geolocate event:

geolocate.on('geolocate', (e) => {
     console.log(e); 
});

this will give you following object:

{
coords: {
    accuracy: number;
    altitude: number;
    altitudeAccuracy: number;
    heading: number;
    latitude: number;
    longitude: number;
    speed: number;
  };
  timestamp: number;
 

heading will give you direction of the user. As the geolocate control keeps triggering automatically so can get the user's direction as well as speed and altitude etc in real time and use that to display data driven symbols on map.

Upvotes: 1

Daniel Paaske
Daniel Paaske

Reputation: 95

So, after some time spend on this, i thought I'd show how i ended up doing this, in case someone else needs it or have a better solution.

It seems cordova has a built in "heading" property in the position object. https://github.com/apache/cordova-plugin-geolocation

var heading = $rootScope.position.heading;

First, i make sure that the marker is always pointing in the heading direction, even when the user turns the map, by subtracting the mapBearing(degrees the map has turned from North), from the user heading.

map.on('rotate', function(){
    map.setLayoutProperty('drone', 'icon-rotate', heading - map.getBearing())
});

I create an icon, at the users position, add the source and add the layer with the source.

map.on('load', function () {
  var point = {"type": "Point", "coordinates": [$rootScope.position.long, $rootScope.position.lat]};
  map.addSource('drone', {type: 'geojson', data: point });
  map.addLayer({
    "id": "drone",
    "type": "symbol",
    "source": "drone"
  }
});

Next i check that heading is actually available, since it only appears to return a value, when the user is moving(only tested on Android so far), and if it is, update the heading of the point.

if($rootScope.position.heading){
    var heading = $rootScope.position.heading; 
    map.setLayoutProperty('drone', 'icon-rotate', $rootScope.position.heading);
};

Finally i update the position of the point, in a "$watch" position.

map.getSource('drone').setData(point);

This way, i can watch the users heading, and the point keeps on track, even when the user rotates the map.

Upvotes: 2

Lucas Wojciechowski
Lucas Wojciechowski

Reputation: 3772

You can get the user's bearing (if their device has such a sensor) by obtaining a Coordinates object from Gelocation#getCurrentPosition() and reading Coordinates#heading.

Mapbox GL JS has no built-in user interface for displaying a user's heading. Building your own user interface is easy. See this example which uses the symbol-rotation property.

Upvotes: 1

Related Questions