mapbox / mapbox-gl-js

Interactive, thoroughly customizable maps in the browser, powered by vector tiles and WebGL
https://docs.mapbox.com/mapbox-gl-js/
Other
11.12k stars 2.21k forks source link

Feature request: camera elevation #3552

Open IvanSanchez opened 7 years ago

IvanSanchez commented 7 years ago

Motivation

With extruded geometries, there are use cases where the focus should be on a specific elevation.

The most obvious is to focus on a building rooftop with a call to Map#flyTo. With more 3D data, users might want to have a control to travel "up" and "down" besides moving N-S-W-E.

Design

I suggest a new property in CameraOptions, named height or elevation, e.g.:

map.flyTo({
  center: [ 63.5, 10.4 ],
  zoom: 17,
  bearing: 15,
  pitch: 45,
  elevation: 100  // assumed meters, or CRS units, as in the extrude height
})

Implementation

I guess the internal camera state shall store the camera elevation somewhere, and modify the 3D transform matrix as appropriate.

This might change the viewport bounds, so the calculations for the tile ranges might change as well (my knowledge of the internals of this project is low, so I don't know if the 3D transform matrix is reused for this purpose).

Any easing function for the camera state (flyTo animations and the like) shall also ease and interpolate the elevation.

1ec5 commented 7 years ago

First of all, any elevation option would be mutually exclusive with the existing zoom option, because they’re competing ways of scaling the map. Zoom level–based APIs are appropriate for aligning with a given function stop in a style layer, whereas altitude-based APIs are appropriate for aligning to real-world features. (This is, after all, a map rather than an image viewer.)

Specifically, imagine you want to set a camera that comfortably clears the tallest building in a city. You could do it in one line by saying you want a camera with a certain altitude. Or you can guess and check until you come up with the right zoom level. But guessing and checking only works if you hard-code a specific city and a specific building.

flyTo demonstrates this point nicely. A developer can only intuit a value for the minZoom option if they’ve designed the map themselves and want to ensure that a certain function stop is reached. But if the goal is to simulate aircraft flying at a certain altitude – say, for a game – a peakAltitude option would be far more intuitive.

The Mapbox iOS and macOS SDKs have an implementation of the requested feature in the altitude property of MGLMapCamera. You can set the map’s zoom level, or you can set the altitude, but not both at the same time. For a tilted map, you can create the camera in two ways: either based on the viewer’s height above the ground or based on the center point’s viewing distance (since Mapbox GL doesn’t offer any way to change the focal point). Unfortunately, the conversion between zoom levels and altitudes is fraught, but there would be a real benefit for developers who need animations with a relation to the real world.

ansis commented 7 years ago

@1ec5 I agree with what you wrote but I understood the request slightly differently. You're thinking about the altitude of the camera but I think @IvanSanchez is asking about the altitude of the point the camera is looking at. This fits in which the current CameraOptions which describes what the camera is looking at and how, not where the camera is.

Right now you can only say "look at the point at 0m above ground level at [-79.3, 43.6]" but the suggestion is to support saying "look at the point at 100m above ground level at [-79.3, 43.6]".

I think this makes sense to support.

Changing the 0 on this line in transform.js should be what we need on the projection side.

Tile loading might automatically adapt or it might need some changes. And once that is working, if you set it to something too high for the current zoom it will try to load too many tiles. We need a temporary limit on this. The level-of-detail tile loading we need to implement for pitched view should let us remove this limit eventually.

IvanSanchez commented 7 years ago

I think @IvanSanchez is asking about the altitude of the point the camera is looking at.

Yes, yes, yes, yes. Sorry if I was unclear in my first explanation.

The main use case I have in mind is being able to perform flyTo calls which centers the map on building rooftops, not on the building footprints, regardless of tilt and bearing.

adamduncan commented 7 years ago

Hey @ansis, @IvanSanchez, has there been any more exploration of this approach? Am currently working on fitBounds-ing some polygon extrusions with known heights, but it's all a bit chicken and egg.

TheMapSmith commented 6 years ago

Hi everyone, I'm thinking about something similar for an app I'm working on. I'm animating airline flights and I'm wanting to make it roughly POV from the airplane. I was hoping for an altitude parameter in AnimationOptions or CameraOptions so I can vary the view across the flight from takeoff to cruising to landing.

strangerintheq commented 5 years ago

Voting up for this feature. In case of showing of buildings overlaying mapbox.

andrewharvey commented 5 years ago

I've hacked together some code to workaround the lack of core support https://bl.ocks.org/andrewharvey/a0b56df5d605d86facc3da9723a9ef6e. It will find the corresponding map center/zoom to use to look at a point at an elevation. It's not the best code, and might not cover all cases, but it seems to work okay so far.

@1ec5 and @ansis raise good points in https://github.com/mapbox/mapbox-gl-js/issues/3552#issuecomment-260753669 and https://github.com/mapbox/mapbox-gl-js/issues/3552#issuecomment-260801024

First of all, any elevation option would be mutually exclusive with the existing zoom option, because they’re competing ways of scaling the map. Zoom level–based APIs are appropriate for aligning with a given function stop in a style layer, whereas altitude-based APIs are appropriate for aligning to real-world features. (This is, after all, a map rather than an image viewer.)

Specifically, imagine you want to set a camera that comfortably clears the tallest building in a city. You could do it in one line by saying you want a camera with a certain altitude. Or you can guess and check until you come up with the right zoom level. But guessing and checking only works if you hard-code a specific city and a specific building.

True. My suggestion would be when passing a [lng, lat, altitude] to jumpTo/flyTo you would also need to pass a cameraHeight instead of a zoom. The cameraHeight would be how high do you want the camera when looking at [lng, lat, altitude]. A nice result for 45deg would be 2 x altitude, and it would pick out the corresponding zoom value. Or it would be distanceFrom as they are equivalent, just something in real world units, not zoom levels.

Changing the 0 on this line in transform.js should be what we need on the projection side.

Hmm, but won't that then mean that map.getCenter() is no longer the point in the middle of the screen? (update: when pitch is not 0)

andrewharvey commented 5 years ago

See also #6093

asheemmamoowala commented 2 years ago

The free camera, via FreeCamerOptions now allows defining the position of the camera with elevation, but the look at point is still based on 0 elevation.

SkylinR commented 2 years ago

PROBLEM DESCRIPTION Hi :) I'm trying to display some popular buildings on map. (I need to disable by ID some default buildings and replace them with my owne models). And it's working like a charm but I get ID of that default buildings on some zoom (from 16, on higher zoom like 15.9 I cannot get ID of that single default building to replace it). So I'm there with zoom 16 and I want to display Burj Khalifa but this is quite high building and it's not visible entirely. I'm trying to change elevation to look a little bit higher then base of building to be able to display it whole on screen and there is my PROBLEM. As far as I can see all those elevation/altitude solutions works on zoom and when I'm trying to use it at the end of the day it changes map zoom (what makes default building visible and I cannot disable it because on higher zoom I cannot disable that single default building). Aditionally I'm rotating camera around that point :D so it's important to not just setup one view but it must be visible from each side (if that's important)

MY QUESTION Is there any option to just make camera look on point like "over" map a little bit.

Bulisor commented 1 year ago

The free camera, via FreeCamerOptions now allows defining the position of the camera with elevation, but the look at point is still based on 0 elevation.

Hey, what about look at point? Is there a way I can set it at a specific altitude? Thank you