For standard operations like zooming and panning, the MapNavigator
API is
enough. See Scale, pan, rotate, and fit a map for an illustration.
2D and 3D maps offer you a lot of freedom, though, and sometimes you need full control over the position of your camera.
The Camera API offers such fine-grained camera control.
The Camera API operates at a lower level than the MapNavigator API. With the MapNavigator API, you can navigate
both 2D and 3D maps in any reference, using the same API calls. The |
Changing the camera position
This example demonstrates how you use the Camera
API to configure the point to look
at, the camera distance, pitch, roll, and yaw:
auto currentCamera = map.getCamera(); if (auto* perspectiveCamera = dynamic_cast<PerspectiveCamera*>(currentCamera.get())) { auto cameraLookAt = Camera::LookAt(targetLocation, 200.0, 215.0, -30.0, 0.0); auto newCamera = perspectiveCamera->asBuilder().lookAt(cameraLookAt).build(); map.setCamera(newCamera); }
You can apply camera changes by setting a new camera on the Map.
It is important that you take into account the threading rules when you use the camera API. |
Animating the camera
When you want to animate the camera, it is important to get a fluent motion. You can accomplish this by updating the camera right before each map repaint . There are 2 concepts in the API that help you accomplish this.
AnimationManager
When your animation has a fixed duration, you can let it run once or let it
loop multiple times by submitting it to the Map’s AnimationManager
.
An example would be a zoom animation that takes half a second of time. The animation receives an
onUpdate
at the beginning of each paint call .
This example shows how you can submit a camera animation to the Map’s AnimationManager
.
// Start the animation that will apply the camera updates. _map->getAnimationManager().startAnimation("MyAnimationKey", animation); // Stop the camera animation _map->getAnimationManager().stopAnimation("MyAnimationKey");
IRendererCallback
When you want to update the camera continuously, for example based on some other state, you can use an
IRendererCallback
. This is a callback that
is called by the map renderer right before each repaint. Inside that callback, the camera can be
updated . An example of such an animation would be a camera that
follows a moving feature on the map.
This example shows how you can add an IRendererCallback
to the Map.
// Start the render callback that will apply the camera updates _cameraUpdater = std::make_shared<OrbitCameraUpdater>(_centerPoint, map->getReference(), _autoRotate); map->addRendererCallback(_cameraUpdater); // Stop the camera updater map->removeRendererCallback(_cameraUpdater);
The camera sample demonstrates the usage of this API in more detail. This sample shows how to:
-
Implement a controller for orbiting the camera around a point
-
Implement a controller for navigating on the map using a typical first person control scheme
Constraining the camera
When you use the MapNavigator class, you can configure which camera constraints are used. When you manipulate the camera directly however, it’s the responsibility of the camera manipulation code to implement the desired camera constraints.
You can for example prevent the camera from moving under the terrain. You can accomplish this using custom code that uses utility methods in the API, for example a method to project a point onto the terrain . The camera sample demonstrates how this can be accomplished in the first person navigation controller:
// Keep the camera above the terrain lookFromCamera.eye = AboveTerrainConstraint::moveAboveTerrain(map, lookFromCamera.eye, 5.0);
Coordinate AboveTerrainConstraint::moveAboveTerrain(const Map& map, const Coordinate& point, double minHeightAboveTerrain) { auto projectedPoint = map.getRenderer().projectPointOnTerrain(point); auto center = Coordinate(0.0, 0.0, 0.0); auto distanceToPoint = MathUtil::distance(center, point); auto distanceToProjectedPoint = MathUtil::distance(center, projectedPoint); double distanceToMoveUp = distanceToProjectedPoint + minHeightAboveTerrain - distanceToPoint; if (distanceToMoveUp > 0) { return point + MathUtil::normalize(point) * (distanceToMoveUp + 1e-6); } else { return point; } }