cephirothdy2j
cephirothdy2j

Reputation: 13

Mapbox GL NavigationControl Events

I have an instance of a Mapbox GL map, after load of my data source I am calling fitBounds() to change the map's center and zoom to fit my data set. I've also attached a number of event listeners to this map because I want to know when the user manually changed the map's zoom or position.

Mapbox also triggers 'movestart' and 'zoomstart' on fitBounds(), though I'm getting around that problem by checking for the presence of the originalEvent property in the event callback.

The problem is, I also have a NavigationControl added to the map, and user interactions triggered through its zoom or rotate buttons fire my map events without the originalEvent property. I cannot find any way in the Mapbox documentation to listen attach event listeners to the NavigationControl, nor a way to differentiate between a zoom / pan initiated by a fitBounds call vs. a user interaction through that component.

Is there something I'm missing? Is there a way to attach mouse / touch event listeners to the NavigationControl component? Or perhaps is there some property within the event objects that will tell me the source of the event?

Simplified code sample -

this._userMoved = false;
this._map = new mapboxgl.Map(options);

// listen for user actions that update the map display
['movestart', 'zoomstart', 'boxzoomstart', 'rotatestart', 'pitchstart'].forEach((action) => {
  this._map.on(action, (e) => {
    if (e.originalEvent) {
      // if this property is set, the event in question was triggered by an actual user ineraction.
      // EXCEPT when the user interaction came from the NavigationControl, hence the problem
      this._userMoved = true;
    }
  });
});
this._map.on('load', () => {
  // add the control after map load
  this._map.addControl(new mapboxgl.NavigationControl(),'top-left');
  this._setMapData(); // adds my data source to the map
  this._setMapView(); // calls this._map.fitBounds() using my data source
});

Upvotes: 1

Views: 1659

Answers (1)

Steve Bennett
Steve Bennett

Reputation: 126125

If your need is specifically to handle a specific event (fitbounds) that is being called once, then you can do this:

this._map.once('moveend', e => {
   // do whatever you do after the fitbounds event.
   this._map.on(['movestart', 'zoomstart', 'boxzoomstart', 'rotatestart', 'pitchstart'], userMovementHandler)
});

EDIT

I just looked more closely at the documentation and there is indeed an eventData parameter to fitBounds which is intended to solve exactly this problem.

<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8' />
    <title>Display a map</title>
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.43.0/mapbox-gl.js'></script>
    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.43.0/mapbox-gl.css' rel='stylesheet' />
    <style>
        body { margin:0; padding:0; }
        #map { position:absolute; top:0; bottom:0; width:100%; }
    </style>
</head>
<body>

<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1Ijoic3RldmFnZSIsImEiOiJGcW03aExzIn0.QUkUmTGIO3gGt83HiRIjQw';
var map = new mapboxgl.Map({
    container: 'map', // container id
    style: 'mapbox://styles/mapbox/streets-v9', // stylesheet location
    center: [-74.50, 40], // starting position [lng, lat]
    zoom: 9 // starting zoom
}).on('moveend', e => { 
    if (e.source === 'fitBounds') {
        console.log('Caused by fitBounds');
    } else {
        console.log('Caused by user');
    }
})
map.fitBounds([140,-42, 150,-37], {}, {source: 'fitBounds'})
</script>

</body>
</html> 

Upvotes: 3

Related Questions