Apply dictionary renderer to graphics overlay | ArcGIS Maps SDK for Swift | Esri Developer
View on GitHub

Create graphics from an XML file with key-value pairs for each graphic and display the military symbols using a MIL-STD-2525D web style in 3D.

Image of Apply dictionary renderer to graphics overlay sample

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

Open the sample and view the military symbols on the scene.

How it works

  1. Create a new DictionarySymbolStyle object with a protal item and set the style’s draw rule configuration to “ORDERED ANCHOR POINTS”.
  2. Create a new DictionaryRenderer object with the dictionary symbol style.
  3. Create an instance of GraphicsOverlay.
  4. Set the dictionary renderer to the graphics overlay’s renderer.
  5. Parse the local XML file, creating a map of key/value pairs for each block of attributes.
  6. Create an instance of Graphic for each attribute.
  7. Use the _wkid key to get the geometry’s spatial reference.
  8. Use the _control_points key to get the geometry’s shape.
  9. Add the graphics to the graphics overlay.

Relevant API

  • DictionaryRenderer
  • DictionarySymbolStyle
  • GraphicsOverlay

About the data

The sample uses the Joint Military Symbology MIL-STD-2525D dictionary style from ArcGIS Online. This ArcGIS Web Style is for use to build custom applications that incorporate the MIL-STD-2525D symbol dictionary. This style supports a configuration for modeling locations as ordered anchor points or full geometries.

A local XML file containing messages with MIL-STD-2525D fields for military symbology (MIL-STD-2525D Messages 100.13.0) is also used. This is downloaded from ArcGIS Online automatically.

Tags

defense, military, situational awareness, tactical, visualization

Sample code

ApplyDictionaryRendererToGraphicsOverlayView.swift
// Copyright 2024 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
//
// https://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.
import ArcGIS
import SwiftUI
struct ApplyDictionaryRendererToGraphicsOverlayView: View {
/// A scene with a topographic basemap.
@State private var scene = Scene(basemapStyle: .arcGISTopographic)
/// The graphics overlay for displaying the message graphics on the scene.
@State private var graphicsOverlay = GraphicsOverlay()
/// The camera for zooming the scene view to the message graphics.
@State private var camera: Camera?
/// The error shown in the error alert.
@State private var error: (any Error)?
var body: some View {
SceneView(scene: scene, camera: $camera, graphicsOverlays: [graphicsOverlay])
.task {
do {
// Sets up the graphics overlay when the sample opens.
graphicsOverlay.renderer = try await makeMIL2525DRenderer()
try graphicsOverlay.addGraphics(makeMessageGraphics())
// Sets the camera to look at the graphics in the graphics overlay.
guard let extent = graphicsOverlay.extent else { return }
camera = Camera(
lookingAt: extent.center,
distance: 15_000,
heading: 0,
pitch: 70,
roll: 0
)
} catch {
self.error = error
}
}
.errorAlert(presentingError: $error)
}
/// Creates a dictionary renderer for styling with MIL-STD-2525D symbols.
/// - Returns: A new `DictionaryRenderer` object.
private func makeMIL2525DRenderer() async throws -> DictionaryRenderer {
// Creates a dictionary symbol style from a dictionary style portal item.
let portalItem = PortalItem(
portal: .arcGISOnline(connection: .anonymous),
id: .jointMilitarySymbologyDictionaryStyle
)
let dictionarySymbolStyle = DictionarySymbolStyle(portalItem: portalItem)
try await dictionarySymbolStyle.load()
// Uses the "Ordered Anchor Points" for the symbol style draw rule.
let drawRuleConfiguration = dictionarySymbolStyle.configurations.first { $0.name == "model" }
drawRuleConfiguration?.value = "ORDERED ANCHOR POINTS"
return DictionaryRenderer(dictionarySymbolStyle: dictionarySymbolStyle)
}
/// Creates graphics from messages in an XML file.
/// - Returns: An array of new `Graphic` objects.
private func makeMessageGraphics() throws -> [Graphic] {
// Gets the data from the local XML file.
let messagesData = try Data(contentsOf: .mil2525dMessagesXMLFile)
let parser = MessageParser(data: messagesData)
if parser.parse() {
// Creates graphics from the parsed messages.
return parser.messages.compactMap { message in
guard let messageWKID = message.wkid,
let wkid = WKID(messageWKID) else { return nil }
let spatialReference = SpatialReference(wkid: wkid)
let points = message.controlPoints.map { x, y in
Point(x: x, y: y, spatialReference: spatialReference)
}
return Graphic(geometry: Multipoint(points: points), attributes: message.other)
}
} else if let error = parser.parserError {
throw error
} else {
return []
}
}
}
// MARK: Message Parser
private extension ApplyDictionaryRendererToGraphicsOverlayView {
/// A parser for the XML file containing the MIL-STD-2525D messages.
final class MessageParser: XMLParser, XMLParserDelegate {
/// The parsed messages.
private(set) var messages: [Message] = []
/// The values of the message element currently being parsed.
private var currentMessage: Message?
/// The characters of the XML element currently being parsed.
private var currentElementContents = ""
override init(data: Data) {
super.init(data: data)
self.delegate = self
}
/// Creates a new `currentMessage` when a message start tag is encountered.
func parser(
_ parser: XMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String: String] = [:]
) {
if elementName == "message" {
currentMessage = Message()
}
currentElementContents.removeAll()
}
/// Adds the characters of the current element to `currentElementContents`.
func parser(_ parser: XMLParser, foundCharacters string: String) {
currentElementContents.append(contentsOf: string)
}
/// Adds the contents of the current element to the `currentMessage` when an end tag is encountered.
func parser(
_ parser: XMLParser,
didEndElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?
) {
switch elementName {
case "_control_points":
currentMessage?.controlPoints = currentElementContents.split(separator: ";")
.map { pair in
let coordinates = pair.split(separator: ",")
return (x: Double(coordinates.first!)!, y: Double(coordinates.last!)!)
}
case "message":
messages.append(currentMessage!)
currentMessage = nil
case "messages":
break
case "_wkid":
currentMessage?.wkid = Int(currentElementContents)
default:
currentMessage?.other[elementName] = currentElementContents
}
currentElementContents.removeAll()
}
}
/// The parsed values from an XML message element.
struct Message {
/// The x and y values of the control points element.
var controlPoints: [(x: Double, y: Double)] = []
/// The value of the wkid element.
var wkid: Int?
/// The other elements and their values.
var other: [String: any Sendable] = [:]
}
}
// MARK: Helper Extensions
private extension PortalItem.ID {
/// The ID for the "Joint Military Symbology MIL-STD-2525D" dictionary style portal item on ArcGIS Online.
static var jointMilitarySymbologyDictionaryStyle: Self {
.init("d815f3bdf6e6452bb8fd153b654c94ca")!
}
}
private extension URL {
/// The URL to the local XML file containing messages with MIL-STD-2525D fields.
static var mil2525dMessagesXMLFile: URL {
Bundle.main.url(forResource: "Mil2525DMessages", withExtension: "xml")!
}
}