Skip to content

Commit 8f8795f

Browse files
NathanMOlsonHarelM
andauthored
Fixes wrong movement when inertia crosses the horizon (#6345)
* add failing unit test to demonstrate #6111 * Fix 6111 by reducing the ease offset so that it never crosses the horizon. * update Changelog * formatting * increase inertia pan offset limit from 50% to 75% of distance to horizon. --------- Co-authored-by: Harel M <harel.mazor@gmail.com>
1 parent 64f727e commit 8f8795f

3 files changed

Lines changed: 47 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
### 🐞 Bug fixes
77
- Fix accuracy circle on locate user control ([#5432](https://github.com/maplibre/maplibre-gl-js/issues/5432))
88
- Fix evaluating `global-state` in paint `...-pattern` properties ([6301](https://github.com/maplibre/maplibre-gl-js/pull/6301))
9+
- Fix pan moving in the wrong direction when map is pitched ([#6111](https://github.com/maplibre/maplibre-gl-js/issues/6111))
910
- Fix evaluation of `text-color` when using `format` within `step` ([#5833](https://github.com/maplibre/maplibre-gl-js/issues/5432))
1011
- Fix regression in `mergeSourceDiffs`: handle add/remove/removeAll ([6342](https://github.com/maplibre/maplibre-gl-js/pull/6342))
1112
- Fix evaluating `global-state` in layout properties `icon-size` and `text-size` ([#6308](https://github.com/maplibre/maplibre-gl-js/issues/6308))

src/geo/projection/mercator_camera_helper.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {LngLat, type LngLatLike} from '../lng_lat';
33
import {cameraForBoxAndBearing, type CameraForBoxAndBearingHandlerResult, type EaseToHandlerResult, type EaseToHandlerOptions, type FlyToHandlerResult, type FlyToHandlerOptions, type ICameraHelper, type MapControlsDeltas, updateRotation, type UpdateRotationArgs} from './camera_helper';
44
import {normalizeCenter} from '../transform_helper';
55
import {rollPitchBearingEqual, scaleZoom, zoomScale} from '../../util/util';
6-
import {projectToWorldCoordinates, unprojectFromWorldCoordinates} from './mercator_utils';
6+
import {getMercatorHorizon, projectToWorldCoordinates, unprojectFromWorldCoordinates} from './mercator_utils';
77
import {interpolates} from '@maplibre/maplibre-gl-style-spec';
88

99
import type {IReadonlyTransform, ITransform} from '../transform_interface';
@@ -21,8 +21,14 @@ export class MercatorCameraHelper implements ICameraHelper {
2121
easingCenter: LngLat;
2222
easingOffset: Point;
2323
} {
24+
// Reduce the offset so that it never goes past the horizon. If it goes past
25+
// the horizon, the pan direction is opposite of the intended direction.
26+
const offsetLength = pan.mag();
27+
const pixelsToHorizon = Math.abs(getMercatorHorizon(transform));
28+
const horizonFactor = 0.75; // Must be < 1 to prevent the offset from crossing the horizon
29+
const offsetAsPoint = pan.mult(Math.min(pixelsToHorizon * horizonFactor / offsetLength, 1.0));
2430
return {
25-
easingOffset: pan,
31+
easingOffset: offsetAsPoint,
2632
easingCenter: transform.center,
2733
};
2834
}

src/ui/camera.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {GlobeTransform} from '../geo/projection/globe_transform';
1212
import {getZoomAdjustment} from '../geo/projection/globe_utils';
1313
import {GlobeCameraHelper} from '../geo/projection/globe_camera_helper';
1414
import {MercatorCameraHelper} from '../geo/projection/mercator_camera_helper';
15+
import {getMercatorHorizon} from '../geo/projection/mercator_utils';
16+
import Point from '@mapbox/point-geometry';
1517

1618
import type {GlobeProjection} from '../geo/projection/globe_projection';
1719
import type {Terrain} from '../render/terrain';
@@ -908,6 +910,24 @@ describe('easeTo', () => {
908910
expect(fixedLngLat(camera.getCenter())).toEqual({lng: 170.3125, lat: 0});
909911
});
910912

913+
test('offset computed from inertia (small) does not cross horizon when pitched', () => {
914+
const camera = createCamera({pitch: 85, zoom: 10});
915+
const easeOptions = camera.cameraHelper.handlePanInertia(new Point(0, 100), camera.transform);
916+
expect(easeOptions.easingOffset.mag()).toBeLessThan(Math.abs(getMercatorHorizon(camera.transform)));
917+
});
918+
919+
test('offset computed from inertia (large) does not cross horizon when pitched', () => {
920+
const camera = createCamera({pitch: 85, zoom: 10});
921+
const easeOptions = camera.cameraHelper.handlePanInertia(new Point(0, 500), camera.transform);
922+
expect(easeOptions.easingOffset.mag()).toBeLessThan(Math.abs(getMercatorHorizon(camera.transform)));
923+
});
924+
925+
test('offset computed from inertia (large) does not cross horizon when pitched and rotated', () => {
926+
const camera = createCamera({pitch: 85, bearing: 135, zoom: 10});
927+
const easeOptions = camera.cameraHelper.handlePanInertia(new Point(0, 500), camera.transform);
928+
expect(easeOptions.easingOffset.mag()).toBeLessThan(Math.abs(getMercatorHorizon(camera.transform)));
929+
});
930+
911931
test('zooms with specified offset', () => {
912932
const camera = createCamera();
913933
camera.easeTo({zoom: 3.2, offset: [100, 0], duration: 0});
@@ -2917,6 +2937,24 @@ describe('easeTo globe projection', () => {
29172937
expect(fixedLngLat(camera.getCenter())).toEqual({lng: -175.50457909, lat: 0});
29182938
});
29192939

2940+
test('offset computed from inertia (small) does not cross horizon when pitched', () => {
2941+
const camera = createCameraGlobe({pitch: 85, zoom: 10});
2942+
const easeOptions = camera.cameraHelper.handlePanInertia(new Point(0, 100), camera.transform);
2943+
expect(easeOptions.easingOffset.mag()).toBeLessThan(Math.abs(getMercatorHorizon(camera.transform)));
2944+
});
2945+
2946+
test('offset computed from inertia (large) does not cross horizon when pitched', () => {
2947+
const camera = createCameraGlobe({pitch: 85, zoom: 10});
2948+
const easeOptions = camera.cameraHelper.handlePanInertia(new Point(0, 500), camera.transform);
2949+
expect(easeOptions.easingOffset.mag()).toBeLessThan(Math.abs(getMercatorHorizon(camera.transform)));
2950+
});
2951+
2952+
test('offset computed from inertia (large) does not cross horizon when pitched and rotated', () => {
2953+
const camera = createCameraGlobe({pitch: 85, bearing: 135, zoom: 10});
2954+
const easeOptions = camera.cameraHelper.handlePanInertia(new Point(0, 500), camera.transform);
2955+
expect(easeOptions.easingOffset.mag()).toBeLessThan(Math.abs(getMercatorHorizon(camera.transform)));
2956+
});
2957+
29202958
test('zooms with specified offset', () => {
29212959
const camera = createCameraGlobe();
29222960
camera.easeTo({zoom: 3.2, offset: [100, 0], duration: 0});

0 commit comments

Comments
 (0)