Skip to content

Commit

Permalink
Check map maxBounds in Geolocate control (#8756)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThisIsOstad authored and Ryan Hamley committed Oct 12, 2019
1 parent da78c2a commit def961a
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 25 deletions.
86 changes: 61 additions & 25 deletions src/ui/control/geolocate_control.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,57 @@ class GeolocateControl extends Evented {
this._map = (undefined: any);
}

_isOutOfMapMaxBounds(position: Position) {
const bounds = this._map.getMaxBounds();
const coordinates = position.coords;

return bounds && (
coordinates.longitude < bounds.getWest() ||
coordinates.longitude > bounds.getEast() ||
coordinates.latitude < bounds.getSouth() ||
coordinates.latitude > bounds.getNorth()
);
}

_setErrorState() {
switch (this._watchState) {
case 'WAITING_ACTIVE':
this._watchState = 'ACTIVE_ERROR';
this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active');
this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-active-error');
break;
case 'ACTIVE_LOCK':
this._watchState = 'ACTIVE_ERROR';
this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active');
this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-active-error');
this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-waiting');
// turn marker grey
break;
case 'BACKGROUND':
this._watchState = 'BACKGROUND_ERROR';
this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-background');
this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-background-error');
this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-waiting');
// turn marker grey
break;
case 'ACTIVE_ERROR':
break;
default:
assert(false, `Unexpected watchState ${this._watchState}`);
}
}

_onSuccess(position: Position) {
if (this._isOutOfMapMaxBounds(position)) {
this._setErrorState();

this.fire(new Event('outofmaxbounds', position));
this._updateMarker();
this._finish();

return;
}

if (this.options.trackUserLocation) {
// keep a record of the position so that if the state is BACKGROUND and the user
// clicks the button, we can move to ACTIVE_LOCK immediately without waiting for
Expand Down Expand Up @@ -218,31 +268,7 @@ class GeolocateControl extends Evented {
this._clearWatch();
}
} else {
switch (this._watchState) {
case 'WAITING_ACTIVE':
this._watchState = 'ACTIVE_ERROR';
this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active');
this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-active-error');
break;
case 'ACTIVE_LOCK':
this._watchState = 'ACTIVE_ERROR';
this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active');
this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-active-error');
this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-waiting');
// turn marker grey
break;
case 'BACKGROUND':
this._watchState = 'BACKGROUND_ERROR';
this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-background');
this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-background-error');
this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-waiting');
// turn marker grey
break;
case 'ACTIVE_ERROR':
break;
default:
assert(false, `Unexpected watchState ${this._watchState}`);
}
this._setErrorState();
}
}

Expand Down Expand Up @@ -456,6 +482,16 @@ export default GeolocateControl;
*
*/

/**
* Fired on each Geolocation API position update which returned as success but user position is out of map maxBounds.
*
* @event outofmaxbounds
* @memberof GeolocateControl
* @instance
* @property {Position} data The returned [Position](https://developer.mozilla.org/en-US/docs/Web/API/Position) object from the callback in [Geolocation.getCurrentPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) or [Geolocation.watchPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition).
*
*/

/**
* Fired when the Geolocate Control changes to the active lock state, which happens either upon first obtaining a successful Geolocation API position for the user (a geolocate event will follow), or the user clicks the geolocate button when in the background state which uses the last known position to recenter the map and enter active lock state (no geolocate event will follow unless the users's location changes).
*
Expand Down
46 changes: 46 additions & 0 deletions test/unit/ui/control/geolocate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,52 @@ test('GeolocateControl error event', (t) => {
geolocation.sendError({code: 2, message: 'error message'});
});

test('GeolocateControl outofmaxbounds event in active lock state', (t) => {
t.plan(5);

const map = createMap(t);
const geolocate = new GeolocateControl();
map.addControl(geolocate);
map.setMaxBounds([[0, 0], [10, 10]]);
geolocate._watchState = 'ACTIVE_LOCK';

const click = new window.Event('click');

geolocate.on('outofmaxbounds', (position) => {
t.equal(geolocate._watchState, 'ACTIVE_ERROR', 'geolocate state');
t.equal(position.coords.latitude, 10, 'geolocate position latitude');
t.equal(position.coords.longitude, 20, 'geolocate position longitude');
t.equal(position.coords.accuracy, 3, 'geolocate position accuracy');
t.equal(position.timestamp, 4, 'geolocate timestamp');
t.end();
});
geolocate._geolocateButton.dispatchEvent(click);
geolocation.send({latitude: 10, longitude: 20, accuracy: 3, timestamp: 4});
});

test('GeolocateControl outofmaxbounds event in background state', (t) => {
t.plan(5);

const map = createMap(t);
const geolocate = new GeolocateControl();
map.addControl(geolocate);
map.setMaxBounds([[0, 0], [10, 10]]);
geolocate._watchState = 'BACKGROUND';

const click = new window.Event('click');

geolocate.on('outofmaxbounds', (position) => {
t.equal(geolocate._watchState, 'BACKGROUND_ERROR', 'geolocate state');
t.equal(position.coords.latitude, 10, 'geolocate position latitude');
t.equal(position.coords.longitude, 20, 'geolocate position longitude');
t.equal(position.coords.accuracy, 3, 'geolocate position accuracy');
t.equal(position.timestamp, 4, 'geolocate timestamp');
t.end();
});
geolocate._geolocateButton.dispatchEvent(click);
geolocation.send({latitude: 10, longitude: 20, accuracy: 3, timestamp: 4});
});

test('GeolocateControl geolocate event', (t) => {
t.plan(4);

Expand Down

0 comments on commit def961a

Please sign in to comment.