Demonstrates how to animate a graphic’s position and rotation and follow it using a OrbitGeoElementCameraController.
Also shows how to combine a SceneView and MapView in an MVC application with property binding.

Use case
Visualize movement through a 3D landscape.
How to use the sample
Animation Controls (Top Left Corner):
- Select a mission — selects a location with a route for the plane to fly
- Play/Pause — toggles playing and stopping the animation
- Fixed/Follow — toggles the camera’s free cam mode and follow
- Mission progress — shows how far along the route the plane is. Slide to change keyframe in animation
Camera Controls (Top Right Corner):
- Camare zoom — distance between camera and plane
- Camera angle — viewing angle between camera and plane
- Flight speed — controls speed of animation
2D Map Controls (Bottom Left Corner):
- Plus and Minus — controls distance of 2D view from ground level
How it works
- Create a
GraphicsOverlayand add it to theSceneView. - Create a
ModelSceneSymbolobject. - Create a
Graphicobject and set its geometry to aPoint. - Set the
ModelSceneSymbolobject to the graphic. - Add heading, pitch, and roll attributes to the graphic. Get the attributes from the graphic with
Graphic::attributes. - Create a
SimpleRendererobject and set its expression properties. - Add graphic and a renderer to the graphics overlay.
- Create a
OrbitGeoElementCameraControllerwhich is set to target the graphic. - Assign the camera controller to the
SceneView. - Update the graphic’s location, heading, pitch, and roll.
Relevant API
- Camera
- GlobeCameraController
- Graphic
- GraphicsOverlay
- ModelSceneSymbol
- OrbitGeoElementCameraController
- Renderer
- Scene
- SceneProperties
- SceneView
- SurfacePlacement
Offline Data
Read more about how to set up the sample’s offline data here.
| Link | Local Location |
|---|---|
| Model Marker Symbol Data | <userhome>/ArcGIS/Runtime/Data/3D/Bristol/Collada/Bristol.dae |
| GrandCanyon.csv mission data | <userhome>/ArcGIS/Runtime/Data/3D/Missions/GrandCanyon.csv |
| Hawaii.csv mission data | <userhome>/ArcGIS/Runtime/Data/3D/Missions/Hawaii.csv |
| Pyrenees.csv mission data | <userhome>/ArcGIS/Runtime/Data/3D/Missions/Pyrenees.csv |
| Snowdon.csv mission data | <userhome>/ArcGIS/Runtime/Data/3D/Missions/Snowdon.csv |
Tags
animation, camera, heading, pitch, roll, rotation, visualize
Sample Code
// [WriteFile Name=Animate3DSymbols, Category=Scenes]// [Legal]// Copyright 2016 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifdef PCH_BUILD#include "pch.hpp"#endif // PCH_BUILD
// sample headers#include "Animate3DSymbols.h"#include "MissionData.h"
// ArcGIS Maps SDK headers#include "ArcGISTiledElevationSource.h"#include "AttributeListModel.h"#include "Camera.h"#include "DistanceCompositeSceneSymbol.h"#include "ElevationSourceListModel.h"#include "GlobeCameraController.h"#include "Graphic.h"#include "GraphicListModel.h"#include "GraphicsOverlay.h"#include "GraphicsOverlayListModel.h"#include "LayerSceneProperties.h"#include "Map.h"#include "MapQuickView.h"#include "MapTypes.h"#include "ModelSceneSymbol.h"#include "OrbitGeoElementCameraController.h"#include "PointCollection.h"#include "Polyline.h"#include "PolylineBuilder.h"#include "RendererSceneProperties.h"#include "Scene.h"#include "SceneQuickView.h"#include "SceneViewTypes.h"#include "SimpleLineSymbol.h"#include "SimpleMarkerSceneSymbol.h"#include "SimpleMarkerSymbol.h"#include "SimpleRenderer.h"#include "SpatialReference.h"#include "Surface.h"#include "SymbolTypes.h"#include "Viewpoint.h"
// Qt headers#include <QFileInfo>#include <QFuture>#include <QStandardPaths>#include <QStringListModel>#include <QtCore/qglobal.h>
using namespace Esri::ArcGISRuntime;
// helper method to get cross platform data pathnamespace{ QString defaultDataPath() { QString dataPath;
#ifdef Q_OS_IOS dataPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); #else dataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); #endif
return dataPath; }} // namespace
using namespace Esri::ArcGISRuntime;
const QString Animate3DSymbols::HEADING = QStringLiteral("heading");const QString Animate3DSymbols::ROLL = QStringLiteral("roll");const QString Animate3DSymbols::PITCH = QStringLiteral("pitch");const QString Animate3DSymbols::ANGLE = QStringLiteral("angle");
Animate3DSymbols::Animate3DSymbols(QQuickItem* parent /* = nullptr */): QQuickItem(parent), m_dataPath(defaultDataPath() + "/ArcGIS/Runtime/Data/3D"), m_missionsModel(new QStringListModel({QStringLiteral("Grand Canyon"), QStringLiteral("Hawaii"), QStringLiteral("Pyrenees"), QStringLiteral("Snowdon")}, this)), m_missionData(new MissionData()){}
Animate3DSymbols::~Animate3DSymbols() = default;
void Animate3DSymbols::init(){ qmlRegisterType<SceneQuickView>("Esri.Samples", 1, 0, "SceneView"); qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<Animate3DSymbols>("Esri.Samples", 1, 0, "Animate3DSymbolsSample"); qmlRegisterUncreatableType<QAbstractListModel>("Esri.Samples", 1, 0, "AbstractListModel", "AbstractListModel is uncreateable");}
void Animate3DSymbols::componentComplete(){ QQuickItem::componentComplete();
// find QML SceneView component m_sceneView = findChild<SceneQuickView*>("sceneView"); // create a new scene instance Scene* scene = new Scene(BasemapStyle::ArcGISImageryStandard, this);
// set scene on the scene view m_sceneView->setArcGISScene(scene);
// for use when not in following mode m_globeController = new GlobeCameraController(this);
// create a new elevation source ArcGISTiledElevationSource* elevationSource = new ArcGISTiledElevationSource( QUrl("https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"), this);
// add the elevation source to the scene to display elevation scene->baseSurface()->elevationSources()->append(elevationSource);
// create a new graphics overlay and add it to the sceneview GraphicsOverlay* sceneOverlay = new GraphicsOverlay(this); sceneOverlay->setSceneProperties(LayerSceneProperties(SurfacePlacement::Absolute)); m_sceneView->graphicsOverlays()->append(sceneOverlay);
SimpleRenderer* renderer3D = new SimpleRenderer(this); RendererSceneProperties renderProperties = renderer3D->sceneProperties(); renderProperties.setHeadingExpression(QString("[%1]").arg(HEADING)); renderProperties.setPitchExpression(QString("[%1]").arg(PITCH)); renderProperties.setRollExpression(QString("[%1]").arg(ROLL)); renderer3D->setSceneProperties(renderProperties); sceneOverlay->setRenderer(renderer3D);
// find QML MapView component m_mapView = findChild<MapQuickView*>("mapView"); m_mapView->setAttributionTextVisible(false);
// set up mini map Map* map = new Map(BasemapStyle::ArcGISImageryStandard, this); m_mapView->setMap(map);
// create a graphics overlay for the mini map GraphicsOverlay* mapOverlay = new GraphicsOverlay(this); m_mapView->graphicsOverlays()->append(mapOverlay);
// set up route graphic createRoute2d(mapOverlay);
// set up overlay 2D graphic createModel2d(mapOverlay);}
void Animate3DSymbols::setMissionFrame(int newFrame){ if (!m_missionData|| newFrame < 0 || m_frame == newFrame) return;
m_frame = newFrame; emit missionFrameChanged();}
void Animate3DSymbols::animate(){ if (!m_missionData) return;
if (missionFrame() < missionSize()) { // get the data for this stage in the mission const MissionData::DataPoint& dp = m_missionData->dataAt(missionFrame());
// move 3D graphic to the new position m_graphic3d->setGeometry(dp.m_pos); // update attribute expressions to immediately update rotation m_graphic3d->attributes()->replaceAttribute(HEADING, dp.m_heading); m_graphic3d->attributes()->replaceAttribute(PITCH, dp.m_pitch); m_graphic3d->attributes()->replaceAttribute(ROLL, dp.m_roll);
// move 2D graphic to the new position m_graphic2d->setGeometry(dp.m_pos); m_symbol2d->setAngle(dp.m_heading); }
// increment the frame count emit nextFrameRequested();}
void Animate3DSymbols::changeMission(const QString &missionNameStr){ setMissionFrame(0);
// read the mission data from the samples .csv files QString formattedname = missionNameStr; m_missionData->parse(m_dataPath + "/Missions/" + formattedname.remove(" ") + ".csv");
// if the mission was loaded successfully, move to the start position if (missionReady() && m_routeGraphic) { // create a polyline representing the route for the mission PolylineBuilder* routeBldr = new PolylineBuilder(SpatialReference::wgs84(), this); for (int i = 0; i < missionSize(); ++i) { const MissionData::DataPoint& dp = m_missionData->dataAt(i); routeBldr->addPoint(dp.m_pos); }
// set the polyline as a graphic on the mapView m_routeGraphic->setGeometry(routeBldr->toGeometry());
m_mapView->setViewpointAndWait(Viewpoint(m_routeGraphic->geometry())); createGraphic3D(); }
emit missionReadyChanged(); emit missionSizeChanged();}
QAbstractListModel *Animate3DSymbols::missionsModel(){ return m_missionsModel;}
void Animate3DSymbols::setZoom(double zoomDist){ if (m_followingController) { m_followingController->setCameraDistance(zoomDist); emit zoomChanged(); }}
void Animate3DSymbols::setAngle(double angle){ if (m_followingController) { m_followingController->setCameraPitchOffset(angle); emit angleChanged(); }}
void Animate3DSymbols::createModel2d(GraphicsOverlay* mapOverlay){ if (m_symbol2d || m_graphic2d) return;
// get the mission data for the frame const MissionData::DataPoint& dp = m_missionData->dataAt(missionFrame());
// create a blue triangle symbol to represent the plane on the mini map m_symbol2d = new SimpleMarkerSymbol(SimpleMarkerSymbolStyle::Triangle, Qt::blue, 10, this); m_symbol2d->setAngle(dp.m_heading);
// create a graphic with the symbol m_graphic2d = new Graphic(dp.m_pos, m_symbol2d, this);
mapOverlay->graphics()->append(m_graphic2d);}
void Animate3DSymbols::createRoute2d(GraphicsOverlay* mapOverlay){ // Create a 2d graphic of a solid red line to represen the route of the mission SimpleLineSymbol* routeSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle::Solid, Qt::red, 1, this); m_routeGraphic = new Graphic(this); m_routeGraphic->setSymbol(routeSymbol); mapOverlay->graphics()->append(m_routeGraphic);}
void Animate3DSymbols::createGraphic3D(){ if (!missionReady()) return;
// create the ModelSceneSymbol to be animated in the 3d view if (!m_model3d) m_model3d = new ModelSceneSymbol(QUrl(m_dataPath + "/Bristol/Collada/Bristol.dae"), 10.0f, this);
// get the mission data for the frame const MissionData::DataPoint& dp = m_missionData->dataAt(missionFrame());
if (!m_graphic3d) { // create a graphic using the model symbol m_graphic3d = new Graphic(dp.m_pos, m_model3d, this); m_graphic3d->attributes()->insertAttribute(HEADING, dp.m_heading); m_graphic3d->attributes()->insertAttribute(PITCH, dp.m_pitch); m_graphic3d->attributes()->insertAttribute(ROLL, dp.m_roll);
// add the graphic to the graphics overlay m_sceneView->graphicsOverlays()->at(0)->graphics()->append(m_graphic3d);
// create the camera controller to follow the graphic m_followingController = new OrbitGeoElementCameraController(m_graphic3d, 500, this); m_sceneView->setCameraController(m_followingController); } else { // update existing graphic's geometry and attributes m_graphic3d->setGeometry(dp.m_pos); m_graphic3d->attributes()->replaceAttribute(HEADING, dp.m_heading); m_graphic3d->attributes()->replaceAttribute(PITCH, dp.m_pitch); m_graphic3d->attributes()->replaceAttribute(ROLL, dp.m_roll); }}
void Animate3DSymbols::setFollowing(bool following){ if (following) m_sceneView->setCameraController(m_followingController); else m_sceneView->setCameraController(m_globeController);}
void Animate3DSymbols::zoomMapIn(){ if (!m_mapView || !m_routeGraphic) return;
// zoom the map view in, focusing on the position of the 2d graphic for the airplane m_mapView->setViewpointAsync(Viewpoint((Point)m_routeGraphic->geometry(), m_mapView->mapScale() / m_mapZoomFactor));}
void Animate3DSymbols::zoomMapOut(){ if (!m_mapView || !m_routeGraphic) return;
// zoom the map view out, focusing on the position of the 2d graphic for the airplane m_mapView->setViewpointAsync(Viewpoint((Point)m_routeGraphic->geometry(), m_mapView->mapScale() * m_mapZoomFactor));}
void Animate3DSymbols::viewWidthChanged(bool sceneViewIsWider){ if (!m_sceneView || !m_mapView) { return; }
// only show the attribution text on the view with the widest visible extent m_sceneView->setAttributionTextVisible(sceneViewIsWider); m_mapView->setAttributionTextVisible(!sceneViewIsWider);}
bool Animate3DSymbols::missionReady() const{ if (!m_missionData) return false;
return m_missionData->ready();}
int Animate3DSymbols::missionSize() const{ if (!m_missionData) return 0;
return (int)m_missionData->size();}
int Animate3DSymbols::missionFrame() const{ return m_frame;}
double Animate3DSymbols::zoom() const{ return m_followingController ? m_followingController->cameraDistance() : 200.0;}
double Animate3DSymbols::angle() const{ return m_followingController ? m_followingController->cameraDistance() : 45.0;}
double Animate3DSymbols::minZoom() const{ return m_followingController ? m_followingController->minCameraDistance() : 0;}// [WriteFile Name=Animate3DSymbols, Category=Scenes]// [Legal]// Copyright 2016 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifndef ANIMATE3DSYMBOLS_H#define ANIMATE3DSYMBOLS_H
// Qt headers#include <QQuickItem>#include <QString>
// STL headers#include <memory>
class MissionData;
namespace Esri::ArcGISRuntime{ class GlobeCameraController; class Graphic; class GraphicsOverlay; class MapQuickView; class ModelSceneSymbol; class OrbitGeoElementCameraController; class SceneQuickView; class SimpleMarkerSymbol;}
class QAbstractListModel;class MissionData;
Q_MOC_INCLUDE("QAbstractListModel")
class Animate3DSymbols : public QQuickItem{ Q_PROPERTY(bool missionReady READ missionReady NOTIFY missionReadyChanged) Q_PROPERTY(int missionSize READ missionSize NOTIFY missionSizeChanged) Q_PROPERTY(int missionFrame READ missionFrame WRITE setMissionFrame NOTIFY missionFrameChanged) Q_PROPERTY(QAbstractListModel* missionsModel READ missionsModel CONSTANT) Q_PROPERTY(double minZoom READ minZoom NOTIFY minZoomChanged) Q_PROPERTY(double zoom READ zoom WRITE setZoom NOTIFY zoomChanged) Q_PROPERTY(double angle READ angle WRITE setAngle NOTIFY angleChanged) Q_OBJECT
public: Animate3DSymbols(QQuickItem* parent = nullptr); ~Animate3DSymbols() override;
void componentComplete() override; static void init();
Q_INVOKABLE void animate(); Q_INVOKABLE void changeMission(const QString& missionName); QAbstractListModel* missionsModel(); Q_INVOKABLE void zoomMapIn(); Q_INVOKABLE void zoomMapOut(); Q_INVOKABLE void viewWidthChanged(bool sceneViewIsWider); Q_INVOKABLE void setFollowing(bool following);
bool missionReady() const; int missionSize() const; int missionFrame() const; double zoom() const; double angle() const; double minZoom() const;
void setMissionFrame(int newFrame); void setZoom(double zoomDist); void setAngle(double angle); void setSpeed(int intervalMs);
signals: void missionReadyChanged(); void missionSizeChanged(); void nextFrameRequested(); void minZoomChanged(); void zoomChanged(); void angleChanged(); void missionFrameChanged();
private: void createModel2d(Esri::ArcGISRuntime::GraphicsOverlay* mapOverlay); void createRoute2d(Esri::ArcGISRuntime::GraphicsOverlay* mapOverlay); void createGraphic3D();
static const QString HEADING; static const QString ROLL; static const QString PITCH; static const QString ANGLE;
Esri::ArcGISRuntime::SceneQuickView* m_sceneView = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr; Esri::ArcGISRuntime::ModelSceneSymbol* m_model3d = nullptr; Esri::ArcGISRuntime::Graphic* m_graphic3d = nullptr; Esri::ArcGISRuntime::Graphic* m_graphic2d = nullptr; Esri::ArcGISRuntime::SimpleMarkerSymbol* m_symbol2d = nullptr; Esri::ArcGISRuntime::Graphic* m_routeGraphic = nullptr; Esri::ArcGISRuntime::GlobeCameraController* m_globeController = nullptr; Esri::ArcGISRuntime::OrbitGeoElementCameraController* m_followingController = nullptr; QString m_dataPath; QAbstractListModel* m_missionsModel = nullptr; std::unique_ptr<MissionData> m_missionData; int m_frame = 0; double m_mapZoomFactor = 5.0;};
#endif // ANIMATE3DSYMBOLS_H// [WriteFile Name=Animate3DSymbols, Category=Scenes]// [Legal]// Copyright 2016 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
import QtQuickimport QtQuick.Controlsimport QtQuick.Layoutsimport Esri.Samples
Animate3DSymbolsSample { id: rootRectangle
property bool following: followButton.checked
missionFrame: progressSlider.value zoom: cameraDistance.value angle: cameraAngle.value
onNextFrameRequested: { progressSlider.value = progressSlider.value + 1; if (progressSlider.value >= missionSize) progressSlider.value = 0; }
Component.onCompleted: { missionList.currentIndex = 0; }
SceneView { id: sceneView objectName: "sceneView" anchors.fill: parent
Component.onCompleted: { // Set the focus on SceneView to initially enable keyboard navigation forceActiveFocus(); }
GridLayout { anchors { left: parent.left right: parent.right top: parent.top bottom: sceneView.attributionTop margins: 10 }
columns: 2
ComboBox { id: missionList enabled: !playButton.checked model: missionsModel textRole: "display" property real modelWidth: 0 Layout.minimumWidth: leftPadding + rightPadding + modelWidth + (indicator ? indicator.width : 10)
onModelChanged: { for (let i = 0; i < missionsModel.rowCount(); ++i) { const index = missionsModel.index(i, 0); textMetrics.text = missionsModel.data(index); modelWidth = Math.max(modelWidth, textMetrics.width); } }
onCurrentTextChanged: { changeMission(currentText); progressSlider.value = 0; }
TextMetrics { id: textMetrics font: missionList.font }
Component.onCompleted: missionList.currentTextChanged()
// Add a background to the ComboBox Rectangle { anchors.fill: parent radius: 10 // Make the rectangle visible if a dropdown indicator exists // An indicator only exists if a theme is set visible: parent.indicator border.width: 1 } }
LabeledSlider { id: cameraDistance Layout.alignment: Qt.AlignRight from: 10.0 to: 5000.0 value: 500.0 text: "zoom" }
RowLayout { Button { id: playButton checked: false checkable: true enabled: missionReady text: checked ? "pause" : "play" }
Button { id: followButton Layout.alignment: Qt.AlignRight enabled: missionReady text: checked? "fixed" : "follow " checked: true checkable: true onCheckedChanged: setFollowing(checked); } }
LabeledSlider { id: cameraAngle Layout.alignment: Qt.AlignRight from: 0 to: 180.0 value: 45.0 text: "angle" }
LabeledSlider { id: progressSlider from: 0 to: missionSize enabled : missionReady text: (value / missionSize * 100).toLocaleString(Qt.locale(), 'f', 0) + "%" handleWidth: progressMetrics.width TextMetrics { id: progressMetrics font: progressSlider.font text: "100%" } }
LabeledSlider { id: animationSpeed Layout.alignment: Qt.AlignRight from: 1 to: 100 value: 50 text: "speed" }
Rectangle { id: mapFrame Layout.columnSpan: 2 Layout.alignment: Qt.AlignLeft | Qt.AlignBottom Layout.minimumHeight: parent.height * 0.25 Layout.minimumWidth: parent.width * 0.3 color: "black" clip: true
MapView { id: mapView anchors { fill: parent margins: 2 } objectName: "mapView"
MouseArea { anchors.fill: parent onPressed: mouse => mouse.accepted onWheel: wheel.accepted } }
RowLayout { anchors { left: parent.left top: parent.top } spacing: 10
Rectangle { Layout.margins: 5 height: width width: childrenRect.width clip: true radius: 5
opacity: 0.9 Image { source: "qrc:/Samples/Scenes/Animate3DSymbols/plus-16-f.png" width: 24 height: width
MouseArea { anchors.fill: parent onClicked: zoomMapIn() } } }
Rectangle { Layout.margins: 5 height: width width: childrenRect.width opacity: 0.9 clip: true radius: 5
Image { source: "qrc:/Samples/Scenes/Animate3DSymbols/minus-16-f.png" width: 24 height: width MouseArea { anchors.fill: parent onClicked: zoomMapOut() } } } } } } }
Timer { id: timer interval: 16.0 + 84 * (animationSpeed.to - animationSpeed.value) / 100.0; running: playButton.checked; repeat: true onTriggered: animate(); }}import QtQuick.Controlsimport QtQuick
Slider { id: slider property alias text: handleText.text property alias handleWidth: handleText.width font.pixelSize: 14 handle: Item { x: parent.leftPadding + parent.visualPosition * (parent.availableWidth - handleNub.width) y: parent.topPadding + parent.availableHeight / 2 - handleNub.height / 2 Rectangle { id: handleNub color: handleRect.color radius: width * 0.5 width: 10 height: width } Rectangle { id: handleRect height: childrenRect.height width: childrenRect.width radius: 3 x: handleNub.x - width / 2 + handleNub.width / 2 y: handleNub.y - handleNub.height * 2 color: progressSlider.background.children[0].color Text { id: handleText padding: 3 color: "white" font: slider.font horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter } } }}// [WriteFile Name=Animate3DSymbols, Category=Scenes]// [Legal]// Copyright 2018 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifdef PCH_BUILD#include "pch.hpp"#endif // PCH_BUILD
// sample headers#include "MissionData.h"
// Qt headers#include <QFile>
// MissionData
MissionData::MissionData(): m_ready(false){}
MissionData::~MissionData() = default;
bool MissionData::parse(const QString& dataPath){ m_data.clear(); m_ready = false;
QFile file(dataPath); if(!file.exists()) return false;
if (!file.open(QIODevice::ReadOnly)) return false;
while (!file.atEnd()) { QByteArray line = file.readLine(); QList<QByteArray> parts = line.split(','); if(parts.size() < 6) continue;
bool ok = false; double lon = parts.at(0).toDouble(&ok); if(!ok) continue;
double lat = parts.at(1).toDouble(&ok); if(!ok) continue;
double elevation = parts.at(2).toDouble(&ok); if(!ok) continue;
double heading = parts.at(3).toDouble(&ok); if(!ok) continue;
double pitch = parts.at(4).toDouble(&ok); if(!ok) continue;
double roll = parts.at(5).simplified().toDouble(&ok); if(!ok) continue;
m_data.emplace_back((double)lon, (double)lat, (double)elevation, (double)heading, (double)pitch, (double)roll); }
m_ready = m_data.size() > 0; return m_ready;}
const MissionData::DataPoint& MissionData::dataAt(size_t i) const{ if(i < m_data.size()) return m_data[i];
static MissionData::DataPoint dataPoint; return dataPoint;}// [WriteFile Name=Animate3DSymbols, Category=Scenes]// [Legal]// Copyright 2018 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifndef MISSIONDATA_H#define MISSIONDATA_H
// ArcGIS Maps SDK headers#include "Point.h"#include "SpatialReference.h"
// STL headers#include <cmath>
class MissionData{public:
struct DataPoint { DataPoint(){} DataPoint(double lon, double lat, double elevation, double heading, double pitch, double roll): m_pos(Esri::ArcGISRuntime::Point(lon, lat, elevation, Esri::ArcGISRuntime::SpatialReference::wgs84())), m_heading(heading), m_pitch(pitch), m_roll(roll){}
Esri::ArcGISRuntime::Point m_pos; double m_heading = NAN; double m_pitch = NAN; double m_roll = NAN; };
typedef std::vector<DataPoint> DataPointList;
MissionData(); ~MissionData();
bool parse(const QString& dataPath); bool isEmpty() const {return m_data.empty();} size_t size() const {return m_data.size();} const DataPoint& dataAt(size_t i) const; bool ready() const {return m_ready;}
private: DataPointList m_data; bool m_ready;};
#endif // MISSIONDATA_H// [Legal]// Copyright 2015 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
// sample headers#include "Animate3DSymbols.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"#include "MapQuickView.h"#include "SceneQuickView.h"
// Qt headers#include <QCommandLineParser>#include <QDir>#include <QGuiApplication>#include <QQmlApplicationEngine>#include <QQmlEngine>#include <QQuickView>#include <QSurfaceFormat>
// Platform specific headers#ifdef Q_OS_WIN#include <Windows.h>#endif
#define STRINGIZE(x) #x#define QUOTE(x) STRINGIZE(x)
using namespace Esri::ArcGISRuntime;
int main(int argc, char *argv[]){ Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setUseLegacyAuthentication(false);#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) // Linux requires 3.2 OpenGL Context // in order to instance 3D symbols QSurfaceFormat fmt = QSurfaceFormat::defaultFormat(); fmt.setVersion(3, 2); QSurfaceFormat::setDefaultFormat(fmt);#endif
QGuiApplication app(argc, argv); app.setApplicationName(QString("Animate3DSymbols"));
// Use of ArcGIS location services, such as basemap styles, geocoding, and routing services, // requires an access token. For more information see // https://links.esri.com/arcgis-runtime-security-auth.
// The following methods grant an access token:
// 1. User authentication: Grants a temporary access token associated with a user's ArcGIS account. // To generate a token, a user logs in to the app with an ArcGIS account that is part of an // organization in ArcGIS Online or ArcGIS Enterprise.
// 2. API key authentication: Get a long-lived access token that gives your application access to // ArcGIS location services. Go to the tutorial at https://links.esri.com/create-an-api-key. // Copy the API Key access token.
const QString accessToken = QString("");
if (accessToken.isEmpty()) { qWarning() << "Use of ArcGIS location services, such as the basemap styles service, requires" << "you to authenticate with an ArcGIS account or set the API Key property."; } else { Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setApiKey(accessToken); }
// Initialize the sample Animate3DSymbols::init();
// Initialize application view QQmlApplicationEngine engine;
// Add the import Path engine.addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml"));
QString arcGISRuntimeImportPath = QUOTE(ARCGIS_RUNTIME_IMPORT_PATH);
#if defined(LINUX_PLATFORM_REPLACEMENT) // on some linux platforms the string 'linux' is replaced with 1 // fix the replacement paths which were created QString replaceString = QUOTE(LINUX_PLATFORM_REPLACEMENT); arcGISRuntimeImportPath = arcGISRuntimeImportPath.replace(replaceString, "linux", Qt::CaseSensitive); #endif
// Add the Runtime and Extras path engine.addImportPath(arcGISRuntimeImportPath);
// Set the source engine.load(QUrl("qrc:/Samples/Scenes/Animate3DSymbols/main.qml"));
return app.exec();}import QtQuickimport QtQuick.Controlsimport Esri.Samples
ApplicationWindow { visible: true width: 800 height: 600
Animate3DSymbols { anchors.fill: parent }}