Skip to content

Commit

Permalink
Merge pull request #529 from Esri/Caleb/New-AddVectorTiledLayerFromCu…
Browse files Browse the repository at this point in the history
…stomStyle

[New] Add vector tiled layer from custom style
  • Loading branch information
CalebRas authored Oct 23, 2024
2 parents 285a023 + 7251172 commit fa08497
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 0 deletions.
27 changes: 27 additions & 0 deletions Samples.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@
D71FCB8B2AD628B9000E517C /* CreateMobileGeodatabaseView.Model.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D71FCB892AD6277E000E517C /* CreateMobileGeodatabaseView.Model.swift */; };
D7201CDA2CC6B710004BDB7D /* AddTiledLayerAsBasemapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7201CD42CC6B710004BDB7D /* AddTiledLayerAsBasemapView.swift */; };
D7201CDB2CC6B72A004BDB7D /* AddTiledLayerAsBasemapView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7201CD42CC6B710004BDB7D /* AddTiledLayerAsBasemapView.swift */; };
D7201D042CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7201D002CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift */; };
D7201D072CC6D3D3004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D7201D002CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift */; };
D7201D2B2CC6D829004BDB7D /* dodge_city.vtpk in Resources */ = {isa = PBXBuildFile; fileRef = D7201D292CC6D829004BDB7D /* dodge_city.vtpk */; settings = {ASSET_TAGS = (AddVectorTiledLayerFromCustomStyle, ); }; };
D721EEA82ABDFF550040BE46 /* LothianRiversAnno.mmpk in Resources */ = {isa = PBXBuildFile; fileRef = D721EEA72ABDFF550040BE46 /* LothianRiversAnno.mmpk */; settings = {ASSET_TAGS = (ShowMobileMapPackageExpirationDate, ); }; };
D722BD222A420DAD002C2087 /* ShowExtrudedFeaturesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D722BD212A420DAD002C2087 /* ShowExtrudedFeaturesView.swift */; };
D722BD232A420DEC002C2087 /* ShowExtrudedFeaturesView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D722BD212A420DAD002C2087 /* ShowExtrudedFeaturesView.swift */; };
Expand Down Expand Up @@ -567,6 +570,7 @@
dstPath = "";
dstSubfolderSpec = 7;
files = (
D7201D072CC6D3D3004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift in Copy Source Code Files */,
D75E5EE92CC0342700252595 /* ListContentsOfKMLFileView.swift in Copy Source Code Files */,
D7201CDB2CC6B72A004BDB7D /* AddTiledLayerAsBasemapView.swift in Copy Source Code Files */,
D7BE7E722CC19CE5006DDB0C /* AddTiledLayerView.swift in Copy Source Code Files */,
Expand Down Expand Up @@ -934,6 +938,8 @@
D71D516D2B51D7B600B2A2BE /* SearchForWebMapView.Views.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchForWebMapView.Views.swift; sourceTree = "<group>"; };
D71FCB892AD6277E000E517C /* CreateMobileGeodatabaseView.Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateMobileGeodatabaseView.Model.swift; sourceTree = "<group>"; };
D7201CD42CC6B710004BDB7D /* AddTiledLayerAsBasemapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTiledLayerAsBasemapView.swift; sourceTree = "<group>"; };
D7201D002CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddVectorTiledLayerFromCustomStyleView.swift; sourceTree = "<group>"; };
D7201D292CC6D829004BDB7D /* dodge_city.vtpk */ = {isa = PBXFileReference; lastKnownFileType = file; path = dodge_city.vtpk; sourceTree = "<group>"; };
D721EEA72ABDFF550040BE46 /* LothianRiversAnno.mmpk */ = {isa = PBXFileReference; lastKnownFileType = file; path = LothianRiversAnno.mmpk; sourceTree = "<group>"; };
D722BD212A420DAD002C2087 /* ShowExtrudedFeaturesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShowExtrudedFeaturesView.swift; sourceTree = "<group>"; };
D7232EE02AC1E5AA0079ABFF /* PlayKMLTourView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayKMLTourView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1222,6 +1228,7 @@
E066DD3E28610F3F004D3D5B /* Add scene layer from service */,
D7BE7E6E2CC19CC3006DDB0C /* Add tiled layer */,
D7201CD72CC6B710004BDB7D /* Add tiled layer as basemap */,
D7201D032CC6D3B5004BDB7D /* Add vector tiled layer from custom style */,
3E54CF202C66AFA400DD2F18 /* Add web tiled layer */,
D7E557602A1D743100B9FB09 /* Add WMS layer */,
1C3B7DC22A5F64FC00907443 /* Analyze network with subnetwork trace */,
Expand Down Expand Up @@ -1492,6 +1499,7 @@
D7C5233F2BED9BBF00E8221A /* e4a398afe9a945f3b0f4dca1e4faccb5 */,
D7F8C03C2B605AF60072BFA7 /* e12b54ea799f4606a2712157cf9f6e41 */,
D7C16D262AC5FEB600689E89 /* e87c154fb9c2487f999143df5b08e9b1 */,
D7201D2A2CC6D829004BDB7D /* f4b742a57af344988b02227e2824ca5f */,
D7497F3E2AC4BA4100167AD2 /* f5ff6f5556a945bca87ca513b8729a1e */,
);
path = "Portal Data";
Expand Down Expand Up @@ -2073,6 +2081,22 @@
path = "Add tiled layer as basemap";
sourceTree = "<group>";
};
D7201D032CC6D3B5004BDB7D /* Add vector tiled layer from custom style */ = {
isa = PBXGroup;
children = (
D7201D002CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift */,
);
path = "Add vector tiled layer from custom style";
sourceTree = "<group>";
};
D7201D2A2CC6D829004BDB7D /* f4b742a57af344988b02227e2824ca5f */ = {
isa = PBXGroup;
children = (
D7201D292CC6D829004BDB7D /* dodge_city.vtpk */,
);
path = f4b742a57af344988b02227e2824ca5f;
sourceTree = "<group>";
};
D721EEA62ABDFF550040BE46 /* 174150279af74a2ba6f8b87a567f480b */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -3115,6 +3139,7 @@
AddPointCloudLayerFromFile,
AddRasterFromFile,
AddTiledLayerAsBasemap,
AddVectorTiledLayerFromCustomStyle,
Animate3DGraphic,
AnimateImagesWithImageOverlay,
ApplyScheduledUpdatesToPreplannedMapArea,
Expand Down Expand Up @@ -3209,6 +3234,7 @@
D73571D72CB613220046A433 /* hydrography in Resources */,
D7BEBAC92CBDC81200F882E7 /* MontereyElevation.tpkx in Resources */,
D7D1F3532ADDBE5D009CE2DA /* philadelphia.mspk in Resources */,
D7201D2B2CC6D829004BDB7D /* dodge_city.vtpk in Resources */,
004A2B9D2BED455B00C297CE /* canyonlands in Resources */,
798C2DA72AFC505600EE7E97 /* PrivacyInfo.xcprivacy in Resources */,
D7E7D09A2AEB3C47003AAD02 /* san_diego_offline_routing in Resources */,
Expand Down Expand Up @@ -3364,6 +3390,7 @@
1C19B4F32A578E46001D2506 /* CreateLoadReportView.swift in Sources */,
7900C5F62A83FC3F002D430F /* AddCustomDynamicEntityDataSourceView.Vessel.swift in Sources */,
D71099702A2802FA0065A1C1 /* DensifyAndGeneralizeGeometryView.SettingsView.swift in Sources */,
D7201D042CC6D3B5004BDB7D /* AddVectorTiledLayerFromCustomStyleView.swift in Sources */,
E004A6ED2849556E002A1FE6 /* CreatePlanarAndGeodeticBuffersView.swift in Sources */,
E041ABD7287DB04D0056009B /* SampleInfoView.swift in Sources */,
D7635FFD2B9277DC0044AB97 /* ConfigureClustersView.SettingsView.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// 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 AddVectorTiledLayerFromCustomStyleView: View {
/// The view model for the sample.
@StateObject private var model = Model()

/// The viewpoint used to update the map view.
@State private var viewpoint: Viewpoint?

/// The label of the style selected by the picker.
@State private var selectedStyleLabel = "Default"

/// The error shown in the error alert.
@State private var error: Error?

var body: some View {
MapView(map: model.map, viewpoint: viewpoint)
.toolbar {
ToolbarItem(placement: .bottomBar) {
Picker("Style", selection: $selectedStyleLabel) {
Section("Online Styles") {
ForEach(model.onlineStyles, id: \.key) { label, _ in
Text(label)
}
}
Section("Offline Styles") {
ForEach(model.offlineStyles, id: \.key) { label, _ in
Text(label)
}
}
}
.task(id: selectedStyleLabel) {
// Updates the map's layer when the picker selection changes.
do {
viewpoint = try await model.setVectorTiledLayer(
label: selectedStyleLabel
)
} catch {
self.error = error
}
}
.errorAlert(presentingError: $error)
}
}
}
}

// MARK: Model

private extension AddVectorTiledLayerFromCustomStyleView {
/// The view model for the sample.
@MainActor
final class Model: ObservableObject {
/// A map with no specified style.
let map = Map()

/// The labels and portal item IDs of the online styles.
let onlineStyles: KeyValuePairs = [
"Default": "1349bfa0ed08485d8a92c442a3850b06",
"Style 1": "bd8ac41667014d98b933e97713ba8377",
"Style 2": "02f85ec376084c508b9c8e5a311724fa",
"Style 3": "1bf0cc4a4380468fbbff107e100f65a5"
]

/// The labels and portal item IDs of the offline styles.
let offlineStyles: KeyValuePairs = [
"Light": "e01262ef2a4f4d91897d9bbd3a9b1075",
"Dark": "ce8a34e5d4ca4fa193a097511daa8855"
]

/// The cached vector tiled layers keyed by the label of their associated style.
private var vectorTiledLayers: [String: ArcGISVectorTiledLayer] = [:]

/// The URL to the temporary directory for the offline style files.
private let temporaryDirectoryURL = FileManager.createTemporaryDirectory()

/// The vector tile cache for creating the offline vector tiled layers.
private let vectorTileCache = VectorTileCache(name: "dodge_city", bundle: .main)!

deinit {
// Removes all of the temporary offline style files used by sample.
try? FileManager.default.removeItem(at: temporaryDirectoryURL)
}

/// Sets the vector tiled layer for a given style on the map.
/// - Parameter label: The label of the style associated with the layer.
/// - Returns: A viewpoint framing some of the layer's data.
func setVectorTiledLayer(label: String) async throws -> Viewpoint {
// Gets or creates a vector tile layer and adds it to the map as a basemap.
let vectorTiledLayer = if let cachedVectorTiledLayer = vectorTiledLayers[label] {
cachedVectorTiledLayer
} else {
try await cacheVectorTiledLayer(label: label)
}
map.basemap = Basemap(baseLayer: vectorTiledLayer)

return if vectorTiledLayer.vectorTileCache != nil {
// Uses a Dodge City, KS viewpoint if the layer was created using the tile cache.
Viewpoint(latitude: 37.76528, longitude: -100.01766, scale: 4e4)
} else {
// Uses a Europe/Africa viewpoint if the layer was created using an online style.
Viewpoint(latitude: 28.53345, longitude: 17.56488, scale: 1e8)
}
}

/// Creates and caches a vector tiled layer for a given style.
/// - Parameter label: The label of the style associated with the layer.
/// - Returns: The cached `ArcGISVectorTiledLayer`.
private func cacheVectorTiledLayer(label: String) async throws -> ArcGISVectorTiledLayer {
let vectorTiledLayer: ArcGISVectorTiledLayer
if let onlineStyle = onlineStyles.first(where: { $0.key == label }) {
vectorTiledLayer = makeOnlineVectorTiledLayer(itemID: onlineStyle.value)
} else {
let offlineStyle = offlineStyles.first(where: { $0.key == label })!
vectorTiledLayer = try await makeOfflineVectorTiledLayer(itemID: offlineStyle.value)
}

try await vectorTiledLayer.load()
vectorTiledLayers[label] = vectorTiledLayer

return vectorTiledLayer
}

/// Creates a vector tiled layer using a portal item.
/// - Parameter itemID: The ID of the portal item.
/// - Returns: A new `ArcGISVectorTiledLayer` object.
private func makeOnlineVectorTiledLayer(itemID: String) -> ArcGISVectorTiledLayer {
let portalItem = PortalItem(
portal: .arcGISOnline(connection: .anonymous),
id: .init(itemID)!
)
return ArcGISVectorTiledLayer(item: portalItem)
}

/// Creates a vector tiled layer using a local vector tile cache and an item resource cache.
/// - Parameter itemID: The ID of the portal item used to create the export vector tiles task.
/// - Returns: A new `ArcGISVectorTiledLayer` object.
private func makeOfflineVectorTiledLayer(
itemID: String
) async throws -> ArcGISVectorTiledLayer {
// Creates a export style resource cache job using a portal item.
let portalItem = PortalItem(
portal: .arcGISOnline(connection: .anonymous),
id: .init(itemID)!
)
let exportTask = ExportVectorTilesTask(portalItem: portalItem)

let temporaryURL = temporaryDirectoryURL.appendingPathComponent(itemID)
let exportStyleResourceCacheJob = exportTask.makeExportStyleResourceCacheJob(
itemResourceCacheURL: temporaryURL
)

// Gets the item resource cache from the job and uses it to create the layer.
exportStyleResourceCacheJob.start()
let output = try await exportStyleResourceCacheJob.output

return ArcGISVectorTiledLayer(
vectorTileCache: vectorTileCache,
itemResourceCache: output.itemResourceCache
)
}
}
}

// MARK: Helper Extensions

private extension FileManager {
/// Creates a temporary directory.
/// - Returns: The URL of the created directory
static func createTemporaryDirectory() -> URL {
// swiftlint:disable:next force_try
try! FileManager.default.url(
for: .itemReplacementDirectory,
in: .userDomainMask,
appropriateFor: FileManager.default.temporaryDirectory,
create: true
)
}
}

private extension URL {
/// The URL to the local vector tile package file with Dodge City, KS data.
static var dodgeCityVectorTilePackage: URL {
Bundle.main.url(forResource: "dodge_city", withExtension: "vtpk")!
}
}
43 changes: 43 additions & 0 deletions Shared/Samples/Add vector tiled layer from custom style/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Add vector tiled layer from custom style

Load an ArcGIS vector tiled layers using custom styles.

![Screenshot of Add vector tiled layer from custom style sample with online style](add-vector-tiled-layer-from-custom-style-1.png)
![Screenshot of Add vector tiled layer from custom style sample with offline style](add-vector-tiled-layer-from-custom-style-2.png)

## Use case

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.

## How to use the sample

Pan and zoom to explore the vector tile basemap. Select a theme to see it applied to the vector tile basemap.

## How it works

1. Create an `ArcGISVectorTiledLayer` with the URL of a custom style from AcrGIS Online.
2. Alternatively, create an `ArcGISVectorTiledLayer` by taking a portal item offline and applying it to an offline vector tile package:
i. Create a `PortalItem` using the URL of a custom style.
ii. Create an `ExportVectorTilesTask` using the portal item.
iii. Get the `ExportVectorTilesJob` using `ExportVectorTilesTask.makeExportStyleResourceCacheJob(itemResourceCacheURL:)`.
iv. Start the job using `Job.start()`.
v. Create a `VectorTileCache` using the path of the local vector tile package.
vi. Once the job is complete, create an `ArcGISVectorTiledLayer` using the vector tile cache and the `ItemResourceCache` from the job's result.
3. Create a `Basemap` from the `ArcGISVectorTiledLayer`.
4. Assign the basemap to the map's `basemap`.

## Relevant API

* ArcGISVectorTiledLayer
* ExportVectorTilesTask
* ItemResourceCache
* Map
* VectorTileCache

## Offline data

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.

## Tags

tiles, vector, vector basemap, vector tile package, vector tiled layer, vector tiles, vtpk
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"category": "Visualization",
"description": "Load an ArcGIS vector tiled layers using custom styles.",
"ignore": false,
"images": [
"add-vector-tiled-layer-from-custom-style-1.png",
"add-vector-tiled-layer-from-custom-style-2.png"
],
"keywords": [
"tiles",
"vector",
"vector basemap",
"vector tile package",
"vector tiled layer",
"vector tiles",
"vtpk",
"ArcGISVectorTiledLayer",
"ExportVectorTilesTask",
"ItemResourceCache",
"Map",
"VectorTileCache"
],
"offline_data": [
"f4b742a57af344988b02227e2824ca5f"
],
"redirect_from": [],
"relevant_apis": [
"ArcGISVectorTiledLayer",
"ExportVectorTilesTask",
"ItemResourceCache",
"Map",
"VectorTileCache"
],
"snippets": [
"AddVectorTiledLayerFromCustomStyleView.swift"
],
"title": "Add vector tiled layer from custom style"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit fa08497

Please sign in to comment.