Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Globe - camera controls #4408

Merged
merged 56 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
52b4294
Camera controls changes from dev branch
kubapelc Jul 15, 2024
36098ca
Move stuff from globe_control_utils to globe_utils
kubapelc Jul 15, 2024
dfb833c
Better globe_utils comments
kubapelc Jul 15, 2024
c65cb17
Fix markers not being updated when globe is toggled
kubapelc Jul 15, 2024
0a53926
Fix globe tests
kubapelc Jul 15, 2024
4123e14
Update build size
kubapelc Jul 15, 2024
3f7ff10
Better comments for camera helper functions
kubapelc Jul 15, 2024
2548bae
Move camera helper functions to beginning of file
kubapelc Jul 15, 2024
2b20bad
Camera: more and better comments
kubapelc Jul 15, 2024
0a5e601
Update build size
kubapelc Jul 15, 2024
4992d10
Fix globe transform error correction handling
kubapelc Jul 17, 2024
f2b3257
Better comments for _last* fields in globe transform
kubapelc Jul 17, 2024
d6097bd
Refactor newFrameUpdate
kubapelc Jul 17, 2024
e0b6f0b
Better comments for CoveringTilesOptions type members.
kubapelc Jul 17, 2024
138c42e
Refactor globe camera tests to use more describe statements
kubapelc Jul 17, 2024
9353096
Remove isTilePositionOccluded function from transform interface
kubapelc Jul 17, 2024
ad70291
Fix camera tests
kubapelc Jul 19, 2024
b619352
Add more mercator_utils test
kubapelc Jul 19, 2024
4998218
Add more globe_transform tests
kubapelc Jul 19, 2024
96fec9c
Fix failing render tests
kubapelc Jul 19, 2024
4831167
Make camera helper functions static
kubapelc Jul 19, 2024
c559504
Remove `around` from flyTo options.
kubapelc Jul 19, 2024
54eae63
Update build size
kubapelc Jul 19, 2024
5cd9eb0
CameraHelper: initial implementation, inertia handling
kubapelc Jul 21, 2024
a607c17
Move createVec* functions to util.ts
kubapelc Jul 21, 2024
3c50b0f
CameraHelper: panning and zooming
kubapelc Jul 21, 2024
203aa8e
CameraHelpers: implement cameraForBounds
kubapelc Jul 21, 2024
b655ef3
CameraHelpers: handle jumpTo
kubapelc Jul 28, 2024
d7ecf37
Merge remote-tracking branch 'upstream/globe' into kubapelc/globe-pr-…
kubapelc Jul 28, 2024
97ec4db
Merge branch 'kubapelc/globe-pr-controls' into kubapelc/globe-pr-cont…
kubapelc Jul 28, 2024
b50dee6
CameraHelper: easeTo
kubapelc Jul 28, 2024
e8a588a
CameraHelper: flyTo
kubapelc Jul 28, 2024
0240d08
Projection event contains new projection name and is fired by changin…
kubapelc Jul 28, 2024
465b178
Fix lint
kubapelc Jul 29, 2024
d422416
Fix test camera/map not having proper CameraHelper
kubapelc Jul 29, 2024
836be51
Fix easeTo not emitting zoom events
kubapelc Jul 29, 2024
6b02e78
Fix cameraForBoxAndBearing globe not returning anything, rename camer…
kubapelc Jul 29, 2024
36a8533
Fix globe easeTo ignoring offset
kubapelc Jul 29, 2024
7eb0205
Fix one flyTo test not creating camera properly
kubapelc Jul 29, 2024
2682810
Update build size
kubapelc Jul 29, 2024
d5b6819
Add projection transition event tests
kubapelc Jul 29, 2024
27e5b80
Add example on how to compensate for how globe size changes with lati…
kubapelc Jul 29, 2024
8c6145b
Merge remote-tracking branch 'upstream/globe' into kubapelc/globe-pr-…
kubapelc Aug 2, 2024
15a8650
Revert scrollzoom delete removal
kubapelc Aug 2, 2024
6b6463d
Remove apparentZoom parameter
kubapelc Aug 2, 2024
410d2e5
CameraHelper is set in camera constuctor
kubapelc Aug 2, 2024
f6dd27e
Merge remote-tracking branch 'upstream/globe' into kubapelc/globe-pr-…
kubapelc Aug 5, 2024
aee09ff
Use spy for projection event unit tests
kubapelc Aug 6, 2024
30937cf
Remove unnecessary done() in tests
kubapelc Aug 6, 2024
9c2a466
Update build size
kubapelc Aug 6, 2024
8d72e77
Remove more unneeded done() calls
kubapelc Aug 6, 2024
41e0b02
Do not use map.once callback in projection events tests
kubapelc Aug 7, 2024
e5356ad
Better zoom delta example title and description
kubapelc Aug 7, 2024
dc9b158
Rename globe zoom delta and planet size function example
kubapelc Aug 7, 2024
73e0201
Add zoom planet size function example image
kubapelc Aug 7, 2024
5e1e774
Reduce size of some globe example images using compresspng
kubapelc Aug 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified docs/assets/examples/globe-fill-extrusion.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/examples/globe-vector-tiles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions src/geo/projection/camera_helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import Point from '@mapbox/point-geometry';
import {IReadonlyTransform, ITransform} from '../transform_interface';
import {LngLat, LngLatLike} from '../lng_lat';
import {CameraForBoundsOptions, PointLike} from '../../ui/camera';
import {PaddingOptions} from '../edge_insets';
import {LngLatBounds} from '../lng_lat_bounds';
import {warnOnce} from '../../util/util';

export type MapControlsDeltas = {
panDelta: Point;
zoomDelta: number;
bearingDelta: number;
pitchDelta: number;
around: Point;
}

export type CameraForBoxAndBearingHandlerResult = {
center: LngLat;
zoom: number;
bearing: number;
};

export type EaseToHandlerOptions = {
bearing: number;
pitch: number;
padding: PaddingOptions;
offsetAsPoint: Point;
around?: LngLat;
aroundPoint?: Point;
center?: LngLatLike;
zoom?: number;
offset?: PointLike;
}

export type EaseToHandlerResult = {
easeFunc: (k: number) => void;
elevationCenter: LngLat;
isZooming: boolean;
}

export type FlyToHandlerOptions = {
bearing: number;
pitch: number;
padding: PaddingOptions;
offsetAsPoint: Point;
center?: LngLatLike;
locationAtOffset: LngLat;
zoom?: number;
minZoom?: number;
}

export type FlyToHandlerResult = {
easeFunc: (k: number, scale: number, centerFactor: number, pointAtOffset: Point) => void;
scaleOfZoom: number;
scaleOfMinZoom?: number;
targetCenter: LngLat;
pixelPathLength: number;
}

/**
* @internal
*/
export function cameraBoundsWarning() {
warnOnce(
'Map cannot fit within canvas with the given bounds, padding, and/or offset.'
);
}

/**
* @internal
* Contains projection-specific functions related to camera controls, easeTo, flyTo, inertia, etc.
*/
export interface ICameraHelper {
get useGlobeControls(): boolean;

handlePanInertia(pan: Point, transform: IReadonlyTransform): {
easingCenter: LngLat;
easingOffset: Point;
};

handleMapControlsPitchBearingZoom(deltas: MapControlsDeltas, tr: ITransform): void;

handleMapControlsPan(deltas: MapControlsDeltas, tr: ITransform, preZoomAroundLoc: LngLat): void;

cameraForBoxAndBearing(options: CameraForBoundsOptions, padding: PaddingOptions, bounds: LngLatBounds, bearing: number, tr: IReadonlyTransform): CameraForBoxAndBearingHandlerResult;

handleJumpToCenterZoom(tr: ITransform, options: { zoom?: number; center?: LngLatLike }): void;

handleEaseTo(tr: ITransform, options: EaseToHandlerOptions): EaseToHandlerResult;

handleFlyTo(tr: ITransform, options: FlyToHandlerOptions): FlyToHandlerResult;
}
2 changes: 1 addition & 1 deletion src/geo/projection/globe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class GlobeProjection implements Projection {
private _errorCorrectionPreviousValue: number = 0.0;
private _errorMeasurementLastChangeTime: number = -1000.0;

get name(): string {
get name(): 'globe' {
return 'globe';
}

Expand Down
483 changes: 483 additions & 0 deletions src/geo/projection/globe_camera_helper.ts

Large diffs are not rendered by default.

53 changes: 34 additions & 19 deletions src/geo/projection/globe_transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {CanonicalTileID, OverscaledTileID, UnwrappedTileID} from '../../source/t
import {angularCoordinatesRadiansToVector, mercatorCoordinatesToAngularCoordinatesRadians, sphereSurfacePointToCoordinates} from './globe_utils';
import {expectToBeCloseToArray, sleep} from '../../util/test/util';
import {MercatorCoordinate} from '../mercator_coordinate';
import {tileCoordinatesToLocation} from './mercator_utils';

function testPlaneAgainstLngLat(lngDegrees: number, latDegrees: number, plane: Array<number>) {
const lat = latDegrees / 180.0 * Math.PI;
Expand Down Expand Up @@ -58,7 +59,6 @@ describe('GlobeTransform', () => {
const globeTransform = createGlobeTransform(globeProjectionMock);

describe('general plane properties', () => {
globeTransform.newFrameUpdate();
const projectionData = globeTransform.getProjectionData(new OverscaledTileID(0, 0, 0, 0, 0));

test('plane vector length', () => {
Expand Down Expand Up @@ -133,16 +133,13 @@ describe('GlobeTransform', () => {
globeTransform.resize(512, 512);
globeTransform.setZoom(-0.5);
globeTransform.setCenter(new LngLat(0, 80));
globeTransform.newFrameUpdate();
expectToBeCloseToArray(globeTransform.cameraPosition as Array<number>, [0, 2.2818294674820794, 0.40234810049271963], precisionDigits);

globeTransform.setPitch(35);
globeTransform.setBearing(70);
globeTransform.newFrameUpdate();
expectToBeCloseToArray(globeTransform.cameraPosition as Array<number>, [-0.7098603286961542, 2.002400604307631, 0.6154310261827212], precisionDigits);

globeTransform.setCenter(new LngLat(-10, 42));
globeTransform.newFrameUpdate();
expectToBeCloseToArray(globeTransform.cameraPosition as Array<number>, [-3.8450970996236364, 2.9368285470351516, 4.311953269048194], precisionDigits);
});

Expand All @@ -165,7 +162,6 @@ describe('GlobeTransform', () => {
describe('project location to coordinates', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform(globeProjectionMock);
globeTransform.newFrameUpdate();

test('basic test', () => {
globeTransform.setCenter(new LngLat(0, 0));
Expand Down Expand Up @@ -200,20 +196,16 @@ describe('GlobeTransform', () => {
test('unproject screen center', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform(globeProjectionMock);
globeTransform.newFrameUpdate();
let unprojected = globeTransform.screenPointToLocation(screenCenter);
expect(unprojected.lng).toBeCloseTo(globeTransform.center.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(globeTransform.center.lat, precisionDigits);

globeTransform.center.lng = 90.0;
globeTransform.newFrameUpdate();
globeTransform.setCenter(new LngLat(90.0, 0.0));
unprojected = globeTransform.screenPointToLocation(screenCenter);
expect(unprojected.lng).toBeCloseTo(globeTransform.center.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(globeTransform.center.lat, precisionDigits);

globeTransform.center.lng = 0.0;
globeTransform.center.lat = 60.0;
globeTransform.newFrameUpdate();
globeTransform.setCenter(new LngLat(0.0, 60.0));
unprojected = globeTransform.screenPointToLocation(screenCenter);
expect(unprojected.lng).toBeCloseTo(globeTransform.center.lng, precisionDigits);
expect(unprojected.lat).toBeCloseTo(globeTransform.center.lat, precisionDigits);
Expand All @@ -222,7 +214,6 @@ describe('GlobeTransform', () => {
test('unproject point to the side', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform(globeProjectionMock);
globeTransform.newFrameUpdate();
let coords: LngLat;
let projected: Point;
let unprojected: LngLat;
Expand Down Expand Up @@ -258,7 +249,6 @@ describe('GlobeTransform', () => {
globeTransform.resize(512, 512);
globeTransform.setZoom(-0.5);
globeTransform.setCenter(new LngLat(0, 80));
globeTransform.newFrameUpdate();

let coords: LngLat;
let projected: Point;
Expand Down Expand Up @@ -288,7 +278,6 @@ describe('GlobeTransform', () => {
// Try unprojection a point somewhere above the western horizon
globeTransform.setPitch(60);
globeTransform.setBearing(-90);
globeTransform.newFrameUpdate();
const unprojected = globeTransform.screenPointToLocation(screenTopEdgeCenter);
expect(unprojected.lng).toBeCloseTo(-34.699626794124015, precisionDigits);
expect(unprojected.lat).toBeCloseTo(0.0, precisionDigits);
Expand All @@ -299,7 +288,6 @@ describe('GlobeTransform', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform(globeProjectionMock);
globeTransform.setZoom(1);
globeTransform.newFrameUpdate();
let coords: LngLat;
let point: Point;
let projected: Point;
Expand Down Expand Up @@ -356,7 +344,6 @@ describe('GlobeTransform', () => {

describe('rotated', () => {
globeTransform.setBearing(90);
globeTransform.newFrameUpdate();

test('identity', () => {
// Should do nothing
Expand Down Expand Up @@ -439,7 +426,6 @@ describe('GlobeTransform', () => {
test('pointCoordinate', () => {
const precisionDigits = 10;
const globeTransform = createGlobeTransform(globeProjectionMock);
globeTransform.newFrameUpdate();
let coords: LngLat;
let coordsMercator: MercatorCoordinate;
let projected: Point;
Expand All @@ -463,7 +449,6 @@ describe('GlobeTransform', () => {
describe('globeViewAllowed', () => {
test('starts enabled', async () => {
const globeTransform = createGlobeTransform(globeProjectionMock);
globeTransform.newFrameUpdate();

expect(globeTransform.getGlobeViewAllowed()).toBe(true);
expect(globeTransform.useGlobeControls).toBe(true);
Expand Down Expand Up @@ -576,11 +561,41 @@ describe('GlobeTransform', () => {
transform.setPitch(60);

const projection = transform.projectTileCoordinates(8192, 8192, new UnwrappedTileID(0, new CanonicalTileID(1, 1, 0)), (_x, _y) => 0);
console.log(projection);
expect(projection.point.x).toBeCloseTo(0.22428309892086878, precisionDigits);
expect(projection.point.y).toBeCloseTo(-0.4462620847133465, precisionDigits);
expect(projection.signedDistanceFromCamera).toBeCloseTo(822.280942015371, precisionDigits);
expect(projection.isOccluded).toBe(true);
});
});

describe('isLocationOccluded', () => {
const transform = new GlobeTransform(globeProjectionMock);
transform.resize(512, 512);
transform.setCenter(new LngLat(0.0, 0.0));
transform.setZoom(-1);

test('center', () => {
expect(transform.isLocationOccluded(new LngLat(0, 0))).toBe(false);
});

test('center from tile', () => {
expect(transform.isLocationOccluded(tileCoordinatesToLocation(0, 0, new CanonicalTileID(1, 1, 1)))).toBe(false);
});

test('backside', () => {
expect(transform.isLocationOccluded(new LngLat(179.9, 0))).toBe(true);
});

test('backside from tile', () => {
expect(transform.isLocationOccluded(tileCoordinatesToLocation(0, 0, new CanonicalTileID(1, 0, 1)))).toBe(true);
});

test('barely visible', () => {
expect(transform.isLocationOccluded(new LngLat(84.49, 0))).toBe(false);
});

test('barely hidden', () => {
expect(transform.isLocationOccluded(new LngLat(84.50, 0))).toBe(true);
});
});
});
Loading