Skip to content

Commit

Permalink
Ensure passing is aligned to bearing when calling map.fitBounds
Browse files Browse the repository at this point in the history
* Allow a nonzero bearing in cameraForBounds()

* Test bearing can be passed to cameraForBounds()

* Ensure offset is a point

* Rotate offset by bearing

* Fix linting errors

* Do not rotate the offset by the bearing

Co-authored-by: Allison Strandberg <allison@mapdwell.com>
  • Loading branch information
allison-strandberg and Allison Strandberg authored Aug 26, 2020
1 parent acd86d4 commit 53b8a40
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 5 deletions.
10 changes: 7 additions & 3 deletions src/ui/camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ class Camera extends Evented {
* in the viewport. LngLatBounds represent a box that is always axis-aligned with bearing 0.
* @param options Options object
* @param {number | PaddingOptions} [options.padding] The amount of padding in pixels to add to the given bounds.
* @param {number} [options.bearing=0] Desired map bearing at end of animation, in degrees.
* @param {PointLike} [options.offset=[0, 0]] The center of the given bounds relative to the map's center, measured in pixels.
* @param {number} [options.maxZoom] The maximum zoom level to allow when the camera would transition to the specified bounds.
* @returns {CameraOptions | void} If map is able to fit to provided bounds, returns `CameraOptions` with
Expand All @@ -491,7 +492,8 @@ class Camera extends Evented {
*/
cameraForBounds(bounds: LngLatBoundsLike, options?: CameraOptions): void | CameraOptions & AnimationOptions {
bounds = LngLatBounds.convert(bounds);
return this._cameraForBoxAndBearing(bounds.getNorthWest(), bounds.getSouthEast(), 0, options);
const bearing = options && options.bearing || 0;
return this._cameraForBoxAndBearing(bounds.getNorthWest(), bounds.getSouthEast(), bearing, options);
}

/**
Expand Down Expand Up @@ -569,10 +571,12 @@ class Camera extends Evented {
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 offset = (typeof options.offset.x === 'number') ? new Point(options.offset.x, options.offset.y) : 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 paddingOffset = new Point(paddingOffsetX, paddingOffsetY);
const rotatedPaddingOffset = paddingOffset.rotate(bearing * Math.PI / 180);
const offsetAtInitialZoom = offset.add(rotatedPaddingOffset);
const offsetAtFinalZoom = offsetAtInitialZoom.mult(tr.scale / tr.zoomScale(zoom));

const center = tr.unproject(p0world.add(p1world).div(2).sub(offsetAtFinalZoom));
Expand Down
44 changes: 42 additions & 2 deletions test/unit/ui/camera.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1797,7 +1797,7 @@ test('camera', (t) => {
});

t.test('#cameraForBounds', (t) => {
t.test('no padding passed', (t) => {
t.test('no options passed', (t) => {
const camera = createCamera();
const bb = [[-133, 16], [-68, 50]];

Expand All @@ -1807,6 +1807,28 @@ test('camera', (t) => {
t.end();
});

t.test('bearing positive number', (t) => {
const camera = createCamera();
const bb = [[-133, 16], [-68, 50]];

const transform = camera.cameraForBounds(bb, {bearing: 175});
t.deepEqual(fixedLngLat(transform.center, 4), {lng: -100.5, lat: 34.7171}, 'correctly calculates coordinates for new bounds');
t.equal(fixedNum(transform.zoom, 3), 2.558);
t.equal(transform.bearing, 175);
t.end();
});

t.test('bearing negative number', (t) => {
const camera = createCamera();
const bb = [[-133, 16], [-68, 50]];

const transform = camera.cameraForBounds(bb, {bearing: -30});
t.deepEqual(fixedLngLat(transform.center, 4), {lng: -100.5, lat: 34.7171}, 'correctly calculates coordinates for new bounds');
t.equal(fixedNum(transform.zoom, 3), 2.392);
t.equal(transform.bearing, -30);
t.end();
});

t.test('padding number', (t) => {
const camera = createCamera();
const bb = [[-133, 16], [-68, 50]];
Expand All @@ -1826,7 +1848,7 @@ test('camera', (t) => {
t.end();
});

t.test('asymetrical padding', (t) => {
t.test('asymmetrical padding', (t) => {
const camera = createCamera();
const bb = [[-133, 16], [-68, 50]];

Expand All @@ -1835,6 +1857,15 @@ test('camera', (t) => {
t.end();
});

t.test('bearing and asymmetrical padding', (t) => {
const camera = createCamera();
const bb = [[-133, 16], [-68, 50]];

const transform = camera.cameraForBounds(bb, {bearing: 90, padding: {top: 10, right: 75, bottom: 50, left: 25}, duration: 0});
t.deepEqual(fixedLngLat(transform.center, 4), {lng: -103.3761, lat: 31.7099}, 'correctly calculates coordinates for bounds with bearing and padding option as object applied');
t.end();
});

t.test('offset', (t) => {
const camera = createCamera();
const bb = [[-133, 16], [-68, 50]];
Expand Down Expand Up @@ -1862,6 +1893,15 @@ test('camera', (t) => {
t.end();
});

t.test('bearing, asymmetrical padding, and offset', (t) => {
const camera = createCamera();
const bb = [[-133, 16], [-68, 50]];

const transform = camera.cameraForBounds(bb, {bearing: 90, padding: {top: 10, right: 75, bottom: 50, left: 25}, offset: [0, 100], duration: 0});
t.deepEqual(fixedLngLat(transform.center, 4), {lng: -103.3761, lat: 43.0929}, 'correctly calculates coordinates for bounds with bearing, padding option as object, and offset applied');
t.end();
});

t.end();
});

Expand Down

0 comments on commit 53b8a40

Please sign in to comment.