diff --git a/debug/vertical.html b/debug/vertical.html
new file mode 100644
index 00000000000..e2acc63cfbc
--- /dev/null
+++ b/debug/vertical.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ui/camera.js b/src/ui/camera.js
index c27de89f3e4..5b297f936e2 100644
--- a/src/ui/camera.js
+++ b/src/ui/camera.js
@@ -7,6 +7,7 @@ const LngLat = require('../geo/lng_lat');
const LngLatBounds = require('../geo/lng_lat_bounds');
const Point = require('point-geometry');
const Evented = require('../util/evented');
+const Transform = require('../geo/transform');
/**
* Options common to {@link Map#jumpTo}, {@link Map#easeTo}, and {@link Map#flyTo},
@@ -492,13 +493,26 @@ class Camera extends Evented {
startZoom = this.getZoom(),
startBearing = this.getBearing(),
startPitch = this.getPitch(),
+ startCenter = tr.center,
zoom = 'zoom' in options ? +options.zoom : startZoom,
bearing = 'bearing' in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing,
pitch = 'pitch' in options ? +options.pitch : startPitch;
- let toLngLat,
- toPoint;
+ let toLngLat,
+ toPoint,
+ offsetLocation,
+ toLocation,
+ fromLocation,
+ endTransform;
+
+ // Setup a Transform representing the state of the map at the end of the transition
+ endTransform = new Transform();
+ endTransform.center = tr.center;
+ endTransform.resize(tr.width, tr.height);
+ endTransform.zoom = zoom;
+ endTransform.bearing = bearing;
+ endTransform.pitch = 0; // fixes #3119 by pretending the map is not pitched; use pitch = 0 to revert to the old behavior
if ('center' in options) {
toLngLat = LngLat.convert(options.center);
@@ -511,8 +525,6 @@ class Camera extends Evented {
toLngLat = tr.pointLocation(toPoint);
}
- const fromPoint = tr.locationPoint(toLngLat);
-
if (options.animate === false) options.duration = 0;
this.zooming = (zoom !== startZoom);
@@ -531,11 +543,30 @@ class Camera extends Evented {
this.fire('zoomstart', eventData);
}
+ offsetLocation = endTransform.pointLocation(toPoint);
+ fromLocation = endTransform.pointLocation(endTransform.centerPoint);
+
+ toLocation = LngLat.convert([
+ toLngLat.lng - (offsetLocation.lng - fromLocation.lng),
+ toLngLat.lat - (offsetLocation.lat - fromLocation.lat)
+ ]);
+
clearTimeout(this._onEaseEnd);
this._ease(function (k) {
+ // When zooming we need to scale our lat/lon interpolation because the distances change over the course of the transition
+ var k2 = k,
+ deltaZoom = zoom - startZoom,
+ totalDistanceExpansion = Math.pow(0.5, deltaZoom) - 1,
+ currentDelta,
+ currentDistanceExpansion;
+
if (this.zooming) {
tr.zoom = interpolate(startZoom, zoom, k);
+ currentDelta = tr.zoom - startZoom;
+ currentDistanceExpansion = Math.pow(0.5, currentDelta) - 1;
+
+ k2 = currentDistanceExpansion / totalDistanceExpansion;
}
if (this.rotating) {
@@ -545,8 +576,10 @@ class Camera extends Evented {
if (this.pitching) {
tr.pitch = interpolate(startPitch, pitch, k);
}
-
- tr.setLocationAtPoint(toLngLat, fromPoint.add(toPoint.sub(fromPoint)._mult(k)));
+
+ var lng = interpolate(startCenter.lng, toLocation.lng, k2);
+ var lat = interpolate(startCenter.lat, toLocation.lat, k2);
+ tr.center = LngLat.convert([lng, lat]);
this.fire('move', eventData);
if (this.zooming) {