Skip to content

Commit

Permalink
Merge pull request #205 from Esri/des12437/set-visibility-sample
Browse files Browse the repository at this point in the history
[New] Set visibility of subtype feature layer
  • Loading branch information
des12437 authored Jul 6, 2023
2 parents ede53ae + 8cc7452 commit 9a53600
Show file tree
Hide file tree
Showing 7 changed files with 398 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 @@ -70,6 +70,12 @@
1C0C1C3D29D34DDD005C8B24 /* ChangeViewpointView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = 1C0C1C3429D34DAE005C8B24 /* ChangeViewpointView.swift */; };
1C42E04729D2396B004FC4BE /* ShowPopupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C42E04329D2396B004FC4BE /* ShowPopupView.swift */; };
1C42E04A29D239D2004FC4BE /* ShowPopupView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = 1C42E04329D2396B004FC4BE /* ShowPopupView.swift */; };
1C43BC7F2A43781200509BF8 /* SetVisibilityOfSubtypeSublayerView.Views.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C43BC792A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.Views.swift */; };
1C43BC822A43781200509BF8 /* SetVisibilityOfSubtypeSublayerView.Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C43BC7C2A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.Model.swift */; };
1C43BC842A43781200509BF8 /* SetVisibilityOfSubtypeSublayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C43BC7E2A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.swift */; };
1C43BC852A43783900509BF8 /* SetVisibilityOfSubtypeSublayerView.Model.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = 1C43BC7C2A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.Model.swift */; };
1C43BC862A43783900509BF8 /* SetVisibilityOfSubtypeSublayerView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = 1C43BC7E2A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.swift */; };
1C43BC872A43783900509BF8 /* SetVisibilityOfSubtypeSublayerView.Views.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = 1C43BC792A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.Views.swift */; };
1C929F092A27B86800134252 /* ShowUtilityAssociationsView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = 1CAF831B2A20305F000E1E60 /* ShowUtilityAssociationsView.swift */; };
1C965C3929DB9176002F8536 /* ShowRealisticLightAndShadowsView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = 1C9B74C529DB43580038B06F /* ShowRealisticLightAndShadowsView.swift */; };
1C9B74C929DB43580038B06F /* ShowRealisticLightAndShadowsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C9B74C529DB43580038B06F /* ShowRealisticLightAndShadowsView.swift */; };
Expand Down Expand Up @@ -251,6 +257,9 @@
D7ABA2FA2A32760D0021822B /* MeasureDistanceInSceneView.swift in Copy Source Code Files */,
D722BD232A420DEC002C2087 /* ShowExtrudedFeaturesView.swift in Copy Source Code Files */,
D752D9602A3BCE63003EB25E /* DisplayMapFromPortalItemView.swift in Copy Source Code Files */,
1C43BC852A43783900509BF8 /* SetVisibilityOfSubtypeSublayerView.Model.swift in Copy Source Code Files */,
1C43BC862A43783900509BF8 /* SetVisibilityOfSubtypeSublayerView.swift in Copy Source Code Files */,
1C43BC872A43783900509BF8 /* SetVisibilityOfSubtypeSublayerView.Views.swift in Copy Source Code Files */,
00EB803A2A31506F00AC2B07 /* DisplayContentOfUtilityNetworkContainerView.swift in Copy Source Code Files */,
00EB803B2A31506F00AC2B07 /* DisplayContentOfUtilityNetworkContainerView.Model.swift in Copy Source Code Files */,
D751018F2A2E966C00B8FA48 /* IdentifyLayerFeaturesView.swift in Copy Source Code Files */,
Expand Down Expand Up @@ -368,6 +377,9 @@
108EC04029D25B2C000F35D0 /* QueryFeatureTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryFeatureTableView.swift; sourceTree = "<group>"; };
1C0C1C3429D34DAE005C8B24 /* ChangeViewpointView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangeViewpointView.swift; sourceTree = "<group>"; };
1C42E04329D2396B004FC4BE /* ShowPopupView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShowPopupView.swift; sourceTree = "<group>"; };
1C43BC792A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.Views.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetVisibilityOfSubtypeSublayerView.Views.swift; sourceTree = "<group>"; };
1C43BC7C2A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetVisibilityOfSubtypeSublayerView.Model.swift; sourceTree = "<group>"; };
1C43BC7E2A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetVisibilityOfSubtypeSublayerView.swift; sourceTree = "<group>"; };
1C9B74C529DB43580038B06F /* ShowRealisticLightAndShadowsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShowRealisticLightAndShadowsView.swift; sourceTree = "<group>"; };
1C9B74D529DB54560038B06F /* ChangeCameraControllerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangeCameraControllerView.swift; sourceTree = "<group>"; };
1CAF831B2A20305F000E1E60 /* ShowUtilityAssociationsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShowUtilityAssociationsView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -572,6 +584,7 @@
D7EAF34F2A1C011000D822C4 /* Set min and max scale */,
E088E1552862578800413100 /* Set surface placement mode */,
E004A6B928414332002A1FE6 /* Set viewpoint rotation */,
1C43BC782A43781100509BF8 /* Set visibility of subtype sublayer */,
E004A6DE2846626A002A1FE6 /* Show callout */,
D7EF5D712A269E2D00FEBDE5 /* Show coordinates in multiple formats */,
E004A6E728493BBB002A1FE6 /* Show device location */,
Expand Down Expand Up @@ -773,6 +786,16 @@
path = "Show popup";
sourceTree = "<group>";
};
1C43BC782A43781100509BF8 /* Set visibility of subtype sublayer */ = {
isa = PBXGroup;
children = (
1C43BC7C2A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.Model.swift */,
1C43BC7E2A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.swift */,
1C43BC792A43781100509BF8 /* SetVisibilityOfSubtypeSublayerView.Views.swift */,
);
path = "Set visibility of subtype sublayer";
sourceTree = "<group>";
};
1C965C4629DBA879002F8536 /* 681d6f7694644709a7c830ec57a2d72b */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1420,6 +1443,7 @@
4D2ADC6729C50BD6003B367F /* AddDynamicEntityLayerView.Model.swift in Sources */,
E004A6E928493BCE002A1FE6 /* ShowDeviceLocationView.swift in Sources */,
F111CCC1288B5D5600205358 /* DisplayMapFromMobileMapPackageView.swift in Sources */,
1C43BC842A43781200509BF8 /* SetVisibilityOfSubtypeSublayerView.swift in Sources */,
00A7A1462A2FC58300F035F7 /* DisplayContentOfUtilityNetworkContainerView.swift in Sources */,
218F35B829C28F4A00502022 /* AuthenticateWithOAuthView.swift in Sources */,
79B7B80A2A1BF8EC00F57C27 /* CreateAndSaveKMLView.swift in Sources */,
Expand All @@ -1436,11 +1460,13 @@
D71099702A2802FA0065A1C1 /* DensifyAndGeneralizeGeometryView.SettingsView.swift in Sources */,
E004A6ED2849556E002A1FE6 /* CreatePlanarAndGeodeticBuffersView.swift in Sources */,
E041ABD7287DB04D0056009B /* SampleInfoView.swift in Sources */,
1C43BC822A43781200509BF8 /* SetVisibilityOfSubtypeSublayerView.Model.swift in Sources */,
D752D9462A3A6F80003EB25E /* MonitorChangesToMapLoadStatusView.swift in Sources */,
E0082217287755AC002AD138 /* View+Sheet.swift in Sources */,
00181B462846AD7100654571 /* View+Alert.swift in Sources */,
E0FE32E728747778002C6ACA /* BrowseBuildingFloorsView.swift in Sources */,
D752D95F2A3BCE06003EB25E /* DisplayMapFromPortalItemView.swift in Sources */,
1CAB8D072A3CCBD8002AA649 /* SetVisibilityOfSubtypeFeatureLayerView.Views.swift in Sources */,
E070A0A3286F3B6000F2B606 /* DownloadPreplannedMapAreaView.swift in Sources */,
D77570C02A2942F800F490CD /* AnimateImagesWithImageOverlayView.swift in Sources */,
E0EA0B772866390E00C9621D /* ProjectGeometryView.swift in Sources */,
Expand Down Expand Up @@ -1506,6 +1532,7 @@
D78666AD2A2161F100C60110 /* FindNearestVertexView.swift in Sources */,
D744FD172A2112D90084A66C /* CreateConvexHullAroundPointsView.swift in Sources */,
00CB9138284814A4005C2C5D /* SearchWithGeocodeView.swift in Sources */,
1C43BC7F2A43781200509BF8 /* SetVisibilityOfSubtypeSublayerView.Views.swift in Sources */,
D754E3232A1D66820006C5F1 /* StylePointWithPictureMarkerSymbolsView.swift in Sources */,
00E5401C27F3CCA200CF66D5 /* SamplesApp.swift in Sources */,
E066DD4028610F55004D3D5B /* AddSceneLayerFromServiceView.swift in Sources */,
Expand Down
44 changes: 44 additions & 0 deletions Shared/Samples/Set visibility of subtype sublayer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Set visibility of subtype sublayer

Display a composite layer of all the subtype values in a feature class.

![Image of Set visibility of subtype sublayer sample](set-visibility-of-subtype-sublayer.png)

## Use case

This is useful for controlling labeling, visibility, and symbology of a given subtype as though they are distinct layers on the map.

## How to use the sample

The sample loads with the sublayer visible on the map. Toggle its visibility by tapping the first switch. Toggle between the sublayer's original renderer and an alternate renderer using the second switch. Tap the
"Set Current to Minimum Scale" button to set the sublayer's minimum scale to the current map scale.

## How it works

1. Create a `SubtypeFeatureLayer` from a `ServiceFeatureTable` that defines a subtype, and add it to the `Map`.
2. Get a `SubtypeSublayer` from the subtype feature layer using its name.
3. Enable the sublayer's labels and define them with `LabelDefinition`.
* Use `SimpleLabelExpression` to set the expression for label definitions.
4. Make a switch to toggle the sublayer's visibility.
5. Create an alternate renderer by making a `SimpleRenderer`.
6. Get the current map scale and make it the minimum map scale.

## Relevant API

* LabelDefinition
* ServiceFeatureTable
* SimpleLabelExpression
* SubtypeFeatureLayer
* SubtypeSublayer

## About the data

The [feature service layer](https://sampleserver7.arcgisonline.com/server/rest/services/UtilityNetwork/NapervilleElectric/FeatureServer/0) in this sample represents an electric network in Naperville, Illinois, which contains a utility network with asset classification for different devices.

## Additional information

Help regarding the Arcade label expression script for defining a label definition can be found on the [ArcGIS Developers](https://developers.arcgis.com/arcade/) site.

## Tags

asset group, feature layer, labeling, sublayer, subtype, symbology, utility network, visible scale range
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"category": "Layers",
"description": "Display a composite layer of all the subtype values in a feature class.",
"ignore": false,
"images": [
"set-visibility-of-subtype-sublayer.png"
],
"keywords": [
"asset group",
"feature layer",
"labeling",
"sublayer",
"subtype",
"symbology",
"utility network",
"visible scale range",
"LabelDefinition",
"ServiceFeatureTable",
"SimpleLabelExpression",
"SubtypeFeatureLayer",
"SubtypeSublayer"
],
"redirect_from": [],
"relevant_apis": [
"LabelDefinition",
"ServiceFeatureTable",
"SimpleLabelExpression",
"SubtypeFeatureLayer",
"SubtypeSublayer"
],
"snippets": [
"SetVisibilityOfSubtypeSublayerView.swift",
"SetVisibilityOfSubtypeSublayerView.Model.swift",
"SetVisibilityOfSubtypeSublayerView.Views.swift"
],
"title": "Set visibility of subtype sublayer"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright 2023 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 SwiftUI
import ArcGIS

extension SetVisibilityOfSubtypeSublayerView {
/// The view model for this sample.
@MainActor
class Model: ObservableObject {
/// A map with a streets basemap style.
let map = Map(basemapStyle: .arcGISStreetsNight)

/// A feature table for the electrical network in this sample.
private let featureTable = ServiceFeatureTable(url: .featureServiceURL)

/// A subtype feature layer created from the service feature table.
private let subtypeFeatureLayer: SubtypeFeatureLayer

/// The subtype sublayer of the subtype feature layer in this sample.
private var subtypeSublayer: SubtypeSublayer?

/// The renderer of the subtype feature layer.
private var originalRenderer: Renderer?

/// The subtype sublayer's label definition.
private let labelDefinition: LabelDefinition = {
// Make and stylize the text symbol.
let textSymbol = TextSymbol(color: .blue, size: 10.5)
textSymbol.backgroundColor = .clear
textSymbol.outlineColor = .white
textSymbol.haloColor = .white
textSymbol.haloWidth = 2
// Make a label definition and adjust its properties.
let labelExpression = SimpleLabelExpression(simpleExpression: "[nominalvoltage]")
let labelDefinition = LabelDefinition(labelExpression: labelExpression, textSymbol: textSymbol)
labelDefinition.placement = .pointAboveRight
labelDefinition.usesCodedValues = true
return labelDefinition
}()

/// The current scale of the map.
var currentScale: Double = .zero

/// The map's current scale value in text.
@Published var currentScaleText: String = ""

/// The subtype sublayer's minimum scale value in text.
@Published private(set) var minimumScaleText: String = "Not Set"

init() {
map.initialViewpoint = .initialViewpoint
subtypeFeatureLayer = SubtypeFeatureLayer(featureTable: featureTable)
subtypeFeatureLayer.scalesSymbols = false
}

deinit {
ArcGISEnvironment.authenticationManager.arcGISCredentialStore.removeAll()
}

/// Performs important tasks including adding credentials, loading and adding operational layers.
func setup() async throws {
try await ArcGISEnvironment.authenticationManager.arcGISCredentialStore.add(.publicSample)
try await subtypeFeatureLayer.load()
map.addOperationalLayer(subtypeFeatureLayer)
guard let subtypeSublayer = subtypeFeatureLayer.sublayer(withSubtypeName: "Street Light") else {
throw SetupError.cannotFindSublayer
}
subtypeSublayer.labelsAreEnabled = true
originalRenderer = subtypeSublayer.renderer
subtypeSublayer.addLabelDefinition(labelDefinition)
self.subtypeSublayer = subtypeSublayer
}

func toggleSublayer(isVisible: Bool) {
subtypeSublayer?.isVisible = isVisible
}

func toggleRenderer(showsOriginalRenderer: Bool) {
if showsOriginalRenderer {
subtypeSublayer?.renderer = originalRenderer
} else {
let symbol = SimpleMarkerSymbol(style: .diamond, color: .systemPink, size: 20)
let alternativeRenderer = SimpleRenderer(symbol: symbol)
subtypeSublayer?.renderer = alternativeRenderer
}
}

func formatCurrentScaleText() {
currentScaleText = "1:\(currentScale.formatted(.decimal))"
}

func setMinimumScale() {
minimumScaleText = currentScaleText
subtypeSublayer?.minScale = currentScale
}
}
}

extension SetVisibilityOfSubtypeSublayerView.Model {
enum SetupError: LocalizedError {
case cannotFindSublayer

var errorDescription: String? {
switch self {
case .cannotFindSublayer:
return NSLocalizedString(
"Cannot find subtype sublayer.",
comment: "Error thrown when subtype sublayer cannot be found."
)
}
}
}
}

private extension ArcGISCredential {
/// The public credentials for the data in this sample.
/// - Note: Never hardcode login information in a production application. This is done solely
/// for the sake of the sample.
static var publicSample: ArcGISCredential {
get async throws {
try await TokenCredential.credential(
for: URL.featureServiceURL,
username: "viewer01",
password: "I68VGU^nMurF"
)
}
}
}

private extension FormatStyle where Self == FloatingPointFormatStyle<Double> {
/// Formats the double with zero decimals places of precision.
static var decimal: Self {
Self.number.precision(.fractionLength(0))
}
}

private extension URL {
static var featureServiceURL: URL {
URL(string: "https://sampleserver7.arcgisonline.com/server/rest/services/UtilityNetwork/NapervilleElectric/FeatureServer/0")!
}
}

private extension Viewpoint {
/// The initial viewpoint to be displayed when the sample is first opened.
static var initialViewpoint: Viewpoint {
.init(
boundingGeometry: Envelope(
xRange: (-9812691.11079696)...(-9812377.9447607),
yRange: (5128687.20710657)...(5128865.36767282),
spatialReference: .webMercator
)
)
}
}
Loading

0 comments on commit 9a53600

Please sign in to comment.