Skip to content

Commit 551ba95

Browse files
HarelMibesorakubapelc
authored
Facilitate for globe transition expression refactoring (#5139)
* Initial commit to move the logic of transition out to a new file * Remove one line method * Fix build test * Fix projection data bad refactoring * Fix render and size tests * Fix failing unit test * Uncomment unit tests * Fix typo, remove unneeded details provider initialization. Added current transfrom getter * Fix typo * Add back error correction for latitude. * Refactors the `Projection` classed only (#5163) * This refactors the projection class only, without touching other parts of the code. * remove mercator from the code, revert more changes * Refactor camera helper to split the logic for vertical-perspective and mercator (#5162) * Refactor camera helper * Use the helper in the factory. * Fix build test * Fix according to code review * Use `Transitionalable` infrastructure for transfrom globeness changes. (#5164) * Remove duplicate code * Fix lint, add some methods for original branch * Add projection definition in factory * Add has transition to projection * Fix transition value * Remove isRenderingDirty and use hasTransition when needed * Remove the last part of the animation handling in the transform class * More clean-up * Remove some "TODO"s. * Remove reference to globe projection in globe transform * Rename newFrame with recalculateCache * Remove unneeded ifs * Add support for arbitrary projection definitions * Uses mercator matrix when globe is using mercator transform * Improve handling of fog martix in globe transform --------- Co-authored-by: Isaac Besora Vilardaga <[email protected]> * Globe custom layer fixes (#5150) * Simplify custom layer ProjectionData code in mercator transform * Fix globe tiles example * Fix globe projection shader * Add custom layer 3D model after globe->mercator transition render test * Fix custom layer 3D models not rendering when globe transitions to mercator * Move camera to center distance to helper # Conflicts: # src/geo/projection/globe_transform.ts # src/geo/projection/mercator_transform.ts # src/geo/transform_helper.ts # src/geo/transform_interface.ts * Synchronize globe+mercator near and far Z to prevent 3D model disappearing during projection transition * Expose getProjectionData and getMatrixForModel for custom layers, don't use internal API in examples * VerticalPerspectiveTransform should have a valid getProjectionDataForCustomLayer implementation * Add changelog entry * Fix missing docs * Fix docs again * Review feedback * Move nearZfarZoverride to transform helper * Update build size * Review feedback * Change near/far Z override API * Custom layers use internal API * Fix globe custom tiles example * Update build size * Fix typo --------- Co-authored-by: HarelM <[email protected]> * Final removal of TODOs and some minor clean up * Update CHANGELOG.md --------- Co-authored-by: Isaac Besora Vilardaga <[email protected]> Co-authored-by: Jakub Pelc <[email protected]>
1 parent a3e98c4 commit 551ba95

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+2469
-1957
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
## main
22

33
### ✨ Features and improvements
4+
- Add support for projection type expression as part of a refactoring of the transfrom and projection classes ([#5139](https://github.com/maplibre/maplibre-gl-js/pull/5139))
45
- _...Add new stuff here..._
56

67
### 🐞 Bug fixes
8+
- Fix globe custom layers being supplied incorrect matrices after projection transition to mercator ([#5150](https://github.com/maplibre/maplibre-gl-js/pull/5150))
9+
- Fix custom 3D models disappearing during projection transition ([#5150](https://github.com/maplibre/maplibre-gl-js/pull/5150))
710
- _...Add new stuff here..._
811

912
## 5.0.0-pre.9

src/geo/projection/camera_helper.ts

+70-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import type Point from '@mapbox/point-geometry';
1+
import Point from '@mapbox/point-geometry';
22
import {type IReadonlyTransform, type ITransform} from '../transform_interface';
33
import {type LngLat, type LngLatLike} from '../lng_lat';
44
import {type CameraForBoundsOptions, type PointLike} from '../../ui/camera';
55
import {type PaddingOptions} from '../edge_insets';
66
import {type LngLatBounds} from '../lng_lat_bounds';
7-
import {getRollPitchBearing, type RollPitchBearing, rollPitchBearingToQuat, warnOnce} from '../../util/util';
7+
import {degreesToRadians, getRollPitchBearing, type RollPitchBearing, rollPitchBearingToQuat, scaleZoom, warnOnce, zoomScale} from '../../util/util';
88
import {quat} from 'gl-matrix';
99
import {interpolates} from '@maplibre/maplibre-gl-style-spec';
10+
import {projectToWorldCoordinates, unprojectFromWorldCoordinates} from './mercator_utils';
1011

1112
export type MapControlsDeltas = {
1213
panDelta: Point;
@@ -151,3 +152,70 @@ export function updateRotation(args: UpdateRotationArgs) {
151152
args.tr.setBearing(interpolates.number(args.startEulerAngles.bearing, args.endEulerAngles.bearing, args.k));
152153
}
153154
}
155+
156+
export function cameraForBoxAndBearing(options: CameraForBoundsOptions, padding: PaddingOptions, bounds: LngLatBounds, bearing: number, tr: IReadonlyTransform): CameraForBoxAndBearingHandlerResult {
157+
const edgePadding = tr.padding;
158+
159+
// Consider all corners of the rotated bounding box derived from the given points
160+
// when find the camera position that fits the given points.
161+
162+
const nwWorld = projectToWorldCoordinates(tr.worldSize, bounds.getNorthWest());
163+
const neWorld = projectToWorldCoordinates(tr.worldSize, bounds.getNorthEast());
164+
const seWorld = projectToWorldCoordinates(tr.worldSize, bounds.getSouthEast());
165+
const swWorld = projectToWorldCoordinates(tr.worldSize, bounds.getSouthWest());
166+
167+
const bearingRadians = degreesToRadians(-bearing);
168+
169+
const nwRotatedWorld = nwWorld.rotate(bearingRadians);
170+
const neRotatedWorld = neWorld.rotate(bearingRadians);
171+
const seRotatedWorld = seWorld.rotate(bearingRadians);
172+
const swRotatedWorld = swWorld.rotate(bearingRadians);
173+
174+
const upperRight = new Point(
175+
Math.max(nwRotatedWorld.x, neRotatedWorld.x, swRotatedWorld.x, seRotatedWorld.x),
176+
Math.max(nwRotatedWorld.y, neRotatedWorld.y, swRotatedWorld.y, seRotatedWorld.y)
177+
);
178+
179+
const lowerLeft = new Point(
180+
Math.min(nwRotatedWorld.x, neRotatedWorld.x, swRotatedWorld.x, seRotatedWorld.x),
181+
Math.min(nwRotatedWorld.y, neRotatedWorld.y, swRotatedWorld.y, seRotatedWorld.y)
182+
);
183+
184+
// Calculate zoom: consider the original bbox and padding.
185+
const size = upperRight.sub(lowerLeft);
186+
187+
const availableWidth = (tr.width - (edgePadding.left + edgePadding.right + padding.left + padding.right));
188+
const availableHeight = (tr.height - (edgePadding.top + edgePadding.bottom + padding.top + padding.bottom));
189+
const scaleX = availableWidth / size.x;
190+
const scaleY = availableHeight / size.y;
191+
192+
if (scaleY < 0 || scaleX < 0) {
193+
cameraBoundsWarning();
194+
return undefined;
195+
}
196+
197+
const zoom = Math.min(scaleZoom(tr.scale * Math.min(scaleX, scaleY)), options.maxZoom);
198+
199+
// Calculate center: apply the zoom, the configured offset, as well as offset that exists as a result of padding.
200+
const offset = Point.convert(options.offset);
201+
const paddingOffsetX = (padding.left - padding.right) / 2;
202+
const paddingOffsetY = (padding.top - padding.bottom) / 2;
203+
const paddingOffset = new Point(paddingOffsetX, paddingOffsetY);
204+
const rotatedPaddingOffset = paddingOffset.rotate(degreesToRadians(bearing));
205+
const offsetAtInitialZoom = offset.add(rotatedPaddingOffset);
206+
const offsetAtFinalZoom = offsetAtInitialZoom.mult(tr.scale / zoomScale(zoom));
207+
208+
const center = unprojectFromWorldCoordinates(
209+
tr.worldSize,
210+
// either world diagonal can be used (NW-SE or NE-SW)
211+
nwWorld.add(seWorld).div(2).sub(offsetAtFinalZoom)
212+
);
213+
214+
const result = {
215+
center,
216+
zoom,
217+
bearing
218+
};
219+
220+
return result;
221+
}

src/geo/projection/covering_tiles.test.ts

+9-13
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
11
import {beforeEach, describe, expect, test} from 'vitest';
22
import {GlobeTransform} from './globe_transform';
3-
import {globeConstants, type GlobeProjection} from './globe';
4-
import {getGlobeProjectionMock} from '../../util/test/util';
53
import {LngLat} from '../lng_lat';
64
import {coveringTiles, coveringZoomLevel, type CoveringZoomOptions} from './covering_tiles';
75
import {OverscaledTileID} from '../../source/tile_id';
86
import {MercatorTransform} from './mercator_transform';
7+
import {globeConstants} from './vertical_perspective_projection';
98

109
describe('coveringTiles', () => {
1110
describe('globe', () => {
12-
let globeProjectionMock: GlobeProjection;
1311

1412
beforeEach(() => {
15-
globeProjectionMock = getGlobeProjectionMock();
1613
// Force faster animations so we can use shorter sleeps when testing them
17-
globeConstants.globeTransitionTimeSeconds = 0.1;
1814
globeConstants.errorTransitionTimeSeconds = 0.1;
1915
});
2016

2117
test('zoomed out', () => {
22-
const transform = new GlobeTransform(globeProjectionMock);
18+
const transform = new GlobeTransform();
2319
transform.resize(128, 128);
2420
transform.setCenter(new LngLat(0.0, 0.0));
2521
transform.setZoom(-1);
@@ -34,7 +30,7 @@ describe('coveringTiles', () => {
3430
});
3531

3632
test('zoomed in', () => {
37-
const transform = new GlobeTransform(globeProjectionMock);
33+
const transform = new GlobeTransform();
3834
transform.resize(128, 128);
3935
transform.setCenter(new LngLat(-0.02, 0.01));
4036
transform.setZoom(3);
@@ -52,7 +48,7 @@ describe('coveringTiles', () => {
5248
});
5349

5450
test('zoomed in 512x512', () => {
55-
const transform = new GlobeTransform(globeProjectionMock);
51+
const transform = new GlobeTransform();
5652
transform.resize(512, 512);
5753
transform.setCenter(new LngLat(-0.02, 0.01));
5854
transform.setZoom(3);
@@ -78,7 +74,7 @@ describe('coveringTiles', () => {
7874
});
7975

8076
test('pitched', () => {
81-
const transform = new GlobeTransform(globeProjectionMock);
77+
const transform = new GlobeTransform();
8278
transform.resize(128, 128);
8379
transform.setCenter(new LngLat(-0.002, 0.001));
8480
transform.setZoom(8);
@@ -98,7 +94,7 @@ describe('coveringTiles', () => {
9894
});
9995

10096
test('pitched+rotated', () => {
101-
const transform = new GlobeTransform(globeProjectionMock);
97+
const transform = new GlobeTransform();
10298
transform.resize(128, 128);
10399
transform.setCenter(new LngLat(-0.002, 0.001));
104100
transform.setZoom(8);
@@ -123,7 +119,7 @@ describe('coveringTiles', () => {
123119
});
124120

125121
test('antimeridian1', () => {
126-
const transform = new GlobeTransform(globeProjectionMock);
122+
const transform = new GlobeTransform();
127123
transform.resize(128, 128);
128124
transform.setCenter(new LngLat(179.99, -0.001));
129125
transform.setZoom(5);
@@ -141,7 +137,7 @@ describe('coveringTiles', () => {
141137
});
142138

143139
test('antimeridian2', () => {
144-
const transform = new GlobeTransform(globeProjectionMock);
140+
const transform = new GlobeTransform();
145141
transform.resize(128, 128);
146142
transform.setCenter(new LngLat(-179.99, 0.001));
147143
transform.setZoom(5);
@@ -159,7 +155,7 @@ describe('coveringTiles', () => {
159155
});
160156

161157
test('zoom < 0', () => {
162-
const transform = new GlobeTransform(globeProjectionMock);
158+
const transform = new GlobeTransform();
163159
transform.resize(128, 128);
164160
transform.setCenter(new LngLat(0.0, 80.0));
165161
transform.setZoom(-0.5);

src/geo/projection/covering_tiles.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import {OverscaledTileID} from '../../source/tile_id';
22
import {vec2, type vec4} from 'gl-matrix';
3-
import {type IReadonlyTransform} from '../transform_interface';
43
import {MercatorCoordinate} from '../mercator_coordinate';
5-
import {scaleZoom} from '../transform_helper';
6-
import {clamp, degreesToRadians} from '../../util/util';
7-
import {type Terrain} from '../../render/terrain';
8-
import {type Frustum} from '../../util/primitives/frustum';
4+
import {clamp, degreesToRadians, scaleZoom} from '../../util/util';
95
import {type Aabb, IntersectionResult} from '../../util/primitives/aabb';
106

7+
import type {IReadonlyTransform} from '../transform_interface';
8+
import type {Terrain} from '../../render/terrain';
9+
import type {Frustum} from '../../util/primitives/frustum';
10+
1111
type CoveringTilesResult = {
1212
tileID: OverscaledTileID;
1313
distanceSq: number;

src/geo/projection/covering_tiles_details_provider.ts

+5
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,9 @@ export interface CoveringTilesDetailsProvider {
3636
* Whether to allow world copies to be rendered.
3737
*/
3838
allowWorldCopies: () => boolean;
39+
40+
/**
41+
* Prepare cache for the next frame.
42+
*/
43+
recalculateCache(): void;
3944
}

0 commit comments

Comments
 (0)