Skip to content

Commit fa08497

Browse files
authored
Merge pull request #529 from Esri/Caleb/New-AddVectorTiledLayerFromCustomStyle
[New] Add vector tiled layer from custom style
2 parents 285a023 + 7251172 commit fa08497

File tree

6 files changed

+309
-0
lines changed

6 files changed

+309
-0
lines changed

Samples.xcodeproj/project.pbxproj

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@
263263
D71FCB8B2AD628B9000E517C /* CreateMobileGeodatabaseView.Model.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D71FCB892AD6277E000E517C /* CreateMobileGeodatabaseView.Model.swift */; };
264264
D7201CDA2CC6B710004BDB7D /* AddTiledLayerAsBasemapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7201CD42CC6B710004BDB7D /* AddTiledLayerAsBasemapView.swift */; };
265265
D7201CDB2CC6B72A004BDB7D /* AddTiledLayerAsBasemapView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7201CD42CC6B710004BDB7D /* AddTiledLayerAsBasemapView.swift */; };
266+
D7201D042CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7201D002CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift */; };
267+
D7201D072CC6D3D3004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7201D002CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift */; };
268+
D7201D2B2CC6D829004BDB7D /* dodge_city.vtpk in Resources */ = {isa = PBXBuildFile; fileRef = D7201D292CC6D829004BDB7D /* dodge_city.vtpk */; settings = {ASSET_TAGS = (AddVectorTiledLayerFromCustomStyle, ); }; };
266269
D721EEA82ABDFF550040BE46 /* LothianRiversAnno.mmpk in Resources */ = {isa = PBXBuildFile; fileRef = D721EEA72ABDFF550040BE46 /* LothianRiversAnno.mmpk */; settings = {ASSET_TAGS = (ShowMobileMapPackageExpirationDate, ); }; };
267270
D722BD222A420DAD002C2087 /* ShowExtrudedFeaturesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D722BD212A420DAD002C2087 /* ShowExtrudedFeaturesView.swift */; };
268271
D722BD232A420DEC002C2087 /* ShowExtrudedFeaturesView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D722BD212A420DAD002C2087 /* ShowExtrudedFeaturesView.swift */; };
@@ -567,6 +570,7 @@
567570
dstPath = "";
568571
dstSubfolderSpec = 7;
569572
files = (
573+
D7201D072CC6D3D3004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift in Copy Source Code Files */,
570574
D75E5EE92CC0342700252595 /* ListContentsOfKMLFileView.swift in Copy Source Code Files */,
571575
D7201CDB2CC6B72A004BDB7D /* AddTiledLayerAsBasemapView.swift in Copy Source Code Files */,
572576
D7BE7E722CC19CE5006DDB0C /* AddTiledLayerView.swift in Copy Source Code Files */,
@@ -934,6 +938,8 @@
934938
D71D516D2B51D7B600B2A2BE /* SearchForWebMapView.Views.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchForWebMapView.Views.swift; sourceTree = "<group>"; };
935939
D71FCB892AD6277E000E517C /* CreateMobileGeodatabaseView.Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateMobileGeodatabaseView.Model.swift; sourceTree = "<group>"; };
936940
D7201CD42CC6B710004BDB7D /* AddTiledLayerAsBasemapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTiledLayerAsBasemapView.swift; sourceTree = "<group>"; };
941+
D7201D002CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddVectorTiledLayerFromCustomStyleView.swift; sourceTree = "<group>"; };
942+
D7201D292CC6D829004BDB7D /* dodge_city.vtpk */ = {isa = PBXFileReference; lastKnownFileType = file; path = dodge_city.vtpk; sourceTree = "<group>"; };
937943
D721EEA72ABDFF550040BE46 /* LothianRiversAnno.mmpk */ = {isa = PBXFileReference; lastKnownFileType = file; path = LothianRiversAnno.mmpk; sourceTree = "<group>"; };
938944
D722BD212A420DAD002C2087 /* ShowExtrudedFeaturesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShowExtrudedFeaturesView.swift; sourceTree = "<group>"; };
939945
D7232EE02AC1E5AA0079ABFF /* PlayKMLTourView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayKMLTourView.swift; sourceTree = "<group>"; };
@@ -1222,6 +1228,7 @@
12221228
E066DD3E28610F3F004D3D5B /* Add scene layer from service */,
12231229
D7BE7E6E2CC19CC3006DDB0C /* Add tiled layer */,
12241230
D7201CD72CC6B710004BDB7D /* Add tiled layer as basemap */,
1231+
D7201D032CC6D3B5004BDB7D /* Add vector tiled layer from custom style */,
12251232
3E54CF202C66AFA400DD2F18 /* Add web tiled layer */,
12261233
D7E557602A1D743100B9FB09 /* Add WMS layer */,
12271234
1C3B7DC22A5F64FC00907443 /* Analyze network with subnetwork trace */,
@@ -1492,6 +1499,7 @@
14921499
D7C5233F2BED9BBF00E8221A /* e4a398afe9a945f3b0f4dca1e4faccb5 */,
14931500
D7F8C03C2B605AF60072BFA7 /* e12b54ea799f4606a2712157cf9f6e41 */,
14941501
D7C16D262AC5FEB600689E89 /* e87c154fb9c2487f999143df5b08e9b1 */,
1502+
D7201D2A2CC6D829004BDB7D /* f4b742a57af344988b02227e2824ca5f */,
14951503
D7497F3E2AC4BA4100167AD2 /* f5ff6f5556a945bca87ca513b8729a1e */,
14961504
);
14971505
path = "Portal Data";
@@ -2073,6 +2081,22 @@
20732081
path = "Add tiled layer as basemap";
20742082
sourceTree = "<group>";
20752083
};
2084+
D7201D032CC6D3B5004BDB7D /* Add vector tiled layer from custom style */ = {
2085+
isa = PBXGroup;
2086+
children = (
2087+
D7201D002CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift */,
2088+
);
2089+
path = "Add vector tiled layer from custom style";
2090+
sourceTree = "<group>";
2091+
};
2092+
D7201D2A2CC6D829004BDB7D /* f4b742a57af344988b02227e2824ca5f */ = {
2093+
isa = PBXGroup;
2094+
children = (
2095+
D7201D292CC6D829004BDB7D /* dodge_city.vtpk */,
2096+
);
2097+
path = f4b742a57af344988b02227e2824ca5f;
2098+
sourceTree = "<group>";
2099+
};
20762100
D721EEA62ABDFF550040BE46 /* 174150279af74a2ba6f8b87a567f480b */ = {
20772101
isa = PBXGroup;
20782102
children = (
@@ -3115,6 +3139,7 @@
31153139
AddPointCloudLayerFromFile,
31163140
AddRasterFromFile,
31173141
AddTiledLayerAsBasemap,
3142+
AddVectorTiledLayerFromCustomStyle,
31183143
Animate3DGraphic,
31193144
AnimateImagesWithImageOverlay,
31203145
ApplyScheduledUpdatesToPreplannedMapArea,
@@ -3209,6 +3234,7 @@
32093234
D73571D72CB613220046A433 /* hydrography in Resources */,
32103235
D7BEBAC92CBDC81200F882E7 /* MontereyElevation.tpkx in Resources */,
32113236
D7D1F3532ADDBE5D009CE2DA /* philadelphia.mspk in Resources */,
3237+
D7201D2B2CC6D829004BDB7D /* dodge_city.vtpk in Resources */,
32123238
004A2B9D2BED455B00C297CE /* canyonlands in Resources */,
32133239
798C2DA72AFC505600EE7E97 /* PrivacyInfo.xcprivacy in Resources */,
32143240
D7E7D09A2AEB3C47003AAD02 /* san_diego_offline_routing in Resources */,
@@ -3364,6 +3390,7 @@
33643390
1C19B4F32A578E46001D2506 /* CreateLoadReportView.swift in Sources */,
33653391
7900C5F62A83FC3F002D430F /* AddCustomDynamicEntityDataSourceView.Vessel.swift in Sources */,
33663392
D71099702A2802FA0065A1C1 /* DensifyAndGeneralizeGeometryView.SettingsView.swift in Sources */,
3393+
D7201D042CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift in Sources */,
33673394
E004A6ED2849556E002A1FE6 /* CreatePlanarAndGeodeticBuffersView.swift in Sources */,
33683395
E041ABD7287DB04D0056009B /* SampleInfoView.swift in Sources */,
33693396
D7635FFD2B9277DC0044AB97 /* ConfigureClustersView.SettingsView.swift in Sources */,
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
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 AddVectorTiledLayerFromCustomStyleView: View {
19+
/// The view model for the sample.
20+
@StateObject private var model = Model()
21+
22+
/// The viewpoint used to update the map view.
23+
@State private var viewpoint: Viewpoint?
24+
25+
/// The label of the style selected by the picker.
26+
@State private var selectedStyleLabel = "Default"
27+
28+
/// The error shown in the error alert.
29+
@State private var error: Error?
30+
31+
var body: some View {
32+
MapView(map: model.map, viewpoint: viewpoint)
33+
.toolbar {
34+
ToolbarItem(placement: .bottomBar) {
35+
Picker("Style", selection: $selectedStyleLabel) {
36+
Section("Online Styles") {
37+
ForEach(model.onlineStyles, id: \.key) { label, _ in
38+
Text(label)
39+
}
40+
}
41+
Section("Offline Styles") {
42+
ForEach(model.offlineStyles, id: \.key) { label, _ in
43+
Text(label)
44+
}
45+
}
46+
}
47+
.task(id: selectedStyleLabel) {
48+
// Updates the map's layer when the picker selection changes.
49+
do {
50+
viewpoint = try await model.setVectorTiledLayer(
51+
label: selectedStyleLabel
52+
)
53+
} catch {
54+
self.error = error
55+
}
56+
}
57+
.errorAlert(presentingError: $error)
58+
}
59+
}
60+
}
61+
}
62+
63+
// MARK: Model
64+
65+
private extension AddVectorTiledLayerFromCustomStyleView {
66+
/// The view model for the sample.
67+
@MainActor
68+
final class Model: ObservableObject {
69+
/// A map with no specified style.
70+
let map = Map()
71+
72+
/// The labels and portal item IDs of the online styles.
73+
let onlineStyles: KeyValuePairs = [
74+
"Default": "1349bfa0ed08485d8a92c442a3850b06",
75+
"Style 1": "bd8ac41667014d98b933e97713ba8377",
76+
"Style 2": "02f85ec376084c508b9c8e5a311724fa",
77+
"Style 3": "1bf0cc4a4380468fbbff107e100f65a5"
78+
]
79+
80+
/// The labels and portal item IDs of the offline styles.
81+
let offlineStyles: KeyValuePairs = [
82+
"Light": "e01262ef2a4f4d91897d9bbd3a9b1075",
83+
"Dark": "ce8a34e5d4ca4fa193a097511daa8855"
84+
]
85+
86+
/// The cached vector tiled layers keyed by the label of their associated style.
87+
private var vectorTiledLayers: [String: ArcGISVectorTiledLayer] = [:]
88+
89+
/// The URL to the temporary directory for the offline style files.
90+
private let temporaryDirectoryURL = FileManager.createTemporaryDirectory()
91+
92+
/// The vector tile cache for creating the offline vector tiled layers.
93+
private let vectorTileCache = VectorTileCache(name: "dodge_city", bundle: .main)!
94+
95+
deinit {
96+
// Removes all of the temporary offline style files used by sample.
97+
try? FileManager.default.removeItem(at: temporaryDirectoryURL)
98+
}
99+
100+
/// Sets the vector tiled layer for a given style on the map.
101+
/// - Parameter label: The label of the style associated with the layer.
102+
/// - Returns: A viewpoint framing some of the layer's data.
103+
func setVectorTiledLayer(label: String) async throws -> Viewpoint {
104+
// Gets or creates a vector tile layer and adds it to the map as a basemap.
105+
let vectorTiledLayer = if let cachedVectorTiledLayer = vectorTiledLayers[label] {
106+
cachedVectorTiledLayer
107+
} else {
108+
try await cacheVectorTiledLayer(label: label)
109+
}
110+
map.basemap = Basemap(baseLayer: vectorTiledLayer)
111+
112+
return if vectorTiledLayer.vectorTileCache != nil {
113+
// Uses a Dodge City, KS viewpoint if the layer was created using the tile cache.
114+
Viewpoint(latitude: 37.76528, longitude: -100.01766, scale: 4e4)
115+
} else {
116+
// Uses a Europe/Africa viewpoint if the layer was created using an online style.
117+
Viewpoint(latitude: 28.53345, longitude: 17.56488, scale: 1e8)
118+
}
119+
}
120+
121+
/// Creates and caches a vector tiled layer for a given style.
122+
/// - Parameter label: The label of the style associated with the layer.
123+
/// - Returns: The cached `ArcGISVectorTiledLayer`.
124+
private func cacheVectorTiledLayer(label: String) async throws -> ArcGISVectorTiledLayer {
125+
let vectorTiledLayer: ArcGISVectorTiledLayer
126+
if let onlineStyle = onlineStyles.first(where: { $0.key == label }) {
127+
vectorTiledLayer = makeOnlineVectorTiledLayer(itemID: onlineStyle.value)
128+
} else {
129+
let offlineStyle = offlineStyles.first(where: { $0.key == label })!
130+
vectorTiledLayer = try await makeOfflineVectorTiledLayer(itemID: offlineStyle.value)
131+
}
132+
133+
try await vectorTiledLayer.load()
134+
vectorTiledLayers[label] = vectorTiledLayer
135+
136+
return vectorTiledLayer
137+
}
138+
139+
/// Creates a vector tiled layer using a portal item.
140+
/// - Parameter itemID: The ID of the portal item.
141+
/// - Returns: A new `ArcGISVectorTiledLayer` object.
142+
private func makeOnlineVectorTiledLayer(itemID: String) -> ArcGISVectorTiledLayer {
143+
let portalItem = PortalItem(
144+
portal: .arcGISOnline(connection: .anonymous),
145+
id: .init(itemID)!
146+
)
147+
return ArcGISVectorTiledLayer(item: portalItem)
148+
}
149+
150+
/// Creates a vector tiled layer using a local vector tile cache and an item resource cache.
151+
/// - Parameter itemID: The ID of the portal item used to create the export vector tiles task.
152+
/// - Returns: A new `ArcGISVectorTiledLayer` object.
153+
private func makeOfflineVectorTiledLayer(
154+
itemID: String
155+
) async throws -> ArcGISVectorTiledLayer {
156+
// Creates a export style resource cache job using a portal item.
157+
let portalItem = PortalItem(
158+
portal: .arcGISOnline(connection: .anonymous),
159+
id: .init(itemID)!
160+
)
161+
let exportTask = ExportVectorTilesTask(portalItem: portalItem)
162+
163+
let temporaryURL = temporaryDirectoryURL.appendingPathComponent(itemID)
164+
let exportStyleResourceCacheJob = exportTask.makeExportStyleResourceCacheJob(
165+
itemResourceCacheURL: temporaryURL
166+
)
167+
168+
// Gets the item resource cache from the job and uses it to create the layer.
169+
exportStyleResourceCacheJob.start()
170+
let output = try await exportStyleResourceCacheJob.output
171+
172+
return ArcGISVectorTiledLayer(
173+
vectorTileCache: vectorTileCache,
174+
itemResourceCache: output.itemResourceCache
175+
)
176+
}
177+
}
178+
}
179+
180+
// MARK: Helper Extensions
181+
182+
private extension FileManager {
183+
/// Creates a temporary directory.
184+
/// - Returns: The URL of the created directory
185+
static func createTemporaryDirectory() -> URL {
186+
// swiftlint:disable:next force_try
187+
try! FileManager.default.url(
188+
for: .itemReplacementDirectory,
189+
in: .userDomainMask,
190+
appropriateFor: FileManager.default.temporaryDirectory,
191+
create: true
192+
)
193+
}
194+
}
195+
196+
private extension URL {
197+
/// The URL to the local vector tile package file with Dodge City, KS data.
198+
static var dodgeCityVectorTilePackage: URL {
199+
Bundle.main.url(forResource: "dodge_city", withExtension: "vtpk")!
200+
}
201+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Add vector tiled layer from custom style
2+
3+
Load an ArcGIS vector tiled layers using custom styles.
4+
5+
![Screenshot of Add vector tiled layer from custom style sample with online style](add-vector-tiled-layer-from-custom-style-1.png)
6+
![Screenshot of Add vector tiled layer from custom style sample with offline style](add-vector-tiled-layer-from-custom-style-2.png)
7+
8+
## Use case
9+
10+
Vector tile basemaps can be created in ArcGIS Pro and published as offline packages or online services. You can create a custom style tailored to your needs and easily apply them to your map. `ArcGISVectorTiledLayer` has many advantages over traditional raster based basemaps (`ArcGISTiledLayer`), including smooth scaling between different screen DPIs, smaller package sizes, and the ability to rotate symbols and labels dynamically.
11+
12+
## How to use the sample
13+
14+
Pan and zoom to explore the vector tile basemap. Select a theme to see it applied to the vector tile basemap.
15+
16+
## How it works
17+
18+
1. Create an `ArcGISVectorTiledLayer` with the URL of a custom style from AcrGIS Online.
19+
2. Alternatively, create an `ArcGISVectorTiledLayer` by taking a portal item offline and applying it to an offline vector tile package:
20+
i. Create a `PortalItem` using the URL of a custom style.
21+
ii. Create an `ExportVectorTilesTask` using the portal item.
22+
iii. Get the `ExportVectorTilesJob` using `ExportVectorTilesTask.makeExportStyleResourceCacheJob(itemResourceCacheURL:)`.
23+
iv. Start the job using `Job.start()`.
24+
v. Create a `VectorTileCache` using the path of the local vector tile package.
25+
vi. Once the job is complete, create an `ArcGISVectorTiledLayer` using the vector tile cache and the `ItemResourceCache` from the job's result.
26+
3. Create a `Basemap` from the `ArcGISVectorTiledLayer`.
27+
4. Assign the basemap to the map's `basemap`.
28+
29+
## Relevant API
30+
31+
* ArcGISVectorTiledLayer
32+
* ExportVectorTilesTask
33+
* ItemResourceCache
34+
* Map
35+
* VectorTileCache
36+
37+
## Offline data
38+
39+
This sample uses the [Dodge City OSM](https://www.arcgis.com/home/item.html?id=f4b742a57af344988b02227e2824ca5f) vector tile package. It is downloaded from ArcGIS Online automatically.
40+
41+
## Tags
42+
43+
tiles, vector, vector basemap, vector tile package, vector tiled layer, vector tiles, vtpk
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"category": "Visualization",
3+
"description": "Load an ArcGIS vector tiled layers using custom styles.",
4+
"ignore": false,
5+
"images": [
6+
"add-vector-tiled-layer-from-custom-style-1.png",
7+
"add-vector-tiled-layer-from-custom-style-2.png"
8+
],
9+
"keywords": [
10+
"tiles",
11+
"vector",
12+
"vector basemap",
13+
"vector tile package",
14+
"vector tiled layer",
15+
"vector tiles",
16+
"vtpk",
17+
"ArcGISVectorTiledLayer",
18+
"ExportVectorTilesTask",
19+
"ItemResourceCache",
20+
"Map",
21+
"VectorTileCache"
22+
],
23+
"offline_data": [
24+
"f4b742a57af344988b02227e2824ca5f"
25+
],
26+
"redirect_from": [],
27+
"relevant_apis": [
28+
"ArcGISVectorTiledLayer",
29+
"ExportVectorTilesTask",
30+
"ItemResourceCache",
31+
"Map",
32+
"VectorTileCache"
33+
],
34+
"snippets": [
35+
"AddVectorTiledLayerFromCustomStyleView.swift"
36+
],
37+
"title": "Add vector tiled layer from custom style"
38+
}
136 KB
Loading
115 KB
Loading

0 commit comments

Comments
 (0)