This exercise walks you through the following:
- Add zoom in and zoom out buttons to the UI
- Zoom in and out on the map and the scene
- Add a button for locking the scene's focus point
Prerequisites:
- Complete Exercise 1, or get the Exercise 1 code solution compiling and running properly in Qt Creator.
If you need some help, you can refer to the solution to this exercise, available in this repository.
-
In your QML file, under
ApplicationWindow
, create a function calledzoom
that takes a parameter calledfactor
. We will fill in this function later, but for now, just print to the console for debugging:function zoom(factor) { console.log("zoom factor: " + factor); }
-
After the 2D/3D toggle button, create zoom out and zoom in buttons that call your
zoom
function when clicked:Button { id: button_zoomOut iconSource: "qrc:///Resources/zoom_out.png" anchors.right: mapView.right anchors.rightMargin: 20 anchors.bottom: button_toggle2d3d.top anchors.bottomMargin: 10 onClicked: { zoom(0.5) } } Button { id: button_zoomIn iconSource: "qrc:///Resources/zoom_in.png" anchors.right: mapView.right anchors.rightMargin: 20 anchors.bottom: button_zoomOut.top anchors.bottomMargin: 10 onClicked: { zoom(2) } }
-
Run your app. Verify that the zoom buttons appear and that output is written to the log in Qt Creator when you click the new buttons:
-
Create a function for zooming the map and a function for zooming the scene:
function zoomMap(factor) { mapView.setViewpointScale(mapView.mapScale / factor); } function zoomScene(factor) { var target = sceneView.currentViewpointCenter.center; var camera = sceneView.currentViewpointCamera.zoomToward(target, factor); sceneView.setViewpointCameraAndSeconds(camera, 0.5); }
-
In
zoom
, replace theconsole.log
call with a call to eitherzoomMap
orzoomScene
, depending on whether we are in 2D mode or 3D mode:var zoomFunction = threeD ? zoomScene : zoomMap; zoomFunction(factor);
-
Run your app. Verify that the zoom in and zoom out buttons work on both the map and the scene.
This portion of the exercise will teach you how to use camera controllers in ArcGIS Runtime.
-
In
main.qml
, after your zoom in and zoom out buttons, declare a button for toggling the lock focus. Make sure to setcheckable: true
so that the button will toggle. Include an emptyonClicked
handler that you will fill in later:Button { id: button_lockFocus iconSource: "qrc:///Resources/lock.png" anchors.right: mapView.right anchors.rightMargin: 20 anchors.bottom: button_zoomIn.top anchors.bottomMargin: 10 checkable: true onClicked: { } }
-
Before your new button, declare a default
GlobeCameraController
with no properties exceptid
:GlobeCameraController { id: globeCameraController_default }
-
In the
onClicked
for your new button, add anif-else
statement for whether or not the button is selected:if (button_lockFocus.checked) { } else { }
-
If the button is NOT selected, it's only one line of code to set the
SceneView
's camera controller to the defaultGlobeCameraController
you declared. Insert this line in your newelse
block:sceneView.cameraController = globeCameraController_default;
-
If the button IS selected, you need to give the
SceneView
a newOrbitLocationCameraController
, which locks the camera's focus on a given point.OrbitLocationCameraController
's constructor takes two arguments:- The target point on Earth's surface. You can use
sceneView.currentViewpointCenter.center
to access the current camera's target point. - The distance (in meters) from the target at which the camera should be placed. ArcGIS Runtime's
GeometryEngine
lets you calculate the x/y distance in meters between two points, but the constructor needs an x/y/z distance, which you can calculate using the Pythagorean theorem (did we mention that this workshop would require junior high school math?).
The following steps will help you set up this camera controller.
- The target point on Earth's surface. You can use
-
In your empty
if
block, get theSceneView
's current camera and its location, and verify that the location is defined:var currentCamera = sceneView.currentViewpointCamera; var currentCameraPoint = currentCamera.location; if (currentCameraPoint) { }
-
If the current camera point is not null (i.e. in the new empty
if
block), useGeometryEngine.distanceGeodetic(Point, Point, LinearUnit, AngularUnit, GeodeticCurveType)
to calculate the ground distance between the target point and the x/y part of the current camera location. Then use the Pythagorean theorem to calculate the distance from the target point and the current camera:var xyDistance = GeometryEngine.distanceGeodetic( sceneView.currentViewpointCenter.center, currentCameraPoint, Enums.LinearUnitIdMeters, Enums.AngularUnitIdDegrees, Enums.GeodeticCurveTypeGeodesic ).distance; var zDistance = currentCameraPoint.z; var distanceToTarget = Math.sqrt(Math.pow(xyDistance, 2.0) + Math.pow(zDistance, 2.0));
-
Create a new
OrbitLocationCameraController
with the target point and distance you calculated. Set its heading and pitch from the current camera. Then give theSceneView
the camera controller you created:var cameraController = ArcGISRuntimeEnvironment.createObject("OrbitLocationCameraController", { targetLocation: sceneView.currentViewpointCenter.center, cameraDistance: distanceToTarget }); cameraController.cameraHeadingOffset = currentCamera.heading; cameraController.cameraPitchOffset = currentCamera.pitch; sceneView.cameraController = cameraController;
-
Run your app. Switch to 3D mode, navigate to a point where you want to lock, and click the lock button. Verify that navigation now focuses on the target point. Click the lock button again and verify that normal navigation is restored:
If you have trouble, refer to the solution code, which is linked near the beginning of this exercise. You can also submit an issue in this repo to ask a question or report a problem. If you are participating live with Esri presenters, feel free to ask a question of the presenters.
If you completed the exercise, congratulations! You learned how to add buttons that programmatically zoom in and out on a 2D map and a 3D scene.
Ready for more? Choose from the following:
- Start on Exercise 3: Add Operational Layers.
- We used
OrbitLocationCameraController
, which causes navigation to orbit around a fixed location.OrbitGeoElementCameraController
causes navigation to orbit around aGeoElement
, whose location can move. See if you can figure out how to make the camera focus on a moving point.