Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check map maxBounds in Geolocate control #8756

Merged
merged 8 commits into from
Oct 12, 2019
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()
);
}

_goToErrorState() {
andrewharvey marked this conversation as resolved.
Show resolved Hide resolved
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';
andrewharvey marked this conversation as resolved.
Show resolved Hide resolved
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._goToErrorState();

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._goToErrorState();
}
}

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
23 changes: 23 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,29 @@ test('GeolocateControl error event', (t) => {
geolocation.sendError({code: 2, message: 'error message'});
});

test('GeolocateControl outofmaxbounds event', (t) => {
andrewharvey marked this conversation as resolved.
Show resolved Hide resolved
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 geolocate event', (t) => {
t.plan(4);

Expand Down