Scene properties expressions

View on GitHubSample viewer app

Update the orientation of a graphic using expressions based on its attributes.

screenshot

Use case

Instead of reading the attribute and changing the rotation on the symbol for a single graphic (a manual CPU operation), you can bind the rotation to an expression that applies to the whole overlay (an automatic GPU operation). This usually results in a noticeable performance boost (smooth rotations).

How to use the sample

Adjust the heading and pitch sliders to rotate the cone.

How it works

  1. Create a new graphics overlay.
  2. Create a simple renderer and set its scene properties.
  3. Set the heading expression to [HEADING].
  4. Apply the renderer to the graphics overlay.
  5. Create a graphic and add it to the overlay.
  6. To update the graphic's rotation, update the HEADING or PITCH property in the graphic's attributes.

Relevant API

  • Graphic::attributes
  • GraphicsOverlay
  • SceneProperties
  • SceneProperties::headingExpression
  • SceneProperties::pitchExpression
  • SimpleRenderer
  • SimpleRenderer::sceneProperties

Tags

3D, expression, graphics, heading, pitch, rotation, scene, symbology

Sample Code

ScenePropertiesExpressions.cppScenePropertiesExpressions.cppScenePropertiesExpressions.hScenePropertiesExpressions.qml
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// [WriteFile Name=ScenePropertiesExpressions, Category=Scenes]
// [Legal]
// Copyright 2019 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 "ScenePropertiesExpressions.h"

// ArcGIS Maps SDK headers
#include "ArcGISTiledElevationSource.h"
#include "AttributeListModel.h"
#include "Basemap.h"
#include "Camera.h"
#include "ElevationSourceListModel.h"
#include "Graphic.h"
#include "GraphicListModel.h"
#include "GraphicsOverlay.h"
#include "GraphicsOverlayListModel.h"
#include "LayerSceneProperties.h"
#include "MapTypes.h"
#include "Point.h"
#include "RendererSceneProperties.h"
#include "Scene.h"
#include "SceneQuickView.h"
#include "SceneViewTypes.h"
#include "SimpleMarkerSceneSymbol.h"
#include "SimpleRenderer.h"
#include "SpatialReference.h"
#include "Surface.h"
#include "SymbolTypes.h"

using namespace Esri::ArcGISRuntime;

namespace
{
  const QString HEADING("HEADING");
  const QString PITCH("PITCH");
}

ScenePropertiesExpressions::ScenePropertiesExpressions(QObject* parent /* = nullptr */):
  QObject(parent),
  m_scene(new Scene(BasemapStyle::ArcGISImageryStandard, this))
{
  // create a new elevation source from Terrain3D REST service
  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
  m_scene->baseSurface()->elevationSources()->append(elevationSource);

  m_graphicsOverlay = new GraphicsOverlay(this);
  m_graphicsOverlay->setSceneProperties(LayerSceneProperties(SurfacePlacement::Relative));

  // Create a SimpleRenderer and set expressions on its scene properties.
  // Then, set the renderer to the graphics overlay with GraphicsOverlay.setRenderer(renderer).
  SimpleRenderer* renderer3D = new SimpleRenderer(this);
  RendererSceneProperties renderProperties = renderer3D->sceneProperties();
  renderProperties.setHeadingExpression(QString("[%1]").arg(HEADING));
  renderProperties.setPitchExpression(QString("[%1]").arg(PITCH));
  renderer3D->setSceneProperties(renderProperties);
  m_graphicsOverlay->setRenderer(renderer3D);
}

ScenePropertiesExpressions::~ScenePropertiesExpressions() = default;

void ScenePropertiesExpressions::init()
{
  // Register classes for QML
  qmlRegisterType<SceneQuickView>("Esri.Samples", 1, 0, "SceneView");
  qmlRegisterType<ScenePropertiesExpressions>("Esri.Samples", 1, 0, "ScenePropertiesExpressionsSample");
}

SceneQuickView* ScenePropertiesExpressions::sceneView() const
{
  return m_sceneView;
}

// Set the view (created in QML)
void ScenePropertiesExpressions::setSceneView(SceneQuickView* sceneView)
{
  if (!sceneView || sceneView == m_sceneView)
  {
    return;
  }

  m_sceneView = sceneView;
  m_sceneView->setArcGISScene(m_scene);

  // create a camera
  const double latitude = 32.09;
  const double longitude = -118.71;
  const double altitude = 100000.0;
  const double heading = 0.0;
  const double pitch = 45.0;
  const double roll = 0.0;
  const int coneDimension = 10000;

  Camera camera(latitude - 1.0, // place the camera arbitrarily south of the cone
                longitude,
                altitude * 2, // place the camera arbitrarily higher than the cone
                heading,
                pitch,
                roll);

  // set the viewpoint
  m_sceneView->setViewpointCameraAndWait(camera);

  // add the graphics overlay to the scene view
  m_sceneView->graphicsOverlays()->append(m_graphicsOverlay);

  // create a scene symbol based on the current type
  SimpleMarkerSceneSymbol* smss = new SimpleMarkerSceneSymbol(SimpleMarkerSceneSymbolStyle::Cone, QColor("red"), 200, 200, 200, SceneSymbolAnchorPosition::Center, this);
  smss->setWidth(coneDimension);
  smss->setDepth(coneDimension);
  smss->setHeight(coneDimension * 2);

  // create a graphic using the symbol above and a point location
  m_graphic = new Graphic(Point(longitude, latitude, altitude, m_sceneView->spatialReference()), smss, this);
  m_graphic->attributes()->insertAttribute(HEADING, 0);
  m_graphic->attributes()->insertAttribute(PITCH, 0);

  // add the graphic to the graphics overlay
  m_graphicsOverlay->graphics()->append(m_graphic);

  emit sceneViewChanged();
}

void ScenePropertiesExpressions::setPitch(double pitchInDegrees)
{
  m_graphic->attributes()->replaceAttribute(PITCH, pitchInDegrees);

  emit pitchChanged();
}

double ScenePropertiesExpressions::pitch() const
{
  return m_graphic->attributes()->attributeValue(PITCH).toDouble();
}

void ScenePropertiesExpressions::setHeading(double headingInDegrees)
{
  m_graphic->attributes()->replaceAttribute(HEADING, headingInDegrees);

  emit headingChanged();
}

double ScenePropertiesExpressions::heading() const
{
  return m_graphic->attributes()->attributeValue(HEADING).toDouble();
}

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.