Skip to content

Commit 4780b28

Browse files
committed
Merge branch 'v.next' into Caleb/New-AddKMLLayerWithNetworkLinks
2 parents 55c1e2e + 2da1dc8 commit 4780b28

File tree

8 files changed

+292
-2
lines changed

8 files changed

+292
-2
lines changed

Samples.xcodeproj/project.pbxproj

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@
235235
D7058B122B59E468000A888A /* StylePointWithSceneSymbolView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7058B0D2B59E44B000A888A /* StylePointWithSceneSymbolView.swift */; };
236236
D7058FB12ACB423C00A40F14 /* Animate3DGraphicView.Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7058FB02ACB423C00A40F14 /* Animate3DGraphicView.Model.swift */; };
237237
D7058FB22ACB424E00A40F14 /* Animate3DGraphicView.Model.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7058FB02ACB423C00A40F14 /* Animate3DGraphicView.Model.swift */; };
238+
D70789922CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D707898E2CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift */; };
239+
D70789952CD1611E000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D707898E2CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift */; };
240+
D707899B2CD16324000DF215 /* Mil2525DMessages.xml in Resources */ = {isa = PBXBuildFile; fileRef = D70789992CD16324000DF215 /* Mil2525DMessages.xml */; settings = {ASSET_TAGS = (ApplyDictionaryRendererToGraphicsOverlay, ); }; };
238241
D7084FA92AD771AA00EC7F4F /* AugmentRealityToFlyOverSceneView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7084FA62AD771AA00EC7F4F /* AugmentRealityToFlyOverSceneView.swift */; };
239242
D7084FAB2AD771F600EC7F4F /* AugmentRealityToFlyOverSceneView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7084FA62AD771AA00EC7F4F /* AugmentRealityToFlyOverSceneView.swift */; };
240243
D70BE5792A5624A80022CA02 /* CategoriesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70BE5782A5624A80022CA02 /* CategoriesView.swift */; };
@@ -579,6 +582,7 @@
579582
dstSubfolderSpec = 7;
580583
files = (
581584
D751B4CB2CD3E598005CE750 /* AddKMLLayerWithNetworkLinksView.swift in Copy Source Code Files */,
585+
D70789952CD1611E000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift in Copy Source Code Files */,
582586
D7F2A0302CD00F400008D981 /* ApplyDictionaryRendererToFeatureLayerView.swift in Copy Source Code Files */,
583587
D75E5EF42CC04A0C00252595 /* EditFeaturesUsingFeatureFormsView.swift in Copy Source Code Files */,
584588
D7201D072CC6D3D3004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift in Copy Source Code Files */,
@@ -935,6 +939,8 @@
935939
D7054AE82ACCCB6C007235BA /* Animate3DGraphicView.SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animate3DGraphicView.SettingsView.swift; sourceTree = "<group>"; };
936940
D7058B0D2B59E44B000A888A /* StylePointWithSceneSymbolView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StylePointWithSceneSymbolView.swift; sourceTree = "<group>"; };
937941
D7058FB02ACB423C00A40F14 /* Animate3DGraphicView.Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animate3DGraphicView.Model.swift; sourceTree = "<group>"; };
942+
D707898E2CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplyDictionaryRendererToGraphicsOverlayView.swift; sourceTree = "<group>"; };
943+
D70789992CD16324000DF215 /* Mil2525DMessages.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Mil2525DMessages.xml; sourceTree = "<group>"; };
938944
D7084FA62AD771AA00EC7F4F /* AugmentRealityToFlyOverSceneView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AugmentRealityToFlyOverSceneView.swift; sourceTree = "<group>"; };
939945
D70BE5782A5624A80022CA02 /* CategoriesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoriesView.swift; sourceTree = "<group>"; };
940946
D710996C2A27D9210065A1C1 /* DensifyAndGeneralizeGeometryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DensifyAndGeneralizeGeometryView.swift; sourceTree = "<group>"; };
@@ -1252,6 +1258,7 @@
12521258
D7C16D172AC5F6C100689E89 /* Animate 3D graphic */,
12531259
D77570BC2A29427200F490CD /* Animate images with image overlay */,
12541260
D7F2A02C2CD00F1C0008D981 /* Apply dictionary renderer to feature layer */,
1261+
D70789912CD160FD000DF215 /* Apply dictionary renderer to graphics overlay */,
12551262
955AFAC52C10FD74009C8FE5 /* Apply mosaic rule to rasters */,
12561263
004A2BA12BED456500C297CE /* Apply scheduled updates to preplanned map area */,
12571264
D75362CC2A1E862B00D83028 /* Apply unique value renderer */,
@@ -1500,6 +1507,7 @@
15001507
004A2B962BED454300C297CE /* 740b663bff5e4198b9b6674af93f638a */,
15011508
D7CE9F9C2AE2F585008F7A5F /* 3424d442ebe54f3cbf34462382d3aebe */,
15021509
D735717F2CB613100046A433 /* 5028bf3513ff4c38b28822d010a4937c */,
1510+
D707899A2CD16324000DF215 /* 8776cfc26eed4485a03de6316826384c */,
15031511
D7C16D232AC5FEA600689E89 /* 12509ffdc684437f8f2656b0129d2c13 */,
15041512
D7BB3DD12C5D781800FFCD56 /* 43809fd639f242fd8045ecbafd61a579 */,
15051513
10D321912BDB187400B39B1B /* 85282f2aaa2844d8935cdb8722e22a93 */,
@@ -2044,6 +2052,22 @@
20442052
path = "Style point with scene symbol";
20452053
sourceTree = "<group>";
20462054
};
2055+
D70789912CD160FD000DF215 /* Apply dictionary renderer to graphics overlay */ = {
2056+
isa = PBXGroup;
2057+
children = (
2058+
D707898E2CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift */,
2059+
);
2060+
path = "Apply dictionary renderer to graphics overlay";
2061+
sourceTree = "<group>";
2062+
};
2063+
D707899A2CD16324000DF215 /* 8776cfc26eed4485a03de6316826384c */ = {
2064+
isa = PBXGroup;
2065+
children = (
2066+
D70789992CD16324000DF215 /* Mil2525DMessages.xml */,
2067+
);
2068+
path = 8776cfc26eed4485a03de6316826384c;
2069+
sourceTree = "<group>";
2070+
};
20472071
D7084FA42AD771AA00EC7F4F /* Augment reality to fly over scene */ = {
20482072
isa = PBXGroup;
20492073
children = (
@@ -3204,6 +3228,7 @@
32043228
Animate3DGraphic,
32053229
AnimateImagesWithImageOverlay,
32063230
ApplyDictionaryRendererToFeatureLayer,
3231+
ApplyDictionaryRendererToGraphicsOverlay,
32073232
ApplyScheduledUpdatesToPreplannedMapArea,
32083233
AugmentRealityToShowTabletopScene,
32093234
ChangeCameraController,
@@ -3282,6 +3307,7 @@
32823307
D7C16D252AC5FEA600689E89 /* Snowdon.csv in Resources */,
32833308
D73F8CF42AB1089900CD39DA /* Restaurant.stylx in Resources */,
32843309
D7BB3DD22C5D781800FFCD56 /* SaveTheBay.geodatabase in Resources */,
3310+
D707899B2CD16324000DF215 /* Mil2525DMessages.xml in Resources */,
32853311
D74C8C022ABA6202007C76B8 /* emoji-mobile.stylx in Resources */,
32863312
D7CE9FA32AE2F595008F7A5F /* san-diego-eagle-locator in Resources */,
32873313
D70539102CD012BB00F63F4A /* militaryoverlay.geodatabase in Resources */,
@@ -3478,6 +3504,7 @@
34783504
1CAB8D4B2A3CEAB0002AA649 /* RunValveIsolationTraceView.Model.swift in Sources */,
34793505
E070A0A3286F3B6000F2B606 /* DownloadPreplannedMapAreaView.swift in Sources */,
34803506
D79482D42C35D872006521CD /* CreateDynamicBasemapGalleryView.swift in Sources */,
3507+
D70789922CD160FD000DF215 /* ApplyDictionaryRendererToGraphicsOverlayView.swift in Sources */,
34813508
D77570C02A2942F800F490CD /* AnimateImagesWithImageOverlayView.swift in Sources */,
34823509
D7848EFE2CBD986400F6F546 /* AddElevationSourceFromRasterView.swift in Sources */,
34833510
D7054AE92ACCCB6C007235BA /* Animate3DGraphicView.SettingsView.swift in Sources */,
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// Copyright 2024 Esri
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import ArcGIS
16+
import SwiftUI
17+
18+
struct ApplyDictionaryRendererToGraphicsOverlayView: View {
19+
/// A scene with a topographic basemap.
20+
@State private var scene = Scene(basemapStyle: .arcGISTopographic)
21+
22+
/// The graphics overlay for displaying the message graphics on the scene.
23+
@State private var graphicsOverlay = GraphicsOverlay()
24+
25+
/// The camera for zooming the scene view to the message graphics.
26+
@State private var camera: Camera?
27+
28+
/// The error shown in the error alert.
29+
@State private var error: Error?
30+
31+
var body: some View {
32+
SceneView(scene: scene, camera: $camera, graphicsOverlays: [graphicsOverlay])
33+
.task {
34+
do {
35+
// Sets up the graphics overlay when the sample opens.
36+
graphicsOverlay.renderer = try await makeMIL2525DRenderer()
37+
try graphicsOverlay.addGraphics(makeMessageGraphics())
38+
39+
// Sets the camera to look at the graphics in the graphics overlay.
40+
guard let extent = graphicsOverlay.extent else { return }
41+
camera = Camera(
42+
lookingAt: extent.center,
43+
distance: 15_000,
44+
heading: 0,
45+
pitch: 70,
46+
roll: 0
47+
)
48+
} catch {
49+
self.error = error
50+
}
51+
}
52+
.errorAlert(presentingError: $error)
53+
}
54+
55+
/// Creates a dictionary renderer for styling with MIL-STD-2525D symbols.
56+
/// - Returns: A new `DictionaryRenderer` object.
57+
private func makeMIL2525DRenderer() async throws -> DictionaryRenderer {
58+
// Creates a dictionary symbol style from a dictionary style portal item.
59+
let portalItem = PortalItem(
60+
portal: .arcGISOnline(connection: .anonymous),
61+
id: .jointMilitarySymbologyDictionaryStyle
62+
)
63+
let dictionarySymbolStyle = DictionarySymbolStyle(portalItem: portalItem)
64+
try await dictionarySymbolStyle.load()
65+
66+
// Uses the "Ordered Anchor Points" for the symbol style draw rule.
67+
let drawRuleConfiguration = dictionarySymbolStyle.configurations.first { $0.name == "model" }
68+
drawRuleConfiguration?.value = "ORDERED ANCHOR POINTS"
69+
70+
return DictionaryRenderer(dictionarySymbolStyle: dictionarySymbolStyle)
71+
}
72+
73+
/// Creates graphics from messages in an XML file.
74+
/// - Returns: An array of new `Graphic` objects.
75+
private func makeMessageGraphics() throws -> [Graphic] {
76+
// Gets the data from the local XML file.
77+
let messagesData = try Data(contentsOf: .mil2525dMessagesXMLFile)
78+
let parser = MessageParser(data: messagesData)
79+
80+
if parser.parse() {
81+
// Creates graphics from the parsed messages.
82+
return parser.messages.compactMap { message in
83+
guard let messageWKID = message.wkid,
84+
let wkid = WKID(messageWKID) else { return nil }
85+
let spatialReference = SpatialReference(wkid: wkid)
86+
let points = message.controlPoints.map { x, y in
87+
Point(x: x, y: y, spatialReference: spatialReference)
88+
}
89+
return Graphic(geometry: Multipoint(points: points), attributes: message.other)
90+
}
91+
} else if let error = parser.parserError {
92+
throw error
93+
} else {
94+
return []
95+
}
96+
}
97+
}
98+
99+
// MARK: Message Parser
100+
101+
private extension ApplyDictionaryRendererToGraphicsOverlayView {
102+
/// A parser for the XML file containing the MIL-STD-2525D messages.
103+
final class MessageParser: XMLParser, XMLParserDelegate {
104+
/// The parsed messages.
105+
private(set) var messages: [Message] = []
106+
107+
/// The values of the message element currently being parsed.
108+
private var currentMessage: Message?
109+
110+
/// The characters of the XML element currently being parsed.
111+
private var currentElementContents = ""
112+
113+
override init(data: Data) {
114+
super.init(data: data)
115+
self.delegate = self
116+
}
117+
118+
/// Creates a new `currentMessage` when a message start tag is encountered.
119+
func parser(
120+
_ parser: XMLParser,
121+
didStartElement elementName: String,
122+
namespaceURI: String?,
123+
qualifiedName qName: String?,
124+
attributes attributeDict: [String: String] = [:]
125+
) {
126+
if elementName == "message" {
127+
currentMessage = Message()
128+
}
129+
currentElementContents.removeAll()
130+
}
131+
132+
/// Adds the characters of the current element to `currentElementContents`.
133+
func parser(_ parser: XMLParser, foundCharacters string: String) {
134+
currentElementContents.append(contentsOf: string)
135+
}
136+
137+
/// Adds the contents of the current element to the `currentMessage` when an end tag is encountered.
138+
func parser(
139+
_ parser: XMLParser,
140+
didEndElement elementName: String,
141+
namespaceURI: String?,
142+
qualifiedName qName: String?
143+
) {
144+
switch elementName {
145+
case "_control_points":
146+
currentMessage?.controlPoints = currentElementContents.split(separator: ";")
147+
.map { pair in
148+
let coordinates = pair.split(separator: ",")
149+
return (x: Double(coordinates.first!)!, y: Double(coordinates.last!)!)
150+
}
151+
case "message":
152+
messages.append(currentMessage!)
153+
currentMessage = nil
154+
case "messages":
155+
break
156+
case "_wkid":
157+
currentMessage?.wkid = Int(currentElementContents)
158+
default:
159+
currentMessage?.other[elementName] = currentElementContents
160+
}
161+
162+
currentElementContents.removeAll()
163+
}
164+
}
165+
166+
/// The parsed values from an XML message element.
167+
struct Message {
168+
/// The x and y values of the control points element.
169+
var controlPoints: [(x: Double, y: Double)] = []
170+
/// The value of the wkid element.
171+
var wkid: Int?
172+
/// The other elements and their values.
173+
var other: [String: any Sendable] = [:]
174+
}
175+
}
176+
177+
// MARK: Helper Extensions
178+
179+
private extension PortalItem.ID {
180+
/// The ID for the "Joint Military Symbology MIL-STD-2525D" dictionary style portal item on ArcGIS Online.
181+
static var jointMilitarySymbologyDictionaryStyle: Self {
182+
.init("d815f3bdf6e6452bb8fd153b654c94ca")!
183+
}
184+
}
185+
186+
private extension URL {
187+
/// The URL to the local XML file containing messages with MIL-STD-2525D fields.
188+
static var mil2525dMessagesXMLFile: URL {
189+
Bundle.main.url(forResource: "Mil2525DMessages", withExtension: "xml")!
190+
}
191+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Apply dictionary renderer to graphics overlay
2+
3+
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.
4+
5+
![Image of Apply dictionary renderer to graphics overlay sample](apply-dictionary-renderer-to-graphics-overlay.png)
6+
7+
## Use case
8+
9+
Use a dictionary renderer on a graphics overlay to display more transient data, such as military messages coming through a local tactical network.
10+
11+
## How to use the sample
12+
13+
Open the sample and view the military symbols on the scene.
14+
15+
## How it works
16+
17+
1. Create a new `DictionarySymbolStyle` object with a protal item and set the style's draw rule configuration to "ORDERED ANCHOR POINTS".
18+
2. Create a new `DictionaryRenderer` object with the dictionary symbol style.
19+
3. Create an instance of `GraphicsOverlay`.
20+
4. Set the dictionary renderer to the graphics overlay's `renderer`.
21+
5. Parse the local XML file, creating a map of key/value pairs for each block of attributes.
22+
6. Create an instance of `Graphic` for each attribute.
23+
7. Use the `_wkid` key to get the geometry's spatial reference.
24+
8. Use the `_control_points` key to get the geometry's shape.
25+
9. Add the graphics to the graphics overlay.
26+
27+
## Relevant API
28+
29+
* DictionaryRenderer
30+
* DictionarySymbolStyle
31+
* GraphicsOverlay
32+
33+
## About the data
34+
35+
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.
36+
37+
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.
38+
39+
## Tags
40+
41+
defense, military, situational awareness, tactical, visualization
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"category": "Visualization",
3+
"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.",
4+
"ignore": false,
5+
"images": [
6+
"apply-dictionary-renderer-to-graphics-overlay.png"
7+
],
8+
"keywords": [
9+
"defense",
10+
"military",
11+
"situational awareness",
12+
"tactical",
13+
"visualization",
14+
"DictionaryRenderer",
15+
"DictionarySymbolStyle",
16+
"GraphicsOverlay"
17+
],
18+
"offline_data": [
19+
"8776cfc26eed4485a03de6316826384c"
20+
],
21+
"redirect_from": [],
22+
"relevant_apis": [
23+
"DictionaryRenderer",
24+
"DictionarySymbolStyle",
25+
"GraphicsOverlay"
26+
],
27+
"snippets": [
28+
"ApplyDictionaryRendererToGraphicsOverlayView.swift"
29+
],
30+
"title": "Apply dictionary renderer to graphics overlay"
31+
}
211 KB
Loading

Shared/Samples/Configure clusters/ConfigureClustersView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ struct ConfigureClustersView: View {
6161
Button("Clustering Settings") {
6262
showsSettings = true
6363
}
64-
.popover(isPresented: $showsSettings) {
64+
.popover(isPresented: $showsSettings) { [mapViewScale] in
6565
SettingsView(model: model, mapViewScale: mapViewScale)
6666
.presentationDetents([.fraction(0.5)])
6767
.frame(idealWidth: 320, idealHeight: 340)

Shared/Samples/Edit features using feature forms/EditFeaturesUsingFeatureFormsView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ struct EditFeaturesUsingFeatureFormsView: View {
132132
/// The toolbar for the feature form panel.
133133
private var featureFormToolbar: some View {
134134
HStack {
135-
Button("Discard Edits", systemImage: "x.circle", role: .destructive) {
135+
Button("Discard Edits", systemImage: "xmark.circle", role: .destructive) {
136136
isShowingDiscardEditsAlert = true
137137
}
138138

706 Bytes
Loading

0 commit comments

Comments
 (0)