Update the orientation of a graphic using expressions based on its attributes.
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
- Create a new graphics overlay.
- Create a simple renderer and set its scene properties.
- Set the heading expression to
[HEADING]
. - Apply the renderer to the graphics overlay.
- Create a graphic and add it to the overlay.
- To update the graphic's rotation, update the
HEADING
orPITCH
property in the graphic's attributes.
Relevant API
- Graphic.attributes
- GraphicsOverlay
- SceneProperties
- SceneProperties.headingExpression
- SceneProperties.pitchExpression
- SimpleRenderer
- SimpleRenderer.rendererSceneProperties
Tags
3D, expression, graphics, heading, pitch, rotation, scene, symbology
Sample Code
// [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]
import QtQuick 2.6
import Esri.ArcGISRuntime 100.15
import QtQuick.Controls 2.2
Rectangle {
id: rootRectangle
clip: true
width: 800
height: 600
readonly property real longitude: -118.71
readonly property real latitude: 32.09
readonly property real altitude: 100000.0
readonly property int coneDimension: 10000
readonly property real initialPitch: 90.0
readonly property real initialHeading: 180.0
readonly property string headingStr: "heading"
readonly property string pitchStr: "pitch"
SceneView {
id: sceneView
anchors.fill: parent
Scene {
id: scene
Basemap {
initStyle: Enums.BasemapStyleArcGISImageryStandard
}
Surface {
ArcGISTiledElevationSource {
url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"
}
}
}
// add a graphics overlay
GraphicsOverlay {
id: graphicsOverlay
LayerSceneProperties {
surfacePlacement: Enums.SurfacePlacementAbsolute
}
SimpleRenderer {
id: sceneRenderer
RendererSceneProperties {
id: renderProps
headingExpression: "["+headingStr+"]"
pitchExpression: "["+pitchStr+"]"
}
}
Graphic {
id: coneGraphic
geometry: Point {
x: longitude
y: latitude
z: altitude
spatialReference: sceneView.spatialReference
}
SimpleMarkerSceneSymbol{
style: Enums.SimpleMarkerSceneSymbolStyleCone
color: "red"
width: coneDimension
depth: coneDimension
height: coneDimension * 2
anchorPosition: Enums.SceneSymbolAnchorPositionCenter
}
Component.onCompleted: {
coneGraphic.attributes.insertAttribute(headingStr, initialHeading);
coneGraphic.attributes.insertAttribute(pitchStr, initialPitch);
}
}
}
Component.onCompleted: {
// Set the focus on SceneView to initially enable keyboard navigation
forceActiveFocus();
// set viewpoint to the specified camera
setViewpointCameraAndWait(camera);
}
}
Rectangle {
anchors.fill: sliderColumn
color: "lightblue"
}
Column{
id: sliderColumn
spacing: 4
anchors {
left: parent.left
top: parent.top
}
height: childrenRect.height
Text {
anchors {
margins: 5
}
text: pitchStr + ": " + pitchSlider.value.toFixed(0)
font.pixelSize: 20
verticalAlignment: Text.AlignTop
}
Slider{
id: pitchSlider
opacity: 0.7
height: 64
// slider controls degrees of rotation:
from: -90
to: 90
value: initialPitch
anchors {
margins: 5
}
onValueChanged: {
coneGraphic.attributes.replaceAttribute(pitchStr, value);
}
}
Text {
anchors {
margins: 5
}
text: headingStr + ": " + headingSlider.value.toFixed(0)
verticalAlignment: Text.AlignTop
font.pixelSize: 20
}
Slider{
id: headingSlider
opacity: 0.7
height: 64
// slider controls degrees of rotation:
from: 0
to: 360
value: initialHeading
anchors {
margins: 5
}
onValueChanged: {
coneGraphic.attributes.replaceAttribute(headingStr, value);
}
}
}
Camera {
id: camera
heading: 0.0
pitch: 45.0
roll: 0.0
Point {
x: longitude
y: latitude - 1.0 // place the camera somewhat south of the cone
z: altitude * 2.0 // place the camera somewhat higher than the cone
spatialReference: SpatialReference { wkid: 4326 }
}
}
}