Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New] Apply dictionary renderer to graphics overlay #534

Merged
merged 7 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Samples.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@
D7058B122B59E468000A888A /* StylePointWithSceneSymbolView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7058B0D2B59E44B000A888A /* StylePointWithSceneSymbolView.swift */; };
D7058FB12ACB423C00A40F14 /* Animate3DGraphicView.Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7058FB02ACB423C00A40F14 /* Animate3DGraphicView.Model.swift */; };
D7058FB22ACB424E00A40F14 /* Animate3DGraphicView.Model.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7058FB02ACB423C00A40F14 /* Animate3DGraphicView.Model.swift */; };
D70789922CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D707898E2CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift */; };
D70789952CD1611E000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D707898E2CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift */; };
D707899B2CD16324000DF215 /* Mil2525DMessages.xml in Resources */ = {isa = PBXBuildFile; fileRef = D70789992CD16324000DF215 /* Mil2525DMessages.xml */; settings = {ASSET_TAGS = (ApplyDictionaryRendererToGraphicsOverlay, ); }; };
D7084FA92AD771AA00EC7F4F /* AugmentRealityToFlyOverSceneView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7084FA62AD771AA00EC7F4F /* AugmentRealityToFlyOverSceneView.swift */; };
D7084FAB2AD771F600EC7F4F /* AugmentRealityToFlyOverSceneView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7084FA62AD771AA00EC7F4F /* AugmentRealityToFlyOverSceneView.swift */; };
D70BE5792A5624A80022CA02 /* CategoriesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70BE5782A5624A80022CA02 /* CategoriesView.swift */; };
Expand Down Expand Up @@ -576,6 +579,7 @@
dstPath = "";
dstSubfolderSpec = 7;
files = (
D70789952CD1611E000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift in Copy Source Code Files */,
D7F2A0302CD00F400008D981 /* ApplyDictionaryRendererToFeatureLayerView.swift in Copy Source Code Files */,
D75E5EF42CC04A0C00252595 /* EditFeaturesUsingFeatureFormsView.swift in Copy Source Code Files */,
D7201D072CC6D3D3004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift in Copy Source Code Files */,
Expand Down Expand Up @@ -932,6 +936,8 @@
D7054AE82ACCCB6C007235BA /* Animate3DGraphicView.SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animate3DGraphicView.SettingsView.swift; sourceTree = "<group>"; };
D7058B0D2B59E44B000A888A /* StylePointWithSceneSymbolView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StylePointWithSceneSymbolView.swift; sourceTree = "<group>"; };
D7058FB02ACB423C00A40F14 /* Animate3DGraphicView.Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animate3DGraphicView.Model.swift; sourceTree = "<group>"; };
D707898E2CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplyDictionaryRendererToGraphicsOverlayView.swift; sourceTree = "<group>"; };
D70789992CD16324000DF215 /* Mil2525DMessages.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Mil2525DMessages.xml; sourceTree = "<group>"; };
D7084FA62AD771AA00EC7F4F /* AugmentRealityToFlyOverSceneView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AugmentRealityToFlyOverSceneView.swift; sourceTree = "<group>"; };
D70BE5782A5624A80022CA02 /* CategoriesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoriesView.swift; sourceTree = "<group>"; };
D710996C2A27D9210065A1C1 /* DensifyAndGeneralizeGeometryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DensifyAndGeneralizeGeometryView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1247,6 +1253,7 @@
D7C16D172AC5F6C100689E89 /* Animate 3D graphic */,
D77570BC2A29427200F490CD /* Animate images with image overlay */,
D7F2A02C2CD00F1C0008D981 /* Apply dictionary renderer to feature layer */,
D70789912CD160FD000DF215 /* Apply dictionary renderer to graphics overlay */,
955AFAC52C10FD74009C8FE5 /* Apply mosaic rule to rasters */,
004A2BA12BED456500C297CE /* Apply scheduled updates to preplanned map area */,
D75362CC2A1E862B00D83028 /* Apply unique value renderer */,
Expand Down Expand Up @@ -1495,6 +1502,7 @@
004A2B962BED454300C297CE /* 740b663bff5e4198b9b6674af93f638a */,
D7CE9F9C2AE2F585008F7A5F /* 3424d442ebe54f3cbf34462382d3aebe */,
D735717F2CB613100046A433 /* 5028bf3513ff4c38b28822d010a4937c */,
D707899A2CD16324000DF215 /* 8776cfc26eed4485a03de6316826384c */,
D7C16D232AC5FEA600689E89 /* 12509ffdc684437f8f2656b0129d2c13 */,
D7BB3DD12C5D781800FFCD56 /* 43809fd639f242fd8045ecbafd61a579 */,
10D321912BDB187400B39B1B /* 85282f2aaa2844d8935cdb8722e22a93 */,
Expand Down Expand Up @@ -2039,6 +2047,22 @@
path = "Style point with scene symbol";
sourceTree = "<group>";
};
D70789912CD160FD000DF215 /* Apply dictionary renderer to graphics overlay */ = {
isa = PBXGroup;
children = (
D707898E2CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift */,
);
path = "Apply dictionary renderer to graphics overlay";
sourceTree = "<group>";
};
D707899A2CD16324000DF215 /* 8776cfc26eed4485a03de6316826384c */ = {
isa = PBXGroup;
children = (
D70789992CD16324000DF215 /* Mil2525DMessages.xml */,
);
path = 8776cfc26eed4485a03de6316826384c;
sourceTree = "<group>";
};
D7084FA42AD771AA00EC7F4F /* Augment reality to fly over scene */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -3191,6 +3215,7 @@
Animate3DGraphic,
AnimateImagesWithImageOverlay,
ApplyDictionaryRendererToFeatureLayer,
ApplyDictionaryRendererToGraphicsOverlay,
ApplyScheduledUpdatesToPreplannedMapArea,
AugmentRealityToShowTabletopScene,
ChangeCameraController,
Expand Down Expand Up @@ -3269,6 +3294,7 @@
D7C16D252AC5FEA600689E89 /* Snowdon.csv in Resources */,
D73F8CF42AB1089900CD39DA /* Restaurant.stylx in Resources */,
D7BB3DD22C5D781800FFCD56 /* SaveTheBay.geodatabase in Resources */,
D707899B2CD16324000DF215 /* Mil2525DMessages.xml in Resources */,
D74C8C022ABA6202007C76B8 /* emoji-mobile.stylx in Resources */,
D7CE9FA32AE2F595008F7A5F /* san-diego-eagle-locator in Resources */,
D70539102CD012BB00F63F4A /* militaryoverlay.geodatabase in Resources */,
Expand Down Expand Up @@ -3464,6 +3490,7 @@
1CAB8D4B2A3CEAB0002AA649 /* RunValveIsolationTraceView.Model.swift in Sources */,
E070A0A3286F3B6000F2B606 /* DownloadPreplannedMapAreaView.swift in Sources */,
D79482D42C35D872006521CD /* CreateDynamicBasemapGalleryView.swift in Sources */,
D70789922CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift in Sources */,
D77570C02A2942F800F490CD /* AnimateImagesWithImageOverlayView.swift in Sources */,
D7848EFE2CBD986400F6F546 /* AddElevationSourceFromRasterView.swift in Sources */,
D7054AE92ACCCB6C007235BA /* Animate3DGraphicView.SettingsView.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// 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: 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")!
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Apply dictionary renderer to graphics overlay

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](apply-dictionary-renderer-to-graphics-overlay.png)

## 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](https://arcgis.com/home/item.html?id=d815f3bdf6e6452bb8fd153b654c94ca) 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](https://www.arcgis.com/home/item.html?id=8776cfc26eed4485a03de6316826384c)) is also used. This is downloaded from ArcGIS Online automatically.

## Tags

defense, military, situational awareness, tactical, visualization
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"category": "Visualization",
"description": "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.",
"ignore": false,
"images": [
"apply-dictionary-renderer-to-graphics-overlay.png"
],
"keywords": [
"defense",
"military",
"situational awareness",
"tactical",
"visualization",
"DictionaryRenderer",
"DictionarySymbolStyle",
"GraphicsOverlay"
],
"offline_data": [
"8776cfc26eed4485a03de6316826384c"
],
"redirect_from": [],
"relevant_apis": [
"DictionaryRenderer",
"DictionarySymbolStyle",
"GraphicsOverlay"
],
"snippets": [
"ApplyDictionaryRendererToGraphicsOverlayView.swift"
],
"title": "Apply dictionary renderer to graphics overlay"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading