Skip to content

Commit

Permalink
Bugfix: _cameraForBoxAndBearing not fitting bounds properly when usin…
Browse files Browse the repository at this point in the history
…g asymettrical camera viewport and bearing (#3591)

* failing test

* tests fixes which were incorrect and code update

* code improvements

* small cleanup

* fix linting findings

* update changelog
  • Loading branch information
fcwheat authored Jan 23, 2024
1 parent 8fb9511 commit 533de48
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

### 🐞 Bug fixes
- Fix wheel zoom to be into the same direction above or under the horizon ([#3398](https://github.com/maplibre/maplibre-gl-js/issues/3398))
- Fix _cameraForBoxAndBearing not fitting bounds properly when using asymettrical camera viewport and bearing.
- _...Add new stuff here..._

## 4.0.0-pre.4
Expand Down
23 changes: 21 additions & 2 deletions src/ui/camera.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {mercatorZfromAltitude} from '../geo/mercator_coordinate';
import {Terrain} from '../render/terrain';
import {LngLat, LngLatLike} from '../geo/lng_lat';
import {Event} from '../util/evented';
import {LngLatBounds} from '../geo/lng_lat_bounds';

beforeEach(() => {
setMatchMedia();
Expand Down Expand Up @@ -1930,7 +1931,7 @@ describe('#cameraForBounds', () => {
const transform = camera.cameraForBounds(bb, {bearing: 175});

expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 34.7171});
expect(fixedNum(transform.zoom, 3)).toBe(2.558);
expect(fixedNum(transform.zoom, 3)).toBe(2.396);
expect(transform.bearing).toBe(175);
});

Expand All @@ -1940,7 +1941,7 @@ describe('#cameraForBounds', () => {
const transform = camera.cameraForBounds(bb, {bearing: -30});

expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 34.7171});
expect(fixedNum(transform.zoom, 3)).toBe(2.392);
expect(fixedNum(transform.zoom, 3)).toBe(2.222);
expect(transform.bearing).toBe(-30);
});

Expand Down Expand Up @@ -2000,6 +2001,24 @@ describe('#cameraForBounds', () => {

expect(fixedLngLat(transform.center, 4)).toEqual({lng: -103.3761, lat: 43.0929});
});

test('asymmetrical transform using LngLatBounds instance', () => {
const transform = new Transform(2, 10, 0, 60, false);
transform.resize(2048, 512);

const camera = attachSimulateFrame(new CameraMock(transform, {} as any));
camera._update = () => {};

const bb = new LngLatBounds();
bb.extend([-66.9326, 49.5904]);
bb.extend([-125.0011, 24.9493]);

const rotatedTransform = camera.cameraForBounds(bb, {bearing: 45});

expect(fixedLngLat(rotatedTransform.center, 4)).toEqual({lng: -95.9669, lat: 38.3048});
expect(fixedNum(rotatedTransform.zoom, 3)).toBe(2.507);
expect(rotatedTransform.bearing).toBe(45);
});
});

describe('#fitBounds', () => {
Expand Down
40 changes: 29 additions & 11 deletions src/ui/camera.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {extend, warnOnce, clamp, wrap, defaultEasing, pick} from '../util/util';
import {extend, warnOnce, clamp, wrap, defaultEasing, pick, degreesToRadians} from '../util/util';
import {interpolates} from '@maplibre/maplibre-gl-style-spec';
import {browser} from '../util/browser';
import {LngLat} from '../geo/lng_lat';
Expand Down Expand Up @@ -692,15 +692,30 @@ export abstract class Camera extends Evented {
const tr = this.transform;
const edgePadding = tr.padding;

// 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));
const p0rotated = p0world.rotate(-bearing * Math.PI / 180);
const p1rotated = p1world.rotate(-bearing * Math.PI / 180);
// Consider all corners of the rotated bounding box derived from the given points
// when find the camera position that fits the given points.
const bounds = new LngLatBounds(p0, p1);
const nwWorld = tr.project(bounds.getNorthWest());
const neWorld = tr.project(bounds.getNorthEast());
const seWorld = tr.project(bounds.getSouthEast());
const swWorld = tr.project(bounds.getSouthWest());

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 bearingRadians = degreesToRadians(-bearing);

const nwRotatedWorld = nwWorld.rotate(bearingRadians);
const neRotatedWorld = neWorld.rotate(bearingRadians);
const seRotatedWorld = seWorld.rotate(bearingRadians);
const swRotatedWorld = swWorld.rotate(bearingRadians);

const upperRight = new Point(
Math.max(nwRotatedWorld.x, neRotatedWorld.x, swRotatedWorld.x, seRotatedWorld.x),
Math.max(nwRotatedWorld.y, neRotatedWorld.y, swRotatedWorld.y, seRotatedWorld.y)
);

const lowerLeft = new Point(
Math.min(nwRotatedWorld.x, neRotatedWorld.x, swRotatedWorld.x, seRotatedWorld.x),
Math.min(nwRotatedWorld.y, neRotatedWorld.y, swRotatedWorld.y, seRotatedWorld.y)
);

// Calculate zoom: consider the original bbox and padding.
const size = upperRight.sub(lowerLeft);
Expand All @@ -721,11 +736,14 @@ export abstract class Camera extends Evented {
const paddingOffsetX = (options.padding.left - options.padding.right) / 2;
const paddingOffsetY = (options.padding.top - options.padding.bottom) / 2;
const paddingOffset = new Point(paddingOffsetX, paddingOffsetY);
const rotatedPaddingOffset = paddingOffset.rotate(bearing * Math.PI / 180);
const rotatedPaddingOffset = paddingOffset.rotate(degreesToRadians(bearing));
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));
const center = tr.unproject(
// either world diagonal can be used (NW-SE or NE-SW)
nwWorld.add(seWorld).div(2).sub(offsetAtFinalZoom)
);

return {
center,
Expand Down
10 changes: 10 additions & 0 deletions src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -679,3 +679,13 @@ export function subscribe(target: Subscriber, message: keyof WindowEventMap, lis
}
};
}

/**
* This method converts degrees to radians.
* The return value is the radian value.
* @param degrees - The number of degrees
* @returns radians
*/
export function degreesToRadians(degrees: number): number {
return degrees * Math.PI / 180;
}

0 comments on commit 533de48

Please sign in to comment.