maplibre-gl-js icon indicating copy to clipboard operation
maplibre-gl-js copied to clipboard

Globe - controls, animations & transform+projection changes

Open kubapelc opened this issue 7 months ago • 5 comments

Globe - transform+projection changes

I've removed all changes to camera controls and animations for easier reviewing (they will come later), but I'll keep the changelog as it was. The demo still includes controls changes, but if you compile from source, you won't have them.

Issue & discussion of globe:

https://github.com/maplibre/maplibre/issues/190 https://github.com/maplibre/maplibre/discussions/161

Changes

Controls for globe - I believe these should now feel natural, but here is a quick summary of how they work and what edge cases are handled:

  • camera can now look at (or nearly at) the poles at any zoom level
  • panning works in a way that is very similar to mercator, and converges to mercator controls at high zooms
    • this means that the pan speed of longitude is increased as the camera moves nearer to the poles
    • at low zooms however, longitude pan speed is reduced when near the poles to prevent the globe from spinning wildly
  • zooming will now correctly keep the location under the cursor in the same place, as it does in mercator
    • this behaviour is not used near the planet edges, as it would move the camera too quickly and eventually cause glitches - an approximation is seamlessly used instead when cursor is near the horizon
    • zooming now works even if cursor is not on the planet surface at all - in this case the map zooms to a location near the horizon that is closest to the cursor
    • additionaly when zooming in/out with the cursor being placed too far away from the planet, the map is prevented from panning too quickly

Camera animations and functions implemented for globe projection:

  • fitBounds - correctly computes zoom & center to fit a given box, respects to planet curvature & padding
  • flyTo
  • easeTo
  • jumpTo

Other changes:

  • Transform is now an abstract class, with specialized implementations for every projection
    • moved most functions from the Projection interface to the Transform class
  • Projection interface is now much simpler, and mostly used for things with are independent of the map's state
  • implemented all relevant projection and unprojection functions for globe
    • unprojecting a pixel that does not lie on the planet surface will instead return the location on the planet horizon that is nearest to the pixel in screenspace
  • removed posMatrix - a per-tile projection matrix that was specific to Mercator projection and was explicitly used and passed to shaders, stored in the Tile class, etc. Mercator now supplies this matrix through the same mechanism as globe (getProjectionData function).
  • further symbol refactor - simplified projection code, removed all mercator-only code paths - the Projection interface (or rather the now general Transform class) is used instead, handling both projections as needed
  • changing map projection will now reload all tiles

Zoom & apparentZoom

Under globe, the planet's size is automatically adjusted when latituded is changed so that the map's center always exactly matches a mercator map. For example, zoom level 0 at equator will result in a small planet, but near Greenland it would be much bigger (think of how oversized Greenland is under mercator projection). Changing projection for a given map center & zoom will however display the exact same location with the exact same size thanks to this behaviour (experiment with the globe toggle in the demo!) Under globe, panning and other map controls automatically counteract this effect to keep the planet size constant, unless the user actually wants to zoom in.

This weird relation of planet size to zoom level can have unexpected effects when programmatic animations are used. Imagine you want to fly from Greenland at zoom 4 to the equator at zoom 4 with globe projection. You would expect the planet size to remain constant, since the zoom levels are the same, but it would actually decrease a lot at the equator!

For this reason most animations have a new optional parameter in options: apparentZoom. This exists alongside the already present zoom paremeter and either can be used. It specifies target "planet size" in relation to the starting planet size. So if want to fly from Greenland to equator, starting map zoom is 4 and target apparentZoom is 4, the final planet size will be the same as the starting size! If we would instead specify apparentZoom: 5, the planet at the end of the animation would be twice as large as at the start, if apparentZoom: 3 it would be half size, etc. If globe is disabled, apparentZoom works exactly like the old zoom option.

What is left

Globe should now be mostly usable. Here are the things that don't yet work:

  • custom layer API for globe
  • 3D model API for globe
  • markers are not hidden/faded when behind planet horizon
  • queryRenderedFeatures
  • globe transform cannot be constrained at the moment - it is unclear how constraining should be implemented, since snapping the camera so that out of bounds regions aren't at all visible would probably feel very unnatural
  • terrain - I'm not sure I'm going to be implementing that though

Other bugs at the moment:

  • Markers do not respect terrain depth, even on mercator projection. Hence the failing unit test. I have no idea what caused this...
  • options.offset semantics for camera animations (flyTo, easeTo) differ across globe and mercator, and I think the mercator implementation is actually bugged? (At least it does something different that the docs claim it should.)

Demo link

Play around with new globe controls here.

Launch Checklist

  • [x] Confirm your changes do not include backports from Mapbox projects (unless with compliant license) - if you are not sure about this, please ask!
  • [x] Briefly describe the changes in this PR.
  • [x] Link to related issues.
  • ~~[ ] Include before/after visuals or gifs if this PR includes visual changes.~~
  • [x] Write tests for all new functionality. - mostly includes tests for new globe projection/unprojection functions and for camera animations
  • [x] Document any changes to public APIs.
  • ~~[ ] Post benchmark scores.~~
  • ~~[ ] Add an entry to CHANGELOG.md under the ## main section.~~

kubapelc avatar Jun 28 '24 12:06 kubapelc