This sample demonstrates applying a dictionary renderer to a graphics overlay in a 3D scene to display military symbology.

Use case
Use a dictionary renderer on a graphics overlay to display more transient data, such as military messages coming through a local tactical network.
How to use the sample
Pan and zoom to explore military symbols on the map.
How it works
- Create a new
DictionarySymbolStyle(dictionaryPath). - Create a new
DictionaryRenderer(symbolDictionary). - Create a new
GraphicsOverlay - Set the dictionary renderer to the graphics overlay.
- Parse through the XML and create a graphic for each element.
- Use the
_wkidkey to get the geometry’s spatial reference. - Use the
_control_pointskey to get the geometry’s shape. - Create a geometry using the shape and spatial reference from above.
- Create a
Graphicfor each attribute, utilizing it’s defined geometry. - Add the graphic to the graphics overlay.
Relevant API
- DictionaryRenderer
- DictionarySymbolStyle
- GraphicsOverlay
Offline Data
To set up the sample’s offline data, see the Use offline data in the samples section of the Qt Samples repository overview.
| Link | Local Location |
|---|---|
| Mil2525d Stylx File | <userhome>/ArcGIS/Runtime/Data/styles/arcade_style/mil2525d.stylx |
| MIL-STD-2525D XML Message File | <userhome>/ArcGIS/Runtime/Data/xml/arcade_style/Mil2525DMessages.xml |
About the data
The sample opens to a view of the county Wiltshire, United Kingdom. It displays military symbols illustrating a simulated combat situation in the area.
Tags
defense, military, situational awareness, tactical, visualization
Sample Code
// [WriteFile Name=GODictionaryRenderer_3D, Category=DisplayInformation]// [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 "GODictionaryRenderer_3D.h"
// ArcGIS Maps SDK headers#include "ArcGISTiledElevationSource.h"#include "Camera.h"#include "DictionaryRenderer.h"#include "DictionarySymbolStyle.h"#include "ElevationSourceListModel.h"#include "GeometryEngine.h"#include "Graphic.h"#include "GraphicListModel.h"#include "GraphicsOverlay.h"#include "GraphicsOverlayListModel.h"#include "MapTypes.h"#include "Point.h"#include "Scene.h"#include "SceneQuickView.h"#include "Surface.h"
// Qt headers#include <QFileInfo>#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
const QString GODictionaryRenderer_3D::FIELD_CONTROL_POINTS = "_control_points";const QString GODictionaryRenderer_3D::FIELD_WKID = "_wkid";
GODictionaryRenderer_3D::GODictionaryRenderer_3D(QQuickItem* parent) : QQuickItem(parent), m_dataPath(defaultDataPath() + "/ArcGIS/Runtime/Data"), m_graphicsOverlay(new GraphicsOverlay(this)){ connect(m_graphicsOverlay, &GraphicsOverlay::errorOccurred, this, &GODictionaryRenderer_3D::logError); m_graphicsOverlay->setRenderingMode(GraphicsRenderingMode::Dynamic);}
GODictionaryRenderer_3D::~GODictionaryRenderer_3D() = default;
void GODictionaryRenderer_3D::init(){ qmlRegisterType<SceneQuickView>("Esri.Samples", 1, 0, "SceneView"); qmlRegisterType<GODictionaryRenderer_3D>("Esri.Samples", 1, 0, "GODictionaryRenderer_3DSample");}
void GODictionaryRenderer_3D::logError(const Error& error){ setErrorMessage(QString("%1: %2").arg(error.message(), error.additionalMessage()));}
void GODictionaryRenderer_3D::componentComplete(){ QQuickItem::componentComplete();
// Set up DictionaryRenderer if (!QFileInfo::exists(m_dataPath + "/styles/arcade_style/mil2525d.stylx")) setErrorMessage("mil2525d.stylx not found");
DictionarySymbolStyle* dictionarySymbolStyle = DictionarySymbolStyle::createFromFile(m_dataPath + "/styles/arcade_style/mil2525d.stylx", this); connect(dictionarySymbolStyle, &DictionarySymbolStyle::errorOccurred, this, &GODictionaryRenderer_3D::logError);
DictionaryRenderer* renderer = new DictionaryRenderer(dictionarySymbolStyle, this); connect(renderer, &DictionaryRenderer::errorOccurred, this, &GODictionaryRenderer_3D::logError); m_graphicsOverlay->setRenderer(renderer);
// Create a scene and give it to the SceneView m_sceneView = findChild<SceneQuickView*>("sceneView"); connect(m_sceneView, &SceneQuickView::errorOccurred, this, &GODictionaryRenderer_3D::logError);
Scene* scene = new Scene(BasemapStyle::ArcGISImageryStandard, this); connect(scene, &Scene::errorOccurred, this, &GODictionaryRenderer_3D::logError);
Surface* surface = new Surface(this); connect(surface, &Surface::errorOccurred, this, &GODictionaryRenderer_3D::logError); surface->elevationSources()->append( new ArcGISTiledElevationSource( QUrl("https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"), this)); scene->setBaseSurface(surface); m_sceneView->setArcGISScene(scene); m_sceneView->graphicsOverlays()->append(m_graphicsOverlay);
parseXmlFile();
emit graphicsLoaded();
zoomToGraphics();}
void GODictionaryRenderer_3D::parseXmlFile(){ bool readingMessage = false; QVariantMap elementValues; QString currentElementName;
if (!QFileInfo::exists(m_dataPath + "/xml/arcade_style/Mil2525DMessages.xml")) setErrorMessage("xml/Mil2525DMessages.xml file is missing");
QFile xmlFile(m_dataPath + "/xml/arcade_style/Mil2525DMessages.xml"); // Open the file for reading if (xmlFile.isOpen()) { xmlFile.reset(); } else { xmlFile.open(QIODevice::ReadOnly | QIODevice::Text); } m_xmlParser.setDevice(&xmlFile);
// Traverse the XML in a loop while (!m_xmlParser.atEnd()) { m_xmlParser.readNext();
// Is this the start or end of a message element? if (m_xmlParser.name() == QString("message")) { if (!readingMessage) { // This is the start of a message element. elementValues.clear(); } else { /** * This is the end of a message element. Here we have a complete message that defines * a military feature to display on the map. Create a graphic from its attributes. */ createGraphic(elementValues); } // Either we just started reading a message, or we just finished reading a message. readingMessage = !readingMessage; } // Are we already inside a message element? else if (readingMessage) { // Is this the start of an element inside a message? if (m_xmlParser.isStartElement()) { // Remember which element we're reading currentElementName = m_xmlParser.name().toString(); } // Is this text? else if (m_xmlParser.isCharacters()) { // Is this text inside an element? if (!currentElementName.isEmpty()) { // Get the text and store it as the current element's value const QStringView trimmedText = m_xmlParser.text().trimmed(); if (!trimmedText.isEmpty()) { elementValues[currentElementName] = trimmedText.toString(); } } } } }}
void GODictionaryRenderer_3D::createGraphic(QVariantMap rawAttributes){ // If _wkid was absent, use WGS 1984 (4326) by default. int wkid = rawAttributes.count(FIELD_WKID) > 0 ? rawAttributes[FIELD_WKID].toInt() : 4326; SpatialReference sr(wkid); Geometry geom; QStringList pointStrings = rawAttributes[FIELD_CONTROL_POINTS].toString().split(";"); if (pointStrings.length() == 1) { // It's a point QStringList coords = pointStrings[0].split(","); geom = Point(coords[0].toDouble(), coords[1].toDouble(), sr); }
if (!geom.isEmpty()) { /** * Get rid of _control_points and _wkid. They are not needed in the graphic's * attributes. */ rawAttributes.remove(FIELD_CONTROL_POINTS); rawAttributes.remove(FIELD_WKID);
Graphic* graphic = new Graphic(geom, rawAttributes, this); m_graphicsOverlay->graphics()->append(graphic);
m_bbox = m_bbox.isEmpty() ? geom.extent() : GeometryEngine::unionOf(m_bbox, geom).extent(); }}
void GODictionaryRenderer_3D::zoomToGraphics(){ m_bbox = GeometryEngine::project(m_bbox, m_sceneView->arcGISScene()->spatialReference());
// Create a camera that looks at the bbox center, height 15000, pitch 70 Camera camera(m_bbox.extent().center(), 15000, 0, 70, 0);
m_sceneView->setViewpointCameraAndWait(camera);}
QString GODictionaryRenderer_3D::errorMessage() const{ return m_errorMsg;}
void GODictionaryRenderer_3D::setErrorMessage(const QString& msg){ m_errorMsg = msg; qDebug() << m_errorMsg; emit errorMessageChanged();}// [WriteFile Name=GODictionaryRenderer_3D, Category=DisplayInformation]// [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 GraphicsOverlayDictionaryRenderer_3D_H#define GraphicsOverlayDictionaryRenderer_3D_H
// ArcGIS Maps SDK headers#include "Envelope.h"#include "Error.h"#include "SpatialReference.h"
// Qt headers#include <QQuickItem>#include <QXmlStreamReader>
namespace Esri::ArcGISRuntime{ class GraphicsOverlay; class SceneQuickView;}
class GODictionaryRenderer_3D : public QQuickItem{ Q_OBJECT
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged)
public: explicit GODictionaryRenderer_3D(QQuickItem* parent = nullptr); ~GODictionaryRenderer_3D() override;
void componentComplete() override; static void init();
signals: void graphicsLoaded(); void errorMessageChanged();
private slots: void logError(const Esri::ArcGISRuntime::Error& error);
private: static const QString FIELD_CONTROL_POINTS; static const QString FIELD_WKID;
void parseXmlFile(); void createGraphic(QVariantMap rawAttributes); void zoomToGraphics(); QString errorMessage() const; void setErrorMessage(const QString& msg);
QString m_dataPath; QXmlStreamReader m_xmlParser; Esri::ArcGISRuntime::SceneQuickView* m_sceneView = nullptr; Esri::ArcGISRuntime::GraphicsOverlay* m_graphicsOverlay = nullptr; Esri::ArcGISRuntime::Envelope m_bbox; QString m_errorMsg;};
#endif // GraphicsOverlayDictionaryRenderer_3D_H// [WriteFile Name=GraphicsOverlayDictionaryRenderer_3D, Category=DisplayInformation]// [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
GODictionaryRenderer_3DSample { id: graphicsOverlayDictionaryRenderer_3DSample width: 800 height: 600
onGraphicsLoaded: progressBar_loading.visible = false;
// Create SceneView here, and create its Scene and GraphicsOverlay in C++ code SceneView { objectName: "sceneView" anchors.fill: parent
Component.onCompleted: { // Set the focus on SceneView to initially enable keyboard navigation forceActiveFocus(); } }
ProgressBar { id: progressBar_loading anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom margins: 5 } indeterminate: true }
Dialog { modal: true x: Math.round(parent.width - width) / 2 y: Math.round(parent.height - height) / 2 standardButtons: Dialog.Ok visible: text.length > 0 property alias text : textLabel.text property alias informativeText : detailsLabel.text ColumnLayout { Text { id: textLabel text: errorMessage } Text { id: detailsLabel text: "Please consult the README.md" } } onAccepted: errorAccepted() }}// [Legal]// Copyright 2015-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 "GODictionaryRenderer_3D.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QDir>#include <QGuiApplication>#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)
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("GODictionary Renderer (3D)"));
// 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 GODictionaryRenderer_3D::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/DisplayInformation/GODictionaryRenderer_3D/GODictionaryRenderer_3D.qml"));
view.show();
return app.exec();}