diff --git a/debug/7517.html b/debug/7517.html
new file mode 100644
index 00000000000..0fb7f135714
--- /dev/null
+++ b/debug/7517.html
@@ -0,0 +1,137 @@
+
+
+
+
+ Mapbox GL JS debug page
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ui/camera.js b/src/ui/camera.js
index 3cde087716c..06135aea72d 100644
--- a/src/ui/camera.js
+++ b/src/ui/camera.js
@@ -434,17 +434,9 @@ class Camera extends Evented {
return;
}
- // we separate the passed padding option into two parts, the part that does not affect the map's center
- // (lateral and vertical padding), and the part that does (paddingOffset). We add the padding offset
- // to the options `offset` object where it can alter the map's center in the subsequent calls to
- // `easeTo` and `flyTo`.
- const paddingOffset = [(options.padding.left - options.padding.right) / 2, (options.padding.top - options.padding.bottom) / 2],
- lateralPadding = Math.min(options.padding.right, options.padding.left),
- verticalPadding = Math.min(options.padding.top, options.padding.bottom);
- options.offset = [options.offset[0] + paddingOffset[0], options.offset[1] + paddingOffset[1]];
-
const tr = this.transform;
- // we want to calculate the upper right and lower left of the box defined by p0 and p1
+
+ // We want to calculate the upper right and lower left of the box defined by p0 and p1
// in a coordinate system rotate to match the destination bearing.
const p0world = tr.project(LngLat.convert(p0));
const p1world = tr.project(LngLat.convert(p1));
@@ -454,10 +446,10 @@ class Camera extends Evented {
const upperRight = new Point(Math.max(p0rotated.x, p1rotated.x), Math.max(p0rotated.y, p1rotated.y));
const lowerLeft = new Point(Math.min(p0rotated.x, p1rotated.x), Math.min(p0rotated.y, p1rotated.y));
- const offset = Point.convert(options.offset),
- size = upperRight.sub(lowerLeft),
- scaleX = (tr.width - lateralPadding * 2 - Math.abs(offset.x) * 2) / size.x,
- scaleY = (tr.height - verticalPadding * 2 - Math.abs(offset.y) * 2) / size.y;
+ // Calculate zoom: consider the original bbox and padding.
+ const size = upperRight.sub(lowerLeft);
+ const scaleX = (tr.width - options.padding.left - options.padding.right) / size.x;
+ const scaleY = (tr.height - options.padding.top - options.padding.bottom) / size.y;
if (scaleY < 0 || scaleX < 0) {
warnOnce(
@@ -465,11 +457,23 @@ class Camera extends Evented {
);
return;
}
- options.center = tr.unproject(p0world.add(p1world).div(2));
- options.zoom = Math.min(tr.scaleZoom(tr.scale * Math.min(scaleX, scaleY)), options.maxZoom);
- options.bearing = bearing;
- return options;
+ const zoom = Math.min(tr.scaleZoom(tr.scale * Math.min(scaleX, scaleY)), options.maxZoom);
+
+ // Calculate center: apply the zoom, the configured offset, as well as offset that exists as a result of padding.
+ const offset = Point.convert(options.offset);
+ const paddingOffsetX = (options.padding.left - options.padding.right) / 2;
+ const paddingOffsetY = (options.padding.top - options.padding.bottom) / 2;
+ const offsetAtInitialZoom = new Point(offset.x + paddingOffsetX, offset.y + paddingOffsetY);
+ const offsetAtFinalZoom = offsetAtInitialZoom.mult(tr.scale / tr.zoomScale(zoom));
+
+ const center = tr.unproject(p0world.add(p1world).div(2).sub(offsetAtFinalZoom));
+
+ return {
+ center,
+ zoom,
+ bearing
+ };
}
/**
diff --git a/test/unit/ui/camera.test.js b/test/unit/ui/camera.test.js
index 3c6f60a8f07..9aa8db43679 100644
--- a/test/unit/ui/camera.test.js
+++ b/test/unit/ui/camera.test.js
@@ -1714,11 +1714,47 @@ test('camera', (t) => {
const camera = createCamera();
const bb = [[-133, 16], [-68, 50]];
- const transform = camera.cameraForBounds(bb, { padding: {top: 10, right: 75, bottom: 50, left: 25}, duration: 0 });
+ const transform = camera.cameraForBounds(bb, { padding: {top: 15, right: 15, bottom: 15, left: 15}, duration: 0 });
t.deepEqual(fixedLngLat(transform.center, 4), { lng: -100.5, lat: 34.7171 }, 'correctly calculates coordinates for bounds with padding option as object applied');
t.end();
});
+ t.test('asymetrical padding', (t) => {
+ const camera = createCamera();
+ const bb = [[-133, 16], [-68, 50]];
+
+ const transform = camera.cameraForBounds(bb, { padding: {top: 10, right: 75, bottom: 50, left: 25}, duration: 0 });
+ t.deepEqual(fixedLngLat(transform.center, 4), { lng: -96.5558, lat: 32.0833 }, 'correctly calculates coordinates for bounds with padding option as object applied');
+ t.end();
+ });
+
+ t.test('offset', (t) => {
+ const camera = createCamera();
+ const bb = [[-133, 16], [-68, 50]];
+
+ const transform = camera.cameraForBounds(bb, { offset: [0, 100] });
+ t.deepEqual(fixedLngLat(transform.center, 4), { lng: -100.5, lat: 44.4717 }, 'correctly calculates coordinates for bounds with padding option as object applied');
+ t.end();
+ });
+
+ t.test('offset as object', (t) => {
+ const camera = createCamera();
+ const bb = [[-133, 16], [-68, 50]];
+
+ const transform = camera.cameraForBounds(bb, { offset: { x: 0, y: 100 } });
+ t.deepEqual(fixedLngLat(transform.center, 4), { lng: -100.5, lat: 44.4717 }, 'correctly calculates coordinates for bounds with padding option as object applied');
+ t.end();
+ });
+
+ t.test('offset and padding', (t) => {
+ const camera = createCamera();
+ const bb = [[-133, 16], [-68, 50]];
+
+ const transform = camera.cameraForBounds(bb, { padding: {top: 10, right: 75, bottom: 50, left: 25}, offset: [0, 100] });
+ t.deepEqual(fixedLngLat(transform.center, 4), { lng: -96.5558, lat: 44.4189 }, 'correctly calculates coordinates for bounds with padding option as object applied');
+ t.end();
+ });
+
t.end();
});