From 575515a07567a82f82a16c11787d1b282e46f102 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Sun, 24 May 2015 19:19:42 -0700 Subject: [PATCH] Rationalize map view method options * Map#setView is replaced with Map#jumpTo. * Map#jumpTo, Map#easeTo, and Map#flyTo now all accept the same set of view options. --- CHANGELOG.md | 5 +- docs/_posts/examples/3400-01-03-flyto.html | 4 +- js/ui/easings.js | 68 ++++++++++------------ js/ui/hash.js | 6 +- js/ui/map.js | 68 ++++++++++++++-------- test/js/ui/easings.test.js | 28 ++++----- test/js/ui/map.test.js | 56 ++++++++++++++++-- 7 files changed, 147 insertions(+), 88 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7245f51bac9..0a523d3565c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,8 +33,9 @@ An in-progress version being developed in the `master` branch. * `mapboxgl.Source` is no longer exported. Use `map.addSource()` instead. * `mapboxgl.util.supported()` moved to `mapboxgl.supported() -* map#setView(latlng, zoom, bearing) changed to map#setView(latlng, zoom, bearing, pitch) -* map#easeTo(latlng, zoom, bearing, options) changed to map#easeTo(latlng, zoom, bearing, pitch, options) +* map#setView(latlng, zoom, bearing) changed to map#jumpTo(options) +* map#easeTo(latlng, zoom, bearing, options) changed to map#easeTo(options) +* map#flyTo(latlng, zoom, bearing, options) changed to map#flyTo(options) diff --git a/docs/_posts/examples/3400-01-03-flyto.html b/docs/_posts/examples/3400-01-03-flyto.html index c82fa6f063f..7a107134d3a 100644 --- a/docs/_posts/examples/3400-01-03-flyto.html +++ b/docs/_posts/examples/3400-01-03-flyto.html @@ -35,8 +35,8 @@ function fly() { // Fly to a random location by offsetting the point 40, -74.50 // by up to 5 degrees. - map.flyTo([ + map.flyTo({ center: [ 40 + (Math.random() - 0.5) * 10, - -74.50 + (Math.random() - 0.5) * 10]); + -74.50 + (Math.random() - 0.5) * 10] }); } diff --git a/js/ui/easings.js b/js/ui/easings.js index b6cdb8a4b50..135caeead0e 100644 --- a/js/ui/easings.js +++ b/js/ui/easings.js @@ -267,31 +267,27 @@ util.extend(exports, /** @lends Map.prototype */{ nw = tr.project(bounds.getNorthWest()), se = tr.project(bounds.getSouthEast()), size = se.sub(nw), - center = tr.unproject(nw.add(se).div(2)), - scaleX = (tr.width - options.padding * 2 - Math.abs(offset.x) * 2) / size.x, - scaleY = (tr.height - options.padding * 2 - Math.abs(offset.y) * 2) / size.y, + scaleY = (tr.height - options.padding * 2 - Math.abs(offset.y) * 2) / size.y; - zoom = Math.min(tr.scaleZoom(tr.scale * Math.min(scaleX, scaleY)), options.maxZoom); + options.center = tr.unproject(nw.add(se).div(2)); + options.zoom = Math.min(tr.scaleZoom(tr.scale * Math.min(scaleX, scaleY)), options.maxZoom); + options.bearing = 0; return options.linear ? - this.easeTo(center, zoom, 0, this.getPitch(), options) : - this.flyTo(center, zoom, 0, options); + this.easeTo(options) : + this.flyTo(options); }, /** * Easing animation to a specified location/zoom/bearing * - * @param {Object} latlng a `LatLng` object - * @param {Number} zoom - * @param {Number} bearing - * @param {Number} pitch - * @param {animOptions} + * @param {ViewOptions+animOptions} options map view and animation options * @fires movestart * @fires moveend * @returns {this} */ - easeTo: function(latlng, zoom, bearing, pitch, options) { + easeTo: function(options) { this.stop(); options = util.extend({ @@ -302,25 +298,27 @@ util.extend(exports, /** @lends Map.prototype */{ var tr = this.transform, offset = Point.convert(options.offset).rotate(-tr.angle), + from = tr.point, startZoom = this.getZoom(), startBearing = this.getBearing(), startPitch = this.getPitch(); - latlng = LatLng.convert(latlng); - zoom = zoom === undefined ? startZoom : zoom; - bearing = bearing === undefined ? startBearing : this._normalizeBearing(bearing, startBearing); - pitch = pitch === undefined ? startPitch : pitch; + var zoom = 'zoom' in options ? +options.zoom : startZoom; + var bearing = 'bearing' in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing; + var pitch = 'pitch' in options ? startPitch : pitch; var scale = tr.zoomScale(zoom - startZoom), - from = tr.point, - to = latlng ? tr.project(latlng).sub(offset.div(scale)) : tr.point, + to = 'center' in options ? tr.project(LatLng.convert(options.center)).sub(offset.div(scale)) : from, around; if (zoom !== startZoom) { around = tr.pointLocation(tr.centerPoint.add(to.sub(from).div(1 - 1 / scale))); this.zooming = true; } - if (startBearing !== bearing) this.rotating = true; + + if (startBearing !== bearing) { + this.rotating = true; + } this.fire('movestart'); @@ -353,21 +351,20 @@ util.extend(exports, /** @lends Map.prototype */{ /** * Flying animation to a specified location/zoom/bearing with automatic curve * - * @param {Object} latlng a `LatLng` object - * @param {Number} zoom - * @param {Number} bearing - * @param {Object} options + * @param {ViewOptions} options map view options * @param {Number} [options.speed=1.2] How fast animation occurs * @param {Number} [options.curve=1.42] How much zooming out occurs during animation - * @param {Function} options.easing + * @param {Function} [options.easing] * @fires movestart * @fires moveend * @returns {this} * @example * // fly with default options to null island - * map.flyTo([0, 0], 9, 0); + * map.flyTo({center: [0, 0], zoom: 9}); * // using flyTo options - * map.flyTo([0, 0], 9, 0, { + * map.flyTo({ + * center: [0, 0], + * zoom: 9, * speed: 0.2, * curve: 1, * easing: function(t) { @@ -375,7 +372,7 @@ util.extend(exports, /** @lends Map.prototype */{ * } * }); */ - flyTo: function(latlng, zoom, bearing, options) { + flyTo: function(options) { this.stop(); options = util.extend({ @@ -385,23 +382,18 @@ util.extend(exports, /** @lends Map.prototype */{ easing: util.ease }, options); - latlng = LatLng.convert(latlng); - - var offset = Point.convert(options.offset), - tr = this.transform, + var tr = this.transform, + offset = Point.convert(options.offset), startZoom = this.getZoom(), startBearing = this.getBearing(); - zoom = zoom === undefined ? startZoom : zoom; - bearing = bearing === undefined ? startBearing : this._normalizeBearing(bearing, startBearing); + var center = 'center' in options ? LatLng.convert(options.center) : this.getCenter(); + var zoom = 'zoom' in options ? +options.zoom : startZoom; + var bearing = 'bearing' in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing; var scale = tr.zoomScale(zoom - startZoom), from = tr.point, - to = tr.project(latlng).sub(offset.div(scale)); - - if (options.animate === false) { - return this.setView(latlng, zoom, bearing, this.getPitch()); - } + to = tr.project(center).sub(offset.div(scale)); var startWorldSize = tr.worldSize, rho = options.curve, diff --git a/js/ui/hash.js b/js/ui/hash.js index ad07cf9b4e1..a0e5f1fba26 100644 --- a/js/ui/hash.js +++ b/js/ui/hash.js @@ -44,7 +44,11 @@ Hash.prototype = { _onHashChange: function() { var loc = location.hash.replace('#', '').split('/'); if (loc.length >= 3) { - this._map.setView([+loc[1], +loc[2]], +loc[0], +(loc[3] || 0), this._map.getPitch()); + this._map.jumpTo({ + center: [+loc[1], +loc[2]], + zoom: +loc[0], + bearing: +(loc[3] || 0) + }); return true; } return false; diff --git a/js/ui/map.js b/js/ui/map.js index c8457b2f655..1e251cf40fb 100644 --- a/js/ui/map.js +++ b/js/ui/map.js @@ -76,7 +76,7 @@ var Map = module.exports = function(options) { this._hash = options.hash && (new Hash()).addTo(this); // don't set position from options if set through hash if (!this._hash || !this._hash._onHashChange()) { - this.setView(options.center, options.zoom, options.bearing, options.pitch); + this.jumpTo(options); } this.sources = {}; @@ -117,28 +117,49 @@ util.extend(Map.prototype, /** @lends Map.prototype */{ }, /** - * Sets a map position + * @typedef {Object} ViewOptions + * @property {Array} [center] Latitude and longitude (passed as `[lat, lng]`) + * @property {number} [zoom] Map zoom level + * @property {number} [bearing] Map rotation bearing in degrees counter-clockwise from north + * @property {number} [pitch] The angle at which the camera is looking at the ground + */ + + /** + * Change any combination of center, zoom, bearing, and pitch, without + * a transition. The map will retain the current values for any options + * not included in `options`. * - * @param {Array} center Latitude and longitude (passed as `[lat, lng]`) - * @param {number} zoom Map zoom level - * @param {number} bearing Map rotation bearing in degrees counter-clockwise from north - * @param {number} pitch The angle at which the camera is looking at the ground + * @param {ViewOptions} options map view options * @fires movestart * @fires moveend * @returns {Map} `this` */ - setView: function(center, zoom, bearing, pitch) { + jumpTo: function(options) { this.stop(); var tr = this.transform, - zoomChanged = tr.zoom !== +zoom, - bearingChanged = tr.bearing !== +bearing, - pitchChanged = tr.pitch !== +pitch; + zoomChanged = false, + bearingChanged = false, + pitchChanged = false; - tr.center = LatLng.convert(center); - tr.zoom = +zoom; - tr.bearing = +bearing; - tr.pitch = +pitch; + if ('center' in options) { + tr.center = LatLng.convert(options.center); + } + + if ('zoom' in options && tr.zoom !== +options.zoom) { + zoomChanged = true; + tr.zoom = +options.zoom; + } + + if ('bearing' in options && tr.bearing !== +options.bearing) { + bearingChanged = true; + tr.bearing = +options.bearing; + } + + if ('pitch' in options && tr.pitch !== +options.pitch) { + pitchChanged = true; + tr.pitch = +options.pitch; + } return this .fire('movestart') @@ -147,8 +168,7 @@ util.extend(Map.prototype, /** @lends Map.prototype */{ }, /** - * Sets a map location. This is like setView (and calls setView internally) - * but keeps the values for zoom, bearing, and pitch all the same. + * Sets a map location. Equivalent to `jumpTo({center: center})`. * * @param {Array} center Latitude and longitude (passed as `[lat, lng]`) * @fires movestart @@ -158,12 +178,11 @@ util.extend(Map.prototype, /** @lends Map.prototype */{ * map.setCenter([-74, 38]); */ setCenter: function(center) { - this.setView(center, this.getZoom(), this.getBearing(), this.getPitch()); + this.jumpTo({center: center}); }, /** - * Sets a map zoom. This is like setView (and calls setView internally) - * but keeps the values for center, bearing, and pitch all the same. + * Sets a map zoom. Equivalent to `jumpTo({zoom: zoom})`. * * @param {number} zoom Map zoom level * @fires movestart @@ -174,12 +193,11 @@ util.extend(Map.prototype, /** @lends Map.prototype */{ * map.setZoom(5); */ setZoom: function(zoom) { - this.setView(this.getCenter(), zoom, this.getBearing(), this.getPitch()); + this.jumpTo({zoom: zoom}); }, /** - * Sets a map rotation. This is like setView (and calls setView internally) - * but keeps the values for center, zoom, and pitch all the same. + * Sets a map rotation. Equivalent to `jumpTo({bearing: bearing})`. * * @param {number} bearing Map rotation bearing in degrees counter-clockwise from north * @fires movestart @@ -190,11 +208,11 @@ util.extend(Map.prototype, /** @lends Map.prototype */{ * map.setBearing(90); */ setBearing: function(bearing) { - this.setView(this.getCenter(), this.getZoom(), bearing, this.getPitch()); + this.jumpTo({bearing: bearing}); }, /** - * Sets a map angle + * Sets a map angle. Equivalent to `jumpTo({pitch: pitch})`. * * @param {number} pitch The angle at which the camera is looking at the ground * @fires movestart @@ -202,7 +220,7 @@ util.extend(Map.prototype, /** @lends Map.prototype */{ * @returns {Map} `this` */ setPitch: function(pitch) { - this.setView(this.getCenter(), this.getZoom(), this.getBearing(), pitch); + this.jumpTo({pitch: pitch}); }, /** diff --git a/test/js/ui/easings.test.js b/test/js/ui/easings.test.js index c659da23020..9f79f7a906f 100644 --- a/test/js/ui/easings.test.js +++ b/test/js/ui/easings.test.js @@ -280,28 +280,28 @@ test('Map', function(t) { t.test('#easeTo', function(t) { t.test('pans to specified location', function(t) { var map = createMap(); - map.easeTo([0, 100], undefined, undefined, undefined, { duration: 0 }); + map.easeTo({ center: [0, 100], duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 100 }); t.end(); }); t.test('zooms to specified level', function(t) { var map = createMap(); - map.easeTo(undefined, 3.2, undefined, undefined, { duration: 0 }); + map.easeTo({ zoom: 3.2, duration: 0 }); t.equal(map.getZoom(), 3.2); t.end(); }); t.test('rotates to specified bearing', function(t) { var map = createMap(); - map.easeTo(undefined, undefined, 90, undefined, { duration: 0 }); + map.easeTo({ bearing: 90, duration: 0 }); t.equal(map.getBearing(), 90); t.end(); }); t.test('pans and zooms', function(t) { var map = createMap(); - map.easeTo([0, 100], 3.2, undefined, undefined, { duration: 0 }); + map.easeTo({ center: [0, 100], zoom: 3.2, duration: 0 }); t.deepEqual(fixedLatLng(map.getCenter()), fixedLatLng({ lat: 0, lng: 100 })); t.equal(map.getZoom(), 3.2); t.end(); @@ -309,7 +309,7 @@ test('Map', function(t) { t.test('pans and rotates', function(t) { var map = createMap(); - map.easeTo([0, 100], undefined, 90, undefined, { duration: 0 }); + map.easeTo({ center: [0, 100], bearing: 90, duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 100 }); t.equal(map.getBearing(), 90); t.end(); @@ -317,7 +317,7 @@ test('Map', function(t) { t.test('zooms and rotates', function(t) { var map = createMap(); - map.easeTo(undefined, 3.2, 90, undefined, { duration: 0 }); + map.easeTo({ zoom: 3.2, bearing: 90, duration: 0 }); t.equal(map.getZoom(), 3.2); t.equal(map.getBearing(), 90); t.end(); @@ -325,7 +325,7 @@ test('Map', function(t) { t.test('pans, zooms, and rotates', function(t) { var map = createMap(); - map.easeTo([0, 100], 3.2, 90, undefined, { duration: 0 }); + map.easeTo({ center: [0, 100], zoom: 3.2, bearing: 90, duration: 0 }); t.deepEqual(fixedLatLng(map.getCenter()), fixedLatLng({ lat: 0, lng: 100 })); t.equal(map.getZoom(), 3.2); t.equal(map.getBearing(), 90); @@ -334,7 +334,7 @@ test('Map', function(t) { t.test('noop', function(t) { var map = createMap(); - map.easeTo(undefined, undefined, undefined, undefined, { duration: 0 }); + map.easeTo({ duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 0 }); t.equal(map.getZoom(), 0); t.equal(map.getBearing(), 0); @@ -343,7 +343,7 @@ test('Map', function(t) { t.test('noop with offset', function(t) { var map = createMap(); - map.easeTo(undefined, undefined, undefined, undefined, { offset: [100, 0], duration: 0 }); + map.easeTo({ offset: [100, 0], duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 0 }); t.equal(map.getZoom(), 0); t.equal(map.getBearing(), 0); @@ -352,14 +352,14 @@ test('Map', function(t) { t.test('pans with specified offset', function(t) { var map = createMap(); - map.easeTo([0, 100], undefined, undefined, undefined, { offset: [100, 0], duration: 0 }); + map.easeTo({ center: [0, 100], offset: [100, 0], duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 29.6875 }); t.end(); }); t.test('pans with specified offset relative to viewport on a rotated map', function(t) { var map = createMap({bearing: 180}); - map.easeTo([0, 100], undefined, undefined, undefined, { offset: [100, 0], duration: 0 }); + map.easeTo({ center: [0, 100], offset: [100, 0], duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 170.3125 }); t.end(); }); @@ -395,13 +395,13 @@ test('Map', function(t) { t.end(); }); - map.easeTo([0, 100], 3.2, 90, undefined, { duration: 0 }); + map.easeTo({ center: [0, 100], zoom: 3.2, bearing: 90, duration: 0 }); }); t.test('stops existing ease', function(t) { var map = createMap(); - map.easeTo([0, 200], undefined, undefined, undefined, { duration: 100 }); - map.easeTo([0, 100], undefined, undefined, undefined, { duration: 0 }); + map.easeTo({ center: [0, 200], duration: 100 }); + map.easeTo({ center: [0, 100], duration: 0 }); t.deepEqual(map.getCenter(), { lat: 0, lng: 100 }); t.end(); }); diff --git a/test/js/ui/map.test.js b/test/js/ui/map.test.js index a4cc73ccd92..9df8c179eb2 100644 --- a/test/js/ui/map.test.js +++ b/test/js/ui/map.test.js @@ -98,32 +98,76 @@ test('Map', function(t) { t.end(); }); - t.test('#setView', function(t) { + t.test('#jumpTo', function(t) { var map = createMap(); t.test('sets center', function(t) { - map.setView([1, 2], 3, 4); + map.jumpTo({center: [1, 2]}); + t.deepEqual(map.getCenter(), { lat: 1, lng: 2 }); + t.end(); + }); + + t.test('keeps current center if not specified', function(t) { + map.jumpTo({}); t.deepEqual(map.getCenter(), { lat: 1, lng: 2 }); t.end(); }); t.test('sets zoom', function(t) { - map.setView([1, 2], 3, 4); + map.jumpTo({zoom: 3}); + t.deepEqual(map.getZoom(), 3); + t.end(); + }); + + t.test('keeps current zoom if not specified', function(t) { + map.jumpTo({}); t.deepEqual(map.getZoom(), 3); t.end(); }); t.test('sets bearing', function(t) { - map.setView([1, 2], 3, 4); + map.jumpTo({bearing: 4}); + t.deepEqual(map.getBearing(), 4); + t.end(); + }); + + t.test('keeps current bearing if not specified', function(t) { + map.jumpTo({}); t.deepEqual(map.getBearing(), 4); t.end(); }); + t.test('sets pitch', function(t) { + map.jumpTo({pitch: 45}); + t.deepEqual(map.getPitch(), 45); + t.end(); + }); + + t.test('keeps current pitch if not specified', function(t) { + map.jumpTo({}); + t.deepEqual(map.getPitch(), 45); + t.end(); + }); + + t.test('sets multiple properties', function(t) { + map.jumpTo({ + center: [1, 2], + zoom: 3, + bearing: 180, + pitch: 45 + }); + t.deepEqual(map.getCenter(), { lat: 1, lng: 2 }); + t.deepEqual(map.getZoom(), 3); + t.deepEqual(map.getBearing(), 180); + t.deepEqual(map.getPitch(), 45); + t.end(); + }); + t.test('emits move events', function(t) { var started, ended; map.on('movestart', function() { started = true; }) .on('moveend', function() { ended = true; }); - map.setView([1, 2], 3, 4); + map.jumpTo({center: [1, 2]}); t.ok(started); t.ok(ended); t.end(); @@ -132,7 +176,7 @@ test('Map', function(t) { t.test('cancels in-progress easing', function(t) { map.panTo([3, 4]); t.ok(map.isEasing()); - map.setView([1, 2], 3, 4); + map.jumpTo({center: [1, 2]}); t.ok(!map.isEasing()); t.end(); });