diff --git a/debug/debug.html b/debug/debug.html index 1d692bc0052..b5853d55866 100644 --- a/debug/debug.html +++ b/debug/debug.html @@ -45,6 +45,9 @@ positionOptions: { enableHighAccuracy: true }, + fitBoundsOptions: { + maxZoom: 20 + }, watchPosition: true })); map.addControl(new mapboxgl.ScaleControl()); diff --git a/js/geo/lng_lat.js b/js/geo/lng_lat.js index ed26453f78e..99202e3814f 100644 --- a/js/geo/lng_lat.js +++ b/js/geo/lng_lat.js @@ -68,6 +68,24 @@ class LngLat { toString() { return `LngLat(${this.lng}, ${this.lat})`; } + + /** + * Returns a `LngLatBounds` from the coordinates extended by a given `radius`. + * + * @param {number} radius Distance in meters from the coordinates to extend the bounds. + * @returns {LngLatBounds} A new `LngLatBounds` object representing the coordinates extended by the `radius`. + * @example + * var ll = new mapboxgl.LngLat(-73.9749, 40.7736); + * ll.toBounds(100).toArray(); // = [[-73.97501862141328, 40.77351016847229], [-73.97478137858673, 40.77368983152771]] + */ + toBounds(radius) { + const latAccuracy = 360 * radius / 40075017, + lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat); + + const LngLatBounds = require('./lng_lat_bounds'); + return new LngLatBounds(new LngLat(this.lng - lngAccuracy, this.lat - latAccuracy), + new LngLat(this.lng + lngAccuracy, this.lat + latAccuracy)); + } } /** diff --git a/js/ui/control/geolocate_control.js b/js/ui/control/geolocate_control.js index b7ef292469c..0a50de35249 100644 --- a/js/ui/control/geolocate_control.js +++ b/js/ui/control/geolocate_control.js @@ -4,6 +4,7 @@ const Evented = require('../../util/evented'); const DOM = require('../../util/dom'); const window = require('../../util/window'); const util = require('../../util/util'); +const LngLat = require('../../geo/lng_lat'); const defaultGeoPositionOptions = { enableHighAccuracy: false, timeout: 6000 /* 6sec */ }; const className = 'mapboxgl-ctrl'; @@ -40,9 +41,12 @@ function checkGeolocationSupport(callback) { * geolocation support is not available, the GeolocateControl will not * be visible. * + * The zoom level applied will depend on the accuracy of the geolocation provided by the device. + * * @implements {IControl} * @param {Object} [options] - * @param {Object} [options.positionOptions={enableHighAccuracy: false, timeout: 6000}] A [PositionOptions](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions) object. + * @param {Object} [options.positionOptions={enableHighAccuracy: false, timeout: 6000}] A Geolocation API [PositionOptions](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions) object. + * @param {Object} [options.fitBoundsOptions={maxZoom: 18}] A [`fitBounds`](#Map#fitBounds) options object to use when the map is panned and zoomed to the device location. The default is to use a `maxZoom` of 18 to limit how far the map will zoom in for very accurate locations. * @param {Object} [options.watchPosition=false] If `true` the map will reposition each time the position of the device changes and the control becomes a toggle. * @example * map.addControl(new mapboxgl.GeolocateControl({ @@ -77,12 +81,12 @@ class GeolocateControl extends Evented { } _onSuccess(position) { - this._map.jumpTo({ - center: [position.coords.longitude, position.coords.latitude], - zoom: 17, - bearing: 0, - pitch: 0 - }); + const center = new LngLat(position.coords.longitude, position.coords.latitude); + const radius = position.coords.accuracy; + + this._map.fitBounds(center.toBounds(radius), util.extend({ + maxZoom: (this.options.maxZoom !== undefined) ? this.options.maxZoom : 18 + }, this.options.fitBoundsOptions || {})); this.fire('geolocate', position); this._finish(); diff --git a/test/js/geo/lng_lat.test.js b/test/js/geo/lng_lat.test.js index 0f0e5ff09d5..e01257dfcab 100644 --- a/test/js/geo/lng_lat.test.js +++ b/test/js/geo/lng_lat.test.js @@ -50,5 +50,11 @@ test('LngLat', (t) => { t.end(); }); + t.test('#toBounds', (t) => { + t.deepEqual(new LngLat(0, 0).toBounds(10).toArray(), [[-0.00008983152770714982, -0.00008983152770714982], [0.00008983152770714982, 0.00008983152770714982]]); + t.deepEqual(new LngLat(-73.9749, 40.7736).toBounds(10).toArray(), [[-73.97501862141328, 40.77351016847229], [-73.97478137858673, 40.77368983152771]]); + t.end(); + }); + t.end(); });