Change a graphic’s symbol based on the camera’s proximity to it.

Use case
When showing dense datasets, it is beneficial to reduce the detail of individual points when zooming out to avoid visual clutter and to avoid data points overlapping and obscuring each other.
How to use the sample
The sample starts looking at a plane. Zoom out from the plane to see it turn into a cone. Keeping zooming out and it will turn into a point.
How it works
- Create a
GraphicsOverlayobject and add it to aSceneViewobject. - Create a
DistanceCompositeSceneSymbolobject. - Create
DistanceSymbolRangeobjects specifying aSymboland the min and max distance within which the symbol should be visible. - Add the ranges to the range collection of the distance composite scene symbol.
- Create a
Graphicobject with the distance composite scene symbol at a location and add it to the graphics overlay.
Relevant API
- DistanceCompositeSceneSymbol
- Range
- RangeCollection
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 |
Tags
3D, data, graphic
Sample Code
// [WriteFile Name=DistanceCompositeSymbol, 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 "DistanceCompositeSymbol.h"
// ArcGIS Maps SDK headers#include "ArcGISTiledElevationSource.h"#include "Basemap.h"#include "DistanceCompositeSceneSymbol.h"#include "ElevationSourceListModel.h"#include "Graphic.h"#include "GraphicListModel.h"#include "GraphicsOverlay.h"#include "GraphicsOverlayListModel.h"#include "LayerSceneProperties.h"#include "MapTypes.h"#include "ModelSceneSymbol.h"#include "OrbitGeoElementCameraController.h"#include "Point.h"#include "Scene.h"#include "SceneQuickView.h"#include "SceneViewTypes.h"#include "SimpleMarkerSceneSymbol.h"#include "SimpleMarkerSymbol.h"#include "SpatialReference.h"#include "Surface.h"#include "SymbolTypes.h"
// Qt headers#include <QStandardPaths>#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
DistanceCompositeSymbol::DistanceCompositeSymbol(QQuickItem* parent) : QQuickItem(parent){}
DistanceCompositeSymbol::~DistanceCompositeSymbol() = default;
void DistanceCompositeSymbol::init(){ qmlRegisterType<SceneQuickView>("Esri.Samples", 1, 0, "SceneView"); qmlRegisterType<DistanceCompositeSymbol>("Esri.Samples", 1, 0, "DistanceCompositeSymbolSample");}
void DistanceCompositeSymbol::componentComplete(){ QQuickItem::componentComplete();
// get the data path const QString dataPath = defaultDataPath() + "/ArcGIS/Runtime/Data/3D/Bristol/Collada/Bristol.dae";
// find QML SceneView component m_sceneView = findChild<SceneQuickView*>("sceneView");
// create a new basemap instance Basemap* basemap = new Basemap(BasemapStyle::ArcGISImageryStandard, this); // create a new scene instance m_scene = new Scene(basemap, this); // set scene on the scene view m_sceneView->setArcGISScene(m_scene);
// 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 m_scene->baseSurface()->elevationSources()->append(elevationSource);
// create a camera const Point point(-2.708471, 56.096575, 5000, m_sceneView->spatialReference());
// create a new graphics overlay and add it to the sceneview GraphicsOverlay* graphicsOverlay = new GraphicsOverlay(this); graphicsOverlay->setSceneProperties(LayerSceneProperties(SurfacePlacement::Relative)); m_sceneView->graphicsOverlays()->append(graphicsOverlay);
//! [create model scene symbol] constexpr float scale = 5.0f; ModelSceneSymbol* mms = new ModelSceneSymbol(QUrl(dataPath), scale, this); mms->setHeading(180); //! [create model scene symbol]
connect(mms, &ModelSceneSymbol::loadStatusChanged, this, [mms, point, graphicsOverlay, this](){ if (mms->loadStatus() == LoadStatus::Loaded) { SimpleMarkerSymbol* sms = new SimpleMarkerSymbol(SimpleMarkerSymbolStyle::Circle, QColor("red"), 10.0f, this); //! [create simple marker scene symbol] const SimpleMarkerSceneSymbolStyle style = SimpleMarkerSceneSymbolStyle::Cone; const QColor color("red"); constexpr double height = 75.0; constexpr double width = 75.0; constexpr double depth = 75.0; const SceneSymbolAnchorPosition anchorPosition = SceneSymbolAnchorPosition::Bottom; SimpleMarkerSceneSymbol* smss = new SimpleMarkerSceneSymbol(style, color, height, width, depth, anchorPosition, this); //! [create simple marker scene symbol]
//! [create distance symbol ranges with each symbol type and a distance range(meters)] DistanceSymbolRange* dsrModel = new DistanceSymbolRange(mms, 0, 999, this); // ModelSceneSymbol DistanceSymbolRange* dsrCone = new DistanceSymbolRange(smss, 1000, 1999, this); // SimpleMarkerSceneSymbol DistanceSymbolRange* dsrCircle = new DistanceSymbolRange(sms, 2000, 0, this); // SimpleMarkerSymbol
DistanceCompositeSceneSymbol* compositeSceneSymbol = new DistanceCompositeSceneSymbol(this);
compositeSceneSymbol->ranges()->append(dsrModel); compositeSceneSymbol->ranges()->append(dsrCone); compositeSceneSymbol->ranges()->append(dsrCircle); //! [create distance symbol ranges with each symbol type and a distance range(meters)]
// create a graphic using the composite symbol Graphic* graphic = new Graphic(point, compositeSceneSymbol, this);
// add the graphic to the graphics overlay graphicsOverlay->graphics()->append(graphic);
// add an orbit camera controller to lock the camera to the graphic OrbitGeoElementCameraController* cameraController = new OrbitGeoElementCameraController(graphic, 200, this); cameraController->setCameraPitchOffset(80); cameraController->setCameraHeadingOffset(-30); m_sceneView->setCameraController(cameraController); } });
mms->load();}// [WriteFile Name=DistanceCompositeSymbol, 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 DISTANCE_COMPOSITE_SYMBOL_H#define DISTANCE_COMPOSITE_SYMBOL_H
// Qt headers#include <QQuickItem>
namespace Esri::ArcGISRuntime{ class Scene; class SceneQuickView;}
class DistanceCompositeSymbol : public QQuickItem{ Q_OBJECT
public: explicit DistanceCompositeSymbol(QQuickItem* parent = nullptr); ~DistanceCompositeSymbol() override;
void componentComplete() override; static void init();
private: Esri::ArcGISRuntime::Scene* m_scene = nullptr; Esri::ArcGISRuntime::SceneQuickView* m_sceneView = nullptr;};
#endif // DISTANCE_COMPOSITE_SYMBOL_H// [WriteFile Name=DistanceCompositeSymbol, 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 Esri.Samples
DistanceCompositeSymbolSample { width: 800 height: 600
// add a mapView component SceneView { anchors.fill: parent objectName: "sceneView"
Component.onCompleted: { // Set the focus on SceneView to initially enable keyboard navigation forceActiveFocus(); } }}// [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]
// sample headers#include "DistanceCompositeSymbol.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QCommandLineParser>#include <QDir>#include <QGuiApplication>#include <QQmlEngine>#include <QQuickView>#include <QSettings>#include <QSurfaceFormat>
// Platform specific headers#ifdef Q_OS_WIN#include <Windows.h>#endif
#define STRINGIZE(x) #x#define QUOTE(x) STRINGIZE(x)
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("DistanceCompositeSymbol"));
// 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 DistanceCompositeSymbol::init();
// Initialize application view QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView);
// Add the import Path view.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 view.engine()->addImportPath(arcGISRuntimeImportPath);
// Set the source view.setSource(QUrl("qrc:/Samples/Scenes/DistanceCompositeSymbol/DistanceCompositeSymbol.qml"));
view.show();
return app.exec();}