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