From 2818053cf7ceb53c817ee7be3c5d453396888577 Mon Sep 17 00:00:00 2001 From: Caleb Rasmussen Date: Wed, 16 Oct 2024 10:51:25 -0700 Subject: [PATCH 1/8] Add initial files. --- Samples.xcodeproj/project.pbxproj | 14 +++++++ .../ListContentsOfKMLFileView.swift | 28 +++++++++++++ .../List contents of KML file/README.md | 37 +++++++++++++++++ .../README.metadata.json | 40 +++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift create mode 100644 Shared/Samples/List contents of KML file/README.md create mode 100644 Shared/Samples/List contents of KML file/README.metadata.json diff --git a/Samples.xcodeproj/project.pbxproj b/Samples.xcodeproj/project.pbxproj index 2e2b476a..6b467a27 100644 --- a/Samples.xcodeproj/project.pbxproj +++ b/Samples.xcodeproj/project.pbxproj @@ -336,6 +336,8 @@ D75B58512AAFB3030038B3B4 /* StyleFeaturesWithCustomDictionaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75B58502AAFB3030038B3B4 /* StyleFeaturesWithCustomDictionaryView.swift */; }; D75B58522AAFB37C0038B3B4 /* StyleFeaturesWithCustomDictionaryView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D75B58502AAFB3030038B3B4 /* StyleFeaturesWithCustomDictionaryView.swift */; }; D75C35672AB50338003CD55F /* GroupLayersTogetherView.GroupLayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75C35662AB50338003CD55F /* GroupLayersTogetherView.GroupLayerListView.swift */; }; + D75E5EE62CC0340100252595 /* ListContentsOfKMLFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75E5EE22CC0340100252595 /* ListContentsOfKMLFileView.swift */; }; + D75E5EE92CC0342700252595 /* ListContentsOfKMLFileView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D75E5EE22CC0340100252595 /* ListContentsOfKMLFileView.swift */; }; D75F66362B48EABC00434974 /* SearchForWebMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75F66332B48EABC00434974 /* SearchForWebMapView.swift */; }; D75F66392B48EB1800434974 /* SearchForWebMapView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D75F66332B48EABC00434974 /* SearchForWebMapView.swift */; }; D76000A22AF18BAB00B3084D /* FindRouteInTransportNetworkView.Model.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7749AD52AF08BF50086632F /* FindRouteInTransportNetworkView.Model.swift */; }; @@ -549,6 +551,7 @@ dstPath = ""; dstSubfolderSpec = 7; files = ( + D75E5EE92CC0342700252595 /* ListContentsOfKMLFileView.swift in Copy Source Code Files */, D7848EDB2CBD85D100F6F546 /* AddPointSceneLayerView.swift in Copy Source Code Files */, D7DFA0ED2CBA0260007C31F2 /* AddMapImageLayerView.swift in Copy Source Code Files */, D7CDD38C2CB86F4A00DE9766 /* AddPointCloudLayerFromFileView.swift in Copy Source Code Files */, @@ -949,6 +952,7 @@ D7588F5C2B7D8DAA008B75E2 /* NavigateRouteWithReroutingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigateRouteWithReroutingView.swift; sourceTree = ""; }; D75B58502AAFB3030038B3B4 /* StyleFeaturesWithCustomDictionaryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyleFeaturesWithCustomDictionaryView.swift; sourceTree = ""; }; D75C35662AB50338003CD55F /* GroupLayersTogetherView.GroupLayerListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupLayersTogetherView.GroupLayerListView.swift; sourceTree = ""; }; + D75E5EE22CC0340100252595 /* ListContentsOfKMLFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListContentsOfKMLFileView.swift; sourceTree = ""; }; D75F66332B48EABC00434974 /* SearchForWebMapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchForWebMapView.swift; sourceTree = ""; }; D76000AB2AF19C2300B3084D /* FindRouteInMobileMapPackageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindRouteInMobileMapPackageView.swift; sourceTree = ""; }; D76000B62AF19FCA00B3084D /* SanFrancisco.mmpk */ = {isa = PBXFileReference; lastKnownFileType = file; path = SanFrancisco.mmpk; sourceTree = ""; }; @@ -1254,6 +1258,7 @@ D70082E72ACF8F6C00E0C3C2 /* Identify KML features */, D751018A2A2E960300B8FA48 /* Identify layer features */, D7464F182ACE0445007FEE88 /* Identify raster cell */, + D75E5EE52CC0340100252595 /* List contents of KML file */, D776880E2B69826B007C3860 /* List spatial reference transformations */, D718A1E92B575FD900447087 /* Manage bookmarks */, D752D93C2A3914E5003EB25E /* Manage operational layers */, @@ -2283,6 +2288,14 @@ path = "Style features with custom dictionary"; sourceTree = ""; }; + D75E5EE52CC0340100252595 /* List contents of KML file */ = { + isa = PBXGroup; + children = ( + D75E5EE22CC0340100252595 /* ListContentsOfKMLFileView.swift */, + ); + path = "List contents of KML file"; + sourceTree = ""; + }; D75F66322B48EABC00434974 /* Search for web map */ = { isa = PBXGroup; children = ( @@ -3278,6 +3291,7 @@ 1C19B4F12A578E46001D2506 /* CreateLoadReportView.Views.swift in Sources */, E066DD3B2860CA08004D3D5B /* ShowResultOfSpatialRelationshipsView.swift in Sources */, 7573E81E29D6134C00BEED9C /* TraceUtilityNetworkView.Views.swift in Sources */, + D75E5EE62CC0340100252595 /* ListContentsOfKMLFileView.swift in Sources */, 4D2ADC5A29C4F612003B367F /* ChangeMapViewBackgroundView.swift in Sources */, 95DEB9B62C127A92009BEC35 /* ShowViewshedFromPointOnMapView.swift in Sources */, D7BA38972BFBFC0F009954F5 /* QueryRelatedFeaturesView.swift in Sources */, diff --git a/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift new file mode 100644 index 00000000..9daec313 --- /dev/null +++ b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift @@ -0,0 +1,28 @@ +// 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 ListContentsOfKMLFileView: View { + @State private var map = Map(basemapStyle: .arcGISTopographic) + + var body: some View { + MapView(map: map) + } +} + +#Preview { + ListContentsOfKMLFileView() +} diff --git a/Shared/Samples/List contents of KML file/README.md b/Shared/Samples/List contents of KML file/README.md new file mode 100644 index 00000000..5308bc93 --- /dev/null +++ b/Shared/Samples/List contents of KML file/README.md @@ -0,0 +1,37 @@ +# List contents of KML file + +List the contents of a KML file. + +![Image of list contents of KML file](ListContentsOfKMLFile.jpg) + +## Use case + +KML files can contain a hierarchy of features, including network links to other KML content. A user may wish to traverse through the contents of KML nodes to know what data is contained within each node and, recursively, their children. + +## How to use the sample + +The contents of the KML file are shown in a tree. Select a node to zoom to that node. Not all nodes can be zoomed to (e.g. screen overlays). + +## How it works + +1. Add the KML file to the scene as a layer. +2. Explore the root nodes of the `KMLDataset` recursively explored to create a view model. + * Each node is enabled for display at this step. KML files may include nodes that are turned off by default. +3. When a node is selected, use the node's `Extent` to determine a viewpoint and set the `SceneView` object's viewpoint do it. + +## Relevant API + +* KMLContainer +* KMLDataset +* KMLDocument +* KMLFolder +* KMLGroundOverlay +* KMLLayer +* KMLNetworkLink +* KMLNode +* KMLPlacemark +* KMLScreenOverlay + +## Tags + +Keyhole, KML, KMZ, layers, OGC diff --git a/Shared/Samples/List contents of KML file/README.metadata.json b/Shared/Samples/List contents of KML file/README.metadata.json new file mode 100644 index 00000000..d98ee2a0 --- /dev/null +++ b/Shared/Samples/List contents of KML file/README.metadata.json @@ -0,0 +1,40 @@ +{ + "category": "Edit and Manage Data", + "description": "List the contents of a KML file.", + "ignore": false, + "images": [], + "keywords": [ + "KML", + "KMZ", + "Keyhole", + "OGC", + "layers", + "KMLContainer", + "KMLDataset", + "KMLDocument", + "KMLFolder", + "KMLGroundOverlay", + "KMLLayer", + "KMLNetworkLink", + "KMLNode", + "KMLPlacemark", + "KMLScreenOverlay" + ], + "redirect_from": [], + "relevant_apis": [ + "KMLContainer", + "KMLDataset", + "KMLDocument", + "KMLFolder", + "KMLGroundOverlay", + "KMLLayer", + "KMLNetworkLink", + "KMLNode", + "KMLPlacemark", + "KMLScreenOverlay" + ], + "snippets": [ + "ListContentsOfKMLFileView.swift" + ], + "title": "List contents of KML file" +} From d6d94e735551bbe5fb19feca5f0f7b82559b8d76 Mon Sep 17 00:00:00 2001 From: Caleb Rasmussen Date: Wed, 16 Oct 2024 12:13:13 -0700 Subject: [PATCH 2/8] Add offline data. --- Samples.xcodeproj/project.pbxproj | 13 +++++++++++++ Shared/Samples/List contents of KML file/README.md | 4 ++++ .../List contents of KML file/README.metadata.json | 3 +++ 3 files changed, 20 insertions(+) diff --git a/Samples.xcodeproj/project.pbxproj b/Samples.xcodeproj/project.pbxproj index 6b467a27..b3f1177b 100644 --- a/Samples.xcodeproj/project.pbxproj +++ b/Samples.xcodeproj/project.pbxproj @@ -338,6 +338,7 @@ D75C35672AB50338003CD55F /* GroupLayersTogetherView.GroupLayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75C35662AB50338003CD55F /* GroupLayersTogetherView.GroupLayerListView.swift */; }; D75E5EE62CC0340100252595 /* ListContentsOfKMLFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75E5EE22CC0340100252595 /* ListContentsOfKMLFileView.swift */; }; D75E5EE92CC0342700252595 /* ListContentsOfKMLFileView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D75E5EE22CC0340100252595 /* ListContentsOfKMLFileView.swift */; }; + D75E5EEC2CC0466900252595 /* esri_test_data.kmz in Resources */ = {isa = PBXBuildFile; fileRef = D75E5EEA2CC0466900252595 /* esri_test_data.kmz */; settings = {ASSET_TAGS = (ListContentsOfKmlFile, ); }; }; D75F66362B48EABC00434974 /* SearchForWebMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75F66332B48EABC00434974 /* SearchForWebMapView.swift */; }; D75F66392B48EB1800434974 /* SearchForWebMapView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D75F66332B48EABC00434974 /* SearchForWebMapView.swift */; }; D76000A22AF18BAB00B3084D /* FindRouteInTransportNetworkView.Model.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7749AD52AF08BF50086632F /* FindRouteInTransportNetworkView.Model.swift */; }; @@ -953,6 +954,7 @@ D75B58502AAFB3030038B3B4 /* StyleFeaturesWithCustomDictionaryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyleFeaturesWithCustomDictionaryView.swift; sourceTree = ""; }; D75C35662AB50338003CD55F /* GroupLayersTogetherView.GroupLayerListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupLayersTogetherView.GroupLayerListView.swift; sourceTree = ""; }; D75E5EE22CC0340100252595 /* ListContentsOfKMLFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListContentsOfKMLFileView.swift; sourceTree = ""; }; + D75E5EEA2CC0466900252595 /* esri_test_data.kmz */ = {isa = PBXFileReference; lastKnownFileType = file; path = esri_test_data.kmz; sourceTree = ""; }; D75F66332B48EABC00434974 /* SearchForWebMapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchForWebMapView.swift; sourceTree = ""; }; D76000AB2AF19C2300B3084D /* FindRouteInMobileMapPackageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindRouteInMobileMapPackageView.swift; sourceTree = ""; }; D76000B62AF19FCA00B3084D /* SanFrancisco.mmpk */ = {isa = PBXFileReference; lastKnownFileType = file; path = SanFrancisco.mmpk; sourceTree = ""; }; @@ -1445,6 +1447,7 @@ 00D4EF8128638BF100B9CC30 /* cb1b20748a9f4d128dad8a87244e3e37 */, 4D126D7629CA3B3F00CFB7A7 /* d5bad9f4fee9483791e405880fb466da */, D77572AC2A295DC100F490CD /* d1453556d91e46dea191c20c398b82cd */, + D75E5EEB2CC0466900252595 /* da301cb122874d5497f8a8f6c81eb36e */, D7E7D0862AEB3C36003AAD02 /* df193653ed39449195af0c9725701dca */, F111CCC2288B63DB00205358 /* e1f3a7254cb845b09450f54937c16061 */, D7C5233F2BED9BBF00E8221A /* e4a398afe9a945f3b0f4dca1e4faccb5 */, @@ -2296,6 +2299,14 @@ path = "List contents of KML file"; sourceTree = ""; }; + D75E5EEB2CC0466900252595 /* da301cb122874d5497f8a8f6c81eb36e */ = { + isa = PBXGroup; + children = ( + D75E5EEA2CC0466900252595 /* esri_test_data.kmz */, + ); + path = da301cb122874d5497f8a8f6c81eb36e; + sourceTree = ""; + }; D75F66322B48EABC00434974 /* Search for web map */ = { isa = PBXGroup; children = ( @@ -3005,6 +3016,7 @@ GenerateOfflineMapWithLocalBasemap, GeocodeOffline, IdentifyRasterCell, + ListContentsOfKmlFile, NavigateRouteWithRerouting, OrbitCameraAroundObject, ShowDeviceLocationWithNmeaDataSources, @@ -3085,6 +3097,7 @@ D7E7D09A2AEB3C47003AAD02 /* san_diego_offline_routing in Resources */, F111CCC4288B641900205358 /* Yellowstone.mmpk in Resources */, D77572AE2A295DDE00F490CD /* PacificSouthWest2 in Resources */, + D75E5EEC2CC0466900252595 /* esri_test_data.kmz in Resources */, 10D321932BDB187400B39B1B /* naperville_imagery.tpkx in Resources */, 00D4EFB12863CE6300B9CC30 /* ScottishWildlifeTrust_reserves in Resources */, D7781D492B7EB03400E53C51 /* SanDiegoTourPath.json in Resources */, diff --git a/Shared/Samples/List contents of KML file/README.md b/Shared/Samples/List contents of KML file/README.md index 5308bc93..d19c4c9c 100644 --- a/Shared/Samples/List contents of KML file/README.md +++ b/Shared/Samples/List contents of KML file/README.md @@ -32,6 +32,10 @@ The contents of the KML file are shown in a tree. Select a node to zoom to that * KMLPlacemark * KMLScreenOverlay +## Offline data + +This sample uses the [esri_test_data](https://www.arcgis.com/home/item.html?id=da301cb122874d5497f8a8f6c81eb36e) KML file. It is downloaded from ArcGIS Online automatically. + ## Tags Keyhole, KML, KMZ, layers, OGC diff --git a/Shared/Samples/List contents of KML file/README.metadata.json b/Shared/Samples/List contents of KML file/README.metadata.json index d98ee2a0..89e727af 100644 --- a/Shared/Samples/List contents of KML file/README.metadata.json +++ b/Shared/Samples/List contents of KML file/README.metadata.json @@ -20,6 +20,9 @@ "KMLPlacemark", "KMLScreenOverlay" ], + "offline_data": [ + "da301cb122874d5497f8a8f6c81eb36e" + ], "redirect_from": [], "relevant_apis": [ "KMLContainer", From 6770316347474f0aed5b3c08019f51093c2d0db6 Mon Sep 17 00:00:00 2001 From: Caleb Rasmussen Date: Fri, 18 Oct 2024 17:21:07 -0700 Subject: [PATCH 3/8] Add source code. --- .../ListContentsOfKMLFileView.swift | 241 +++++++++++++++++- 1 file changed, 237 insertions(+), 4 deletions(-) diff --git a/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift index 9daec313..58dce4c7 100644 --- a/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift +++ b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift @@ -16,13 +16,246 @@ import ArcGIS import SwiftUI struct ListContentsOfKMLFileView: View { - @State private var map = Map(basemapStyle: .arcGISTopographic) + /// The view model for the sample. + @StateObject private var model = Model() var body: some View { - MapView(map: map) + VStack(spacing: 0) { + Text("Tap on a disclosure to reveal a node's children. Tap on a node to open it in a scene.") + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity, alignment: .center) + .padding(8) + .background(Color(.systemGroupedBackground)) + + // Recursively displays the dataset's nodes in a list. + List(model.kmlDataset?.rootNodes ?? [], id: \.name, children: \.children) { node in + VStack(alignment: .leading) { + NavigationLink { + if let viewpoint = model.nodeViewpoints[node.name] { + SceneView(scene: model.scene, viewpoint: viewpoint) + .navigationTitle(node.name) + } else { + Text("This node has no extent to view.") + .navigationTitle(node.name) + } + } label: { + VStack(alignment: .leading) { + if !node.name.isEmpty { + Text(node.name) + } + Text(node.typeLabel) + .font(.footnote) + } +#if targetEnvironment(macCatalyst) + .padding(.leading) +#endif + } + } + } + } + .errorAlert(presentingError: $model.error) } } -#Preview { - ListContentsOfKMLFileView() +// MARK: Model + +extension ListContentsOfKMLFileView { + /// The view model for the sample. + @MainActor + fileprivate final class Model: ObservableObject { + /// A dataset containing the KML data from a local file. + @Published private(set) var kmlDataset: KMLDataset? + + /// The viewpoints for the nodes in the dataset. + @Published private(set) var nodeViewpoints: [String: Viewpoint] = [:] + + /// The error shown in the error alert. + @Published var error: Error? + + /// A scene for displaying the KML data. + let scene: ArcGIS.Scene = { + let scene = Scene(basemapStyle: .arcGISImagery) + let elevationSource = ArcGISTiledElevationSource(url: .worldElevationService) + scene.baseSurface.addElevationSource(elevationSource) + return scene + }() + + /// The task used for the asynchronous setup operations. + private var setupTask: Task? + + init() { + setupTask = Task { [weak self] in + guard let self else { return } + + do { + try await setUpKMLDataset() + } catch { + self.error = error + } + } + } + + deinit { + setupTask?.cancel() + } + + /// Sets up the KML dataset and adds it to the scene as layer. + private func setUpKMLDataset() async throws { + // Creates the dataset using a local ".kml" file in the bundle. + let kmlDataset = KMLDataset(name: "esri_test_data", bundle: .main)! + try await kmlDataset.load() + self.kmlDataset = kmlDataset + + // Adds the dataset to the scene as a KML layer. + let kmlLayer = KMLLayer(dataset: kmlDataset) + scene.addOperationalLayer(kmlLayer) + try await scene.load() + + try await setUpKMLNodes(kmlDataset.rootNodes) + } + + /// Recursively creates viewpoints for KML nodes in a given list. + /// - Parameter kmlNodes: The list of KML nodes to set up. + private func setUpKMLNodes(_ kmlNodes: [KMLNode]) async throws { + // Loads the surface so that the elevation can be queried when the viewpoint is made. + try await scene.baseSurface.load() + + for node in kmlNodes { + let viewpoint = try await Viewpoint(kmlNode: node, surface: scene.baseSurface) + nodeViewpoints[node.name] = viewpoint + + // Ensures the node is visible since some are hidden by default. + node.isVisible = true + + if let childNodes = node.children { + try await setUpKMLNodes(childNodes) + } + } + } + } +} + +// MARK: Helper Extensions + +extension KMLNode { + /// The child nodes of the node, if any. + fileprivate var children: [KMLNode]? { + switch self { + case let container as KMLContainer: + container.childNodes + case let networkLink as KMLNetworkLink: + networkLink.childNodes + default: + nil + } + } + + /// A human-readable label of the type of the node. + fileprivate var typeLabel: String { + switch self { + case is KMLDocument: "Document" + case is KMLFolder: "Folder" + case is KMLContainer: "Container" + case is KMLGroundOverlay: "Ground Overlay" + case is KMLNetworkLink: "Network Link" + case is KMLPhotoOverlay: "Photo Overlay" + case is KMLPlacemark: "Placemark" + case is KMLScreenOverlay: "Screen Overlay" + case is KMLTour: "Tour" + default: "Unknown" + } + } +} + +extension Viewpoint { + /// Creates a viewpoint from a KML node. + /// - Parameters: + /// - kmlNode: The KML node. + /// - surface: A surface for determining the elevation needed to offset the viewpoint. + fileprivate init?(kmlNode: KMLNode, surface: Surface) async throws { + if let kmlViewpoint = kmlNode.viewpoint { + try await self.init(kmlViewpoint: kmlViewpoint, surface: surface) + } else if let extent = kmlNode.extent { + // the node does not have a predefined viewpoint, so create a viewpoint based on its extent + try await self.init(kmlNodeExtent: extent, surface: surface) + } else { + return nil + } + } + + /// Creates a viewpoint from a KML viewpoint. + /// - Parameters: + /// - kmlViewpoint: The KML viewpoint. + /// - surface: A surface for determining the elevation needed to offset the viewpoint. + private init(kmlViewpoint: KMLViewpoint, surface: Surface) async throws { + switch kmlViewpoint.kind { + case .lookAt: + var lookAtPoint = kmlViewpoint.location + if kmlViewpoint.altitudeMode != .absolute { + // If the elevation is relative, account for the surface's elevation. + let elevation = try await surface.elevation(at: kmlViewpoint.location) + lookAtPoint = kmlViewpoint.location.withBuilder { $0.z += elevation } + } + + let camera = Camera( + lookingAt: lookAtPoint, + distance: kmlViewpoint.range, + heading: kmlViewpoint.heading, + pitch: kmlViewpoint.pitch, + roll: kmlViewpoint.roll + ) + self.init(latitude: .nan, longitude: .nan, scale: .nan, camera: camera) + case .camera: + let camera = Camera( + location: kmlViewpoint.location, + heading: kmlViewpoint.heading, + pitch: kmlViewpoint.pitch, + roll: kmlViewpoint.roll + ) + self.init(latitude: .nan, longitude: .nan, scale: .nan, camera: camera) + @unknown default: + fatalError("Unexpected KMLViewpoint.Kind: \(kmlViewpoint.kind)") + } + } + + /// Creates a viewpoint from the extent of a KML node. + /// - Parameters: + /// - extent: The extent of a KML node. + /// - surface: A surface for determining the elevation needed to offset the viewpoint. + private init?(kmlNodeExtent extent: Envelope, surface: Surface) async throws { + // Ensures the extent isn't empty since some nodes don't include a geometry. + guard !extent.isEmpty else { return nil } + + let extentCenter = extent.center + let elevation = try await surface.elevation(at: extentCenter) + + if extent.extent.width == 0, extent.height == 0 { + // If the extent is not empty, but the width and height are still zero, + // default values (based on Google Earth) are used to create a camera. + let elevatedCenter = extentCenter.withBuilder { $0.z += elevation } + let camera = Camera( + lookingAt: elevatedCenter, + distance: 1000, + heading: 0, + pitch: 45, + roll: 0 + ) + self.init(latitude: .nan, longitude: .nan, scale: .nan, camera: camera) + } else { + // Adds the elevation and a buffer to the extent. + let bufferedExtent = extent.withBuilder { builder in + builder.zMin += elevation + builder.zMax += elevation + builder.expand(by: 1.1) + } + self.init(boundingGeometry: bufferedExtent) + } + } +} + +extension URL { + /// A web URL to the Terrain3D image server on ArcGIS REST. + fileprivate static var worldElevationService: URL { + URL(string: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer")! + } } From 643186b79099fa341fa5d4b3e58f255235ea9197 Mon Sep 17 00:00:00 2001 From: Caleb Rasmussen Date: Fri, 18 Oct 2024 17:30:56 -0700 Subject: [PATCH 4/8] Update readme. --- .../Samples/List contents of KML file/README.md | 8 ++++---- .../README.metadata.json | 4 +++- .../list-contents-of-kml-file.png | Bin 0 -> 59078 bytes 3 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 Shared/Samples/List contents of KML file/list-contents-of-kml-file.png diff --git a/Shared/Samples/List contents of KML file/README.md b/Shared/Samples/List contents of KML file/README.md index d19c4c9c..e9915fa3 100644 --- a/Shared/Samples/List contents of KML file/README.md +++ b/Shared/Samples/List contents of KML file/README.md @@ -2,7 +2,7 @@ List the contents of a KML file. -![Image of list contents of KML file](ListContentsOfKMLFile.jpg) +![Screenshot of List contents of KML file sample](list-contents-of-kml-file.png) ## Use case @@ -10,14 +10,14 @@ KML files can contain a hierarchy of features, including network links to other ## How to use the sample -The contents of the KML file are shown in a tree. Select a node to zoom to that node. Not all nodes can be zoomed to (e.g. screen overlays). +The contents of the KML file are shown in a tree. Tap on a disclosure to reveal a node's children. Tap on a node to open it in a scene zoomed to that node. Not all nodes can be zoomed to (e.g., screen overlays). ## How it works 1. Add the KML file to the scene as a layer. 2. Explore the root nodes of the `KMLDataset` recursively explored to create a view model. - * Each node is enabled for display at this step. KML files may include nodes that are turned off by default. -3. When a node is selected, use the node's `Extent` to determine a viewpoint and set the `SceneView` object's viewpoint do it. + * Each node is enabled for display at this step. KML files may include nodes that are turned off by default. +3. When a node is selected, use the node's `extent` to create a `Viewpoint` and pass it to the `SceneView`. ## Relevant API diff --git a/Shared/Samples/List contents of KML file/README.metadata.json b/Shared/Samples/List contents of KML file/README.metadata.json index 89e727af..b83a9bf4 100644 --- a/Shared/Samples/List contents of KML file/README.metadata.json +++ b/Shared/Samples/List contents of KML file/README.metadata.json @@ -2,7 +2,9 @@ "category": "Edit and Manage Data", "description": "List the contents of a KML file.", "ignore": false, - "images": [], + "images": [ + "list-contents-of-kml-file.png" + ], "keywords": [ "KML", "KMZ", diff --git a/Shared/Samples/List contents of KML file/list-contents-of-kml-file.png b/Shared/Samples/List contents of KML file/list-contents-of-kml-file.png new file mode 100644 index 0000000000000000000000000000000000000000..71d485dbbebabdcf06865380ec88deca5d5d725b GIT binary patch literal 59078 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 Date: Fri, 18 Oct 2024 17:52:16 -0700 Subject: [PATCH 5/8] Remove `fileprivate` keywords. --- .../ListContentsOfKMLFileView.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift index 58dce4c7..81a78204 100644 --- a/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift +++ b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift @@ -59,10 +59,10 @@ struct ListContentsOfKMLFileView: View { // MARK: Model -extension ListContentsOfKMLFileView { +private extension ListContentsOfKMLFileView { /// The view model for the sample. @MainActor - fileprivate final class Model: ObservableObject { + final class Model: ObservableObject { /// A dataset containing the KML data from a local file. @Published private(set) var kmlDataset: KMLDataset? @@ -137,9 +137,9 @@ extension ListContentsOfKMLFileView { // MARK: Helper Extensions -extension KMLNode { +private extension KMLNode { /// The child nodes of the node, if any. - fileprivate var children: [KMLNode]? { + var children: [KMLNode]? { switch self { case let container as KMLContainer: container.childNodes @@ -151,7 +151,7 @@ extension KMLNode { } /// A human-readable label of the type of the node. - fileprivate var typeLabel: String { + var typeLabel: String { switch self { case is KMLDocument: "Document" case is KMLFolder: "Folder" @@ -167,12 +167,12 @@ extension KMLNode { } } -extension Viewpoint { +private extension Viewpoint { /// Creates a viewpoint from a KML node. /// - Parameters: /// - kmlNode: The KML node. /// - surface: A surface for determining the elevation needed to offset the viewpoint. - fileprivate init?(kmlNode: KMLNode, surface: Surface) async throws { + init?(kmlNode: KMLNode, surface: Surface) async throws { if let kmlViewpoint = kmlNode.viewpoint { try await self.init(kmlViewpoint: kmlViewpoint, surface: surface) } else if let extent = kmlNode.extent { @@ -253,9 +253,9 @@ extension Viewpoint { } } -extension URL { +private extension URL { /// A web URL to the Terrain3D image server on ArcGIS REST. - fileprivate static var worldElevationService: URL { + static var worldElevationService: URL { URL(string: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer")! } } From 908ab767647cc8ccfe10fd4ebd0440d6200977a9 Mon Sep 17 00:00:00 2001 From: Caleb Rasmussen Date: Mon, 21 Oct 2024 13:08:23 -0700 Subject: [PATCH 6/8] Reword comments. https://github.com/Esri/arcgis-maps-sdk-swift-samples/pull/526#discussion_r1809304138 --- .../ListContentsOfKMLFileView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift index 81a78204..83a78037 100644 --- a/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift +++ b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift @@ -171,7 +171,7 @@ private extension Viewpoint { /// Creates a viewpoint from a KML node. /// - Parameters: /// - kmlNode: The KML node. - /// - surface: A surface for determining the elevation needed to offset the viewpoint. + /// - surface: A surface that determines the elevation needed to offset the viewpoint. init?(kmlNode: KMLNode, surface: Surface) async throws { if let kmlViewpoint = kmlNode.viewpoint { try await self.init(kmlViewpoint: kmlViewpoint, surface: surface) @@ -186,7 +186,7 @@ private extension Viewpoint { /// Creates a viewpoint from a KML viewpoint. /// - Parameters: /// - kmlViewpoint: The KML viewpoint. - /// - surface: A surface for determining the elevation needed to offset the viewpoint. + /// - surface: A surface that determines the elevation needed to offset the viewpoint. private init(kmlViewpoint: KMLViewpoint, surface: Surface) async throws { switch kmlViewpoint.kind { case .lookAt: @@ -221,7 +221,7 @@ private extension Viewpoint { /// Creates a viewpoint from the extent of a KML node. /// - Parameters: /// - extent: The extent of a KML node. - /// - surface: A surface for determining the elevation needed to offset the viewpoint. + /// - surface: A surface that determines the elevation needed to offset the viewpoint. private init?(kmlNodeExtent extent: Envelope, surface: Surface) async throws { // Ensures the extent isn't empty since some nodes don't include a geometry. guard !extent.isEmpty else { return nil } From c38ee59d36cb77e9c0fdb29f8ec6617fd756f446 Mon Sep 17 00:00:00 2001 From: Caleb Rasmussen Date: Tue, 22 Oct 2024 16:48:55 -0700 Subject: [PATCH 7/8] Fix comment. https://github.com/Esri/arcgis-maps-sdk-swift-samples/pull/526#discussion_r1811559398 --- .../List contents of KML file/ListContentsOfKMLFileView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift index 83a78037..2903814a 100644 --- a/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift +++ b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift @@ -176,7 +176,7 @@ private extension Viewpoint { if let kmlViewpoint = kmlNode.viewpoint { try await self.init(kmlViewpoint: kmlViewpoint, surface: surface) } else if let extent = kmlNode.extent { - // the node does not have a predefined viewpoint, so create a viewpoint based on its extent + // Creates a viewpoint with node's extent if it doesn't have a predefined viewpoint. try await self.init(kmlNodeExtent: extent, surface: surface) } else { return nil From ea1f3bf47e5f61db777a28a3e0516cf0e065c061 Mon Sep 17 00:00:00 2001 From: Caleb Rasmussen Date: Tue, 22 Oct 2024 16:51:01 -0700 Subject: [PATCH 8/8] Make setupTask Task. https://github.com/Esri/arcgis-maps-sdk-swift-samples/pull/526#discussion_r1811567738 --- .../List contents of KML file/ListContentsOfKMLFileView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift index 2903814a..b1e9e82f 100644 --- a/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift +++ b/Shared/Samples/List contents of KML file/ListContentsOfKMLFileView.swift @@ -81,7 +81,7 @@ private extension ListContentsOfKMLFileView { }() /// The task used for the asynchronous setup operations. - private var setupTask: Task? + private var setupTask: Task? init() { setupTask = Task { [weak self] in