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

Pitch > 90 degrees #4851

Merged
merged 77 commits into from
Oct 28, 2024
Merged

Pitch > 90 degrees #4851

merged 77 commits into from
Oct 28, 2024

Conversation

NathanMOlson
Copy link
Contributor

@NathanMOlson NathanMOlson commented Oct 17, 2024

Allow pitch angle > 90 degrees. #4717

Fixes #3683

Changes:

  1. Add centerClampedToGround configuration variable. The default is true, which maintains previous behavior. To use pitch angles higher than 90 degrees, this must be set to true, and the elevation must be provided. The elevation must be floating in space above the ground to keep the camera from going below ground.
  2. Add elevation to JumpToOptions. This is needed to set camera pitch > 90 degrees, as the center point must be above the camera, which must be above the terrain.
  3. Extend maxPitch to 180.
  4. Change recalculateZoom() to keep the camera position constant. This means that it now adjusts the center point, in addition to zoom and elevation. This is important to keep the camera from jumping when terrain is updated behind the scenes. This is also the biggest change and the one with the most potential for unwanted ripples.

demo: https://nathanmolson.github.io/camera-centric
image

TODO

  • Figure out whether elevation needs to be added to style spec. Design Proposal: Add Center Altitude maplibre-style-spec#851

  • Confirm your changes do not include backports from Mapbox projects (unless with compliant license) - if you are not sure about this, please ask!

  • Briefly describe the changes in this PR.

  • Link to related issues.

  • Include before/after visuals or gifs if this PR includes visual changes.

  • Write tests for all new functionality.

  • Document any changes to public APIs.

  • Add an entry to CHANGELOG.md under the ## main section.

Additional details about the center point elevation:
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).

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.

image

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

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

image
image

In most cases, having the camera underground is undesirable.

To help users position the camera, a new function calculateCameraOptionsFromCameraLngLatAltRotation() has been added to Camera.

@codecov-commenter
Copy link

codecov-commenter commented Oct 17, 2024

Codecov Report

Attention: Patch coverage is 92.85714% with 12 lines in your changes missing coverage. Please review.

Project coverage is 90.37%. Comparing base (d42e0da) to head (53e5f0e).
Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
src/geo/projection/globe_transform.ts 0.00% 8 Missing ⚠️
src/ui/camera.ts 93.02% 0 Missing and 3 partials ⚠️
src/ui/handler_manager.ts 83.33% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4851      +/-   ##
==========================================
+ Coverage   89.94%   90.37%   +0.42%     
==========================================
  Files         265      265              
  Lines       37977    38095     +118     
  Branches     2529     3191     +662     
==========================================
+ Hits        34159    34427     +268     
+ Misses       2951     2696     -255     
- Partials      867      972     +105     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@HarelM
Copy link
Collaborator

HarelM commented Oct 17, 2024

Is it possible to move some parts of this to a plugin? Or maybe place that in an example instead?
I would like to keep changes to the public API as minimal as possible for what I believe is a niche requirement, if possible.

@NathanMOlson
Copy link
Contributor Author

Is it possible to move some parts of this to a plugin? Or maybe place that in an example instead?
I would like to keep changes to the public API as minimal as possible for what I believe is a niche requirement, if possible.

Certainly the jumpToLLA() function and it's supporting changes can be pulled out to a plugin.

I think the other changes (change in recalculateZoom(), increase of maxPitch to 180, and addition of elevation to JumpToOptions) need to stay. The only change affecting the public API would be the addition of elevation to JumpToOptions.

@HarelM
Copy link
Collaborator

HarelM commented Oct 18, 2024

How does elevation and zoom correlate? Can one set an elevation value that will change the zoom value?
It might be better to start with a design before jumping to implementation to see that there is an alignment on what should be the solution...

@HarelM HarelM requested a review from ibesora October 18, 2024 04:03
@NathanMOlson
Copy link
Contributor Author

How does elevation and zoom correlate? Can one set an elevation value that will change the zoom value?

elevation sets the height of the "center point" above sea level. When 3D terrain is used, this is the height of the terrain at the center point, otherwise it is zero. To allow pitch > 90, I intend to position the "center point" floating in space above the terrain. If the center point remains tied to terrain, then pitch > 90 will cause the camera to move under the terrain.

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.

image

To allow pitch > 90, I want to move the "center point" off of the ground. This will allow the camera to stay above the ground when it pitches above 90.

image

It might be better to start with a design before jumping to implementation to see that there is an alignment on what should be the solution...

Where/how should I present the design?

@NathanMOlson NathanMOlson changed the title Camera-centric Pitch > 90 degrees Oct 18, 2024
@NathanMOlson
Copy link
Contributor Author

I've reduced the scope of this PR to only changes that allow pitch greater than 90 degrees.

@HarelM
Copy link
Collaborator

HarelM commented Oct 18, 2024

The design can be presented either as an initial comment of an issue, and initial comment in a PR or a discussion.
I would advise to present it before diving into code changes as it makes changes harder when you "fall in love" with a solution :-)

In any case, the diagram above makes things a lot more clearer.
The first question that comes to mind though is how would this support pitch of 180 degrees? Elevation would be set to infinity?

src/ui/map.ts Outdated Show resolved Hide resolved
src/ui/map.ts Outdated Show resolved Hide resolved
@NathanMOlson
Copy link
Contributor Author

The first question that comes to mind though is how would this support pitch of 180 degrees? Elevation would be set to infinity?

No need to set elevation to infinity. It just needs to be set high enough that the camera is above the terrain.

image

src/ui/map.ts Outdated Show resolved Hide resolved
@HarelM
Copy link
Collaborator

HarelM commented Oct 18, 2024

Is it possible to clean the git history as well? Sorry for not picking on that, but it makes review harder because I need to expand every time I look at this PR as git thinks there's a lot of commits...

@HarelM
Copy link
Collaborator

HarelM commented Oct 18, 2024

Can you elaborate a bit of the center change that is added.
I know that we already suffer from the zoom change at the end of a panning when terrain is on, can you explain what is expected to change here?
A video or drawing works be great.
Thanks!!

package.json Outdated Show resolved Hide resolved
src/geo/transform_helper.ts Outdated Show resolved Hide resolved
@HarelM
Copy link
Collaborator

HarelM commented Oct 22, 2024

@kubapelc @ibesora would you like to go over this PR as well?
I think it's currently in a good state to be merged.
It is only an opt-in behavior basically that currently can't really be used by current handlers, which I'm not sure I know how I feel about it...
@NathanMOlson does this PR requires the following PR?

@NathanMOlson
Copy link
Contributor Author

@NathanMOlson does this PR requires the following PR?

No, those changes are independent.

Copy link
Collaborator

@ibesora ibesora left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left some minor comments around the code, please take a look.
One thing that worries me is that using your example I'm able to go below earth. Shouldn't that be impossible?

Screenshot 2024-10-23 at 10 55 00

developer-guides/center-point.md Outdated Show resolved Hide resolved
src/geo/projection/mercator_transform.ts Outdated Show resolved Hide resolved
src/style/style.ts Show resolved Hide resolved
@NathanMOlson
Copy link
Contributor Author

One thing that worries me is that using your example I'm able to go below earth. Shouldn't that be impossible?

I don't think it should be fundamentally impossible.

You can make it impossible by setting maxPitch to 90, or by setting the right combination of maxPitch, minZoom, and elevation (for example, setting minZoom to 14.75 in the example, where maxPitch=105 and elevation=541).

@NathanMOlson
Copy link
Contributor Author

NathanMOlson commented Oct 23, 2024

One thing that worries me is that using your example I'm able to go below earth. Shouldn't that be impossible?

I don't think it should be fundamentally impossible.

Maybe a good compromise between "going underground is impossible" and "no fundamental limits on going underground" is "mouse/touch navigation never puts the camera underground".

Here's a proof of concept that ignores mouse/touch commands that result in the camera being underground. It works in the center-point demo (which doesn't have terrain).

Changes to mercator_camera_helper.ts:

     handleMapControlsRollPitchBearingZoom(deltas: MapControlsDeltas, tr: ITransform): void {
         if (deltas.bearingDelta) tr.setBearing(tr.bearing + deltas.bearingDelta);
-        if (deltas.pitchDelta) tr.setPitch(tr.pitch + deltas.pitchDelta);
+        if (deltas.pitchDelta) {
+            const originalPitch = tr.pitch;
+            tr.setPitch(tr.pitch + deltas.pitchDelta);
+            if (tr.getCameraAltitude() < 0) {
+                tr.setPitch(originalPitch);
+            }
+        }
         if (deltas.rollDelta) tr.setRoll(tr.roll + deltas.rollDelta);
-        if (deltas.zoomDelta) tr.setZoom(tr.zoom + deltas.zoomDelta);
+        if (deltas.zoomDelta) {
+            const originalZoom = tr.zoom;
+            tr.setZoom(tr.zoom + deltas.zoomDelta);
+            if (tr.getCameraAltitude() < 0) {
+                tr.setZoom(originalZoom);
+            }
+        }
     }

To fully work, this would need to support terrain, and would probably have some glitches due to the fact that terrain is constantly changing as the resolution of queried tiles changes. In the terrain case, it might be better to either accept/reject the entire deltas struct instead of the individual components.

@HarelM
Copy link
Collaborator

HarelM commented Oct 24, 2024

For terrain the following method is being used to make sure the camera is not inside the terrain:

_elevateCameraIfInsideTerrain(tr: ITransform) : { pitch?: number; zoom?: number } {
const cameraLngLat = tr.screenPointToLocation(tr.getCameraPoint());
const cameraAltitude = tr.getCameraAltitude();
const minAltitude = this.terrain.getElevationForLngLatZoom(cameraLngLat, tr.zoom);
if (cameraAltitude < minAltitude) {
const newCamera = this.calculateCameraOptionsFromTo(
cameraLngLat, minAltitude, tr.center, tr.elevation);
return {
pitch: newCamera.pitch,
zoom: newCamera.zoom,
};
}
return {};
}

Which is being used by a global transform update for every movement of the camera:
_applyUpdatedTransform(tr: ITransform) {

I'm guessing this should be similar, or the elevateCameraIfInsideTheTerrain can be updated to take into account when the pitch is causing the camera to maybe go underground...

@NathanMOlson
Copy link
Contributor Author

We could easily update _elevateCameraIfInsideTheTerrain() to handle the no-terrain case.

There are two things I don't like about applying the limit there.

  1. It applies to all transform updates, making it impossible to place the camera underground in cases where that is the desired behavior.
  2. At that point we have lost the reason why the camera went underground. We just have a transform that puts the camera underground, and we always "fix" it the same way: by raising the camera altitude. This feels strange when the camera entered the ground due to zooming out, but rather than simply limiting the zoom, the correction primarily uses pitch. I would prefer if I try to zoom out to place the camera underground, my zoom change is rejected instead of causing a pitch change.

@HarelM
Copy link
Collaborator

HarelM commented Oct 25, 2024

For me it feels like solving the same issue in different places, so I prefer to have a single point that handles this, it doesn't have to be the current solution.

@NathanMOlson
Copy link
Contributor Author

I've moved the "underground" check in the no-terrain case to _elevateCameraIfInsideTerrain().

@HarelM HarelM enabled auto-merge (squash) October 28, 2024 21:29
@HarelM HarelM merged commit 49d7fc9 into maplibre:main Oct 28, 2024
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Change altitude of look at point?
5 participants