Display a composite layer of all the subtype values in a feature class.
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
- Create an
AGSSubtypeFeatureLayer
from anAGSServiceFeatureTable
that defines a subtype, and add it to theAGSMap
. - Get an
AGSSubtypeSublayer
from the subtype feature layer using its name. - Enable the sublayer's labels and define them with
AGSLabelDefinition
.- Use
AGSSimpleLabelExpression
to set the expression for label definitions.
- Use
- Make a switch to toggle the sublayer's visibility.
- Create an alternate renderer by making an
AGSSimpleRenderer
. - Get the current map scale and make it the minimum map scale.
Relevant API
- AGSLabelDefinition
- AGSServiceFeatureTable
- AGSSimpleLabelExpression
- AGSSubtypeFeatureLayer
- AGSSubtypeSublayer
About the data
The feature service layer 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 site.
Tags
asset group, feature layer, labeling, sublayer, subtype, symbology, utility network, visible scale range
Sample Code
// Copyright 2020 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
//
// http://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 UIKit
import ArcGIS
class DisplaySubtypeFeatureLayerViewController: UIViewController {
// The map view managed by the view controller.
@IBOutlet private weak var mapView: AGSMapView! {
didSet {
AGSAuthenticationManager.shared().delegate = self
mapView.map = makeMap()
mapView.setViewpoint(AGSViewpoint(targetExtent: AGSEnvelope(xMin: -9812691.11079696, yMin: 5128687.20710657, xMax: -9812377.9447607, yMax: 5128865.36767282, spatialReference: .webMercator())))
}
}
@IBOutlet var settingsButton: UIBarButtonItem!
@IBOutlet var currentScaleLabel: UILabel!
var subtypeSublayer: AGSSubtypeSublayer!
var originalRenderer: AGSRenderer!
// The observation of the map view's map scale.
private var mapScaleObservation: NSKeyValueObservation?
var subtypeFeatureLayer: AGSSubtypeFeatureLayer? {
didSet {
subtypeFeatureLayer?.load { [weak self] (error) in
guard let self = self else { return }
if let error = error {
self.presentAlert(error: error)
} else if let layer = self.subtypeFeatureLayer {
self.subtypeSublayer = layer.sublayer(withName: "Street Light")
self.originalRenderer = self.subtypeSublayer?.renderer
self.subtypeSublayer?.labelsEnabled = true
self.settingsButton.isEnabled = true
// Make and add the labels.
let label = self.makeLabelDefinition()
self.subtypeSublayer?.labelDefinitions.append(label)
}
}
}
}
func makeMap() -> AGSMap {
let map = AGSMap(basemapStyle: .arcGISStreetsNight)
// Create a subtype feature layer from a service feature table.
let featureServiceURL = URL(string: "https://sampleserver7.arcgisonline.com/server/rest/services/UtilityNetwork/NapervilleElectric/FeatureServer/0")
let featureTable = AGSServiceFeatureTable(url: featureServiceURL!)
subtypeFeatureLayer = AGSSubtypeFeatureLayer(featureTable: featureTable)
subtypeFeatureLayer?.scaleSymbols = false
map.operationalLayers.add(subtypeFeatureLayer!)
return map
}
private func makeLabelDefinition() -> AGSLabelDefinition {
// Make and stylize the text symbol.
let textSymbol = AGSTextSymbol()
textSymbol.backgroundColor = .clear
textSymbol.outlineColor = .white
textSymbol.color = .blue
textSymbol.haloColor = .white
textSymbol.haloWidth = 2
textSymbol.size = 10.5
// Make a label definition and adjust its properties.
let labelDefinition = AGSLabelDefinition()
labelDefinition.expression = AGSSimpleLabelExpression(simpleExpression: "[nominalvoltage]")
labelDefinition.placement = .pointAboveRight
labelDefinition.useCodedValues = true
labelDefinition.textSymbol = textSymbol
return labelDefinition
}
// The formatter used to generate strings from scale values.
private let scaleFormatter: NumberFormatter = {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.maximumFractionDigits = 0
return numberFormatter
}()
// Called in response to the map view's map scale changing.
func mapScaleDidChange() {
// Update the text of the Current Map Scale label.
let mapScale = mapView.mapScale
let updatedText = String(format: "1:%@", scaleFormatter.string(from: mapScale as NSNumber)!)
currentScaleLabel.text = "Current scale: " + updatedText
}
override func viewDidLoad() {
super.viewDidLoad()
mapScaleObservation = mapView.observe(\.mapScale, options: .initial) { [weak self] (_, _) in
DispatchQueue.main.async { self?.mapScaleDidChange() }
}
// Add the source code button item to the right of navigation bar.
(self.navigationItem.rightBarButtonItem as! SourceCodeBarButtonItem).filenames = ["DisplaySubtypeFeatureLayerViewController", "DisplaySubtypeSettingsViewController"]
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let navController = segue.destination as? UINavigationController,
let controller = navController.topViewController as? DisplaySubtypeSettingsViewController {
controller.map = mapView?.map
controller.mapScale = mapView.mapScale
controller.minScale = subtypeSublayer.minScale
controller.subtypeSublayer = subtypeSublayer
controller.originalRenderer = self.originalRenderer
navController.presentationController?.delegate = self
}
}
}
// MARK: - UIAdaptivePresentationControllerDelegate
extension DisplaySubtypeFeatureLayerViewController: UIAdaptivePresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
// Ensure that the settings show in a popover even on small displays.
return .none
}
}
extension DisplaySubtypeFeatureLayerViewController: AGSAuthenticationManagerDelegate {
func authenticationManager(_ authenticationManager: AGSAuthenticationManager, didReceive challenge: AGSAuthenticationChallenge) {
// NOTE: Never hardcode login information in a production application. This is done solely for the sake of the sample.
let credentials = AGSCredential(user: "viewer01", password: "I68VGU^nMurF")
challenge.continue(with: credentials)
}
}