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) {