Skip to content

Commit

Permalink
Pitch > 90 degrees (#4851)
Browse files Browse the repository at this point in the history
* squashed changes to support pitch > 90

* fix merge conflicts

* fix changelog screwup

* fix lint and adjust build size

* better naming

* remove jumpToLLA (it can be made a plugin)

* fix render tests by breaking camera-centric behavior

* better naming

* add setRoll operation

* fix pitch95 render test

* add pitch=90 roll=135 render test

* add render test for pitch95 with terrain

* test getMercatorHorizon() at high pitch

* add test for recalculateZoomAndCenter without terrain

* documentation

* <89 -> <=89

* LLA -> LngLatAlt

* fix lint

* Merc -> Mercator

* add public API function setCenterElevation(), and remove render test modification

* don't let camera distances go negative

* change the mercator horizon heuristic to be continuous, and update render tests.

* function to only set elevation if center point is below horizon

* update sky render tests

* update changelog

* revert unintended package changes.

* revert unnecessarry shader change

* updated maxPitch docs

* fix spelling

Co-authored-by: Harel M <[email protected]>

* add named constant maxMercatorHorizonAngle

* use roll and pitch spec definitions in sky render tests

* move __setElevationIfCenterPointBelowHorizon() from Transform to Map

* change mercator horizon limit from 89 to 89.25, so that horizon heuristic is unchanged below the old maximumpitch angle of 85 degrees.

* update comment

* fix lint

* update unit tests for new horizon heuristic

* revert unchanged render test results

* add switch to control "centerClampedToGround" behavior

* fix render tests

* Update src/geo/projection/mercator_utils.ts

* Apply suggestions from code review

* Update src/geo/transform_interface.ts

* Don't update center point if there is nothing to do (this was causing issues with mouse pitch control at high pitch)

* Add an example showing center point at the top of a building and pitch > 90

* remove warnings about pitch angles > 60.

* move cameraMercatorCoordinateFromCenterAndRotation() to mercator_utils

* Restore old setLocationAtPoint(). This fixes unit tests but breaks `center-point` example :(

* better unit test coverage and documentation for calculateCenterFromCameraLngLatAlt()

* add @internal

* code cleanup

* Add calculateCameraOptionsFromCameraLngLatAltRotation() and unit tests

* add developer guide describing center point.

* move high-pitch render tests to high-pitch folder

* fix build

* update high pitch + terrain render test to remove setPitch() call

* use transform's elevation in setLocationAtPoint()

* fix jumps when using the mouse to rotate past 90 degrees pitch

* further safety against numerical issues near the horizon

* fix spelling

* make centerClampedToGround get/set symmetric

* split recalculateZoomAndCenter unit tests.

* simplify center-point example

* add examples for calculateCameraOptionsFromTo() and calculateCameraOptionsFromCameraLngLatAltRotation()

* origPixelPerMeter -> originalPixelPerMeter

* whitespace

* simplify center-point example

* set minZoom in example so we don't lose the map

* remove unnecessary limit on _farZ

* change pseudocode to Typescript

* add unit tests to cover various code paths in _updateMapTransform()

* update build size

* Update developer-guides/center-point.md

Co-authored-by: Isaac Besora Vilardaga <[email protected]>

* use local variables

* move "underground" check to _elevateCameraIfInsideTerrain()

* fix high-pitch render tests, which were having their pitch angle modified by _elevateCameraIfInsideTerrain()

* return early in case of no terrain and pitch < 90

---------

Co-authored-by: Harel M <[email protected]>
Co-authored-by: Isaac Besora Vilardaga <[email protected]>
  • Loading branch information
3 people authored Oct 28, 2024
1 parent 20ce115 commit 49d7fc9
Show file tree
Hide file tree
Showing 33 changed files with 988 additions and 84 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### ✨ Features and improvements
- ⚠️ Changed `geometry-type` to identify "Multi-" features ([#4877](https://github.com/maplibre/maplibre-gl-js/pull/4877))
- Add support for pitch > 90 degrees ([#4717](https://github.com/maplibre/maplibre-gl-js/issues/4717))
- _...Add new stuff here..._

### 🐞 Bug fixes
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added developer-guides/assets/center-point_nominal.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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions developer-guides/center-point.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# How Camera position is calculated

This guide describes how camera position is calculated from the center point, zoom, and camera rotation.
The `Transform` variables `center`, `elevation`, `zoom`, `pitch`, `bearing`, and `fov` control the location of the camera indirectly.

`elevation` sets the height of the "center point" above sea level. In the typical use case (`centerClampedToGround = true`), the library modifies `elevation` in an attempt to keep the center point always on the terrain (or 0 MSL if no terrain is enabled). When `centerClampedToGround = false`, the user provides the elevation of the center point.

`zoom` sets the distance from the center point to the camera (in conjunction with `fovInRadians`, which is currently hardcoded).

Together, `zoom`, `elevation`, and `pitch` set the altitude of the camera:

See `MercatorTransform::getCameraAltitude()`:
```typescript
getCameraAltitude(): number {
const altitude = Math.cos(this.pitchInRadians) * this._cameraToCenterDistance / this._helper._pixelPerMeter;
return altitude + this.elevation;
}
```

![image](assets/center-point_nominal.png)

To allow pitch > 90, the "center point" must be placed off of the ground. This will allow the camera to stay above the ground when it pitches above 90. This requires setting `centerClampedToGround = false`.

![image](assets/center-point_high-pitch.png)

The same math applies whether the center point is on terrain or not, and whether the camera is above or below the ground:

![image](assets/center-point_straight-up.png)
![image](assets/center-point_underground.png)


To help users position the camera, `Camera` exports the function `calculateCameraOptionsFromCameraLngLatAltRotation()`.
Binary file added docs/assets/examples/center-point.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 10 additions & 2 deletions src/geo/projection/globe_transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -701,8 +701,8 @@ export class GlobeTransform implements ITransform {
return globeCoveringTiles(this._cachedFrustum, this._cachedClippingPlane, cameraCoord, centerCoord, coveringZ, options);
}

recalculateZoom(terrain: Terrain): void {
this._mercatorTransform.recalculateZoom(terrain);
recalculateZoomAndCenter(terrain?: Terrain): void {
this._mercatorTransform.recalculateZoomAndCenter(terrain);
this.apply(this._mercatorTransform);
}

Expand All @@ -719,6 +719,10 @@ export class GlobeTransform implements ITransform {
return this._mercatorTransform.getCameraAltitude();
}

getCameraLngLat(): LngLat {
return this._mercatorTransform.getCameraLngLat();
}

lngLatToCameraDepth(lngLat: LngLat, elevation: number): number {
if (!this.isGlobeRendering) {
return this._mercatorTransform.lngLatToCameraDepth(lngLat, elevation);
Expand Down Expand Up @@ -827,6 +831,10 @@ export class GlobeTransform implements ITransform {
};
}

calculateCenterFromCameraLngLatAlt(ll: LngLat, alt: number, bearing?: number, pitch?: number): {center: LngLat; elevation: number; zoom: number} {
return this._mercatorTransform.calculateCenterFromCameraLngLatAlt(ll, alt, bearing, pitch);
}

/**
* Note: automatically adjusts zoom to keep planet size consistent
* (same size before and after a {@link setLocationAtPoint} call).
Expand Down
6 changes: 6 additions & 0 deletions src/geo/projection/mercator_camera_helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ export class MercatorCameraHelper implements ICameraHelper {
}

handleMapControlsPan(deltas: MapControlsDeltas, tr: ITransform, preZoomAroundLoc: LngLat): void {
// If we are rotating about the center point, there is no need to update the transform center. Doing so causes
// a small amount of drift of the center point, especially when pitch is close to 90 degrees.
// In this case, return early.
if (deltas.around.distSqr(tr.centerPoint) < 1.0e-2) {
return;
}
tr.setLocationAtPoint(preZoomAroundLoc, deltas.around);
}

Expand Down
Loading

0 comments on commit 49d7fc9

Please sign in to comment.