Skip to content

Commit beab5d4

Browse files
authored
Merge pull request #603 from Esri/phil2533/new-ApplyClassBreaksRendererToSublayer
[New] Apply class breaks renderer to sublayer
2 parents d7240c4 + 2364f0d commit beab5d4

File tree

5 files changed

+203
-0
lines changed

5 files changed

+203
-0
lines changed

Samples.xcodeproj/project.pbxproj

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@
118118
10BD9EB52BF51F9000ABDBD5 /* GenerateOfflineMapWithCustomParametersView.Model.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = 10BD9EB32BF51B4B00ABDBD5 /* GenerateOfflineMapWithCustomParametersView.Model.swift */; };
119119
10CFF4CA2DBAAFAC0027F144 /* AddFeatureLayerWithTimeOffsetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CFF4C92DBAAFAC0027F144 /* AddFeatureLayerWithTimeOffsetView.swift */; };
120120
10CFF5082DC1A3850027F144 /* AddFeatureLayerWithTimeOffsetView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = 10CFF4C92DBAAFAC0027F144 /* AddFeatureLayerWithTimeOffsetView.swift */; };
121+
10CFF50B2DC2EC990027F144 /* ApplyClassBreaksRendererToSublayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CFF50A2DC2EC990027F144 /* ApplyClassBreaksRendererToSublayerView.swift */; };
122+
10CFF54C2DCD4DFA0027F144 /* ApplyClassBreaksRendererToSublayerView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = 10CFF50A2DC2EC990027F144 /* ApplyClassBreaksRendererToSublayerView.swift */; };
121123
10D321932BDB187400B39B1B /* naperville_imagery.tpkx in Resources */ = {isa = PBXBuildFile; fileRef = 10D321922BDB187400B39B1B /* naperville_imagery.tpkx */; settings = {ASSET_TAGS = (GenerateOfflineMapWithLocalBasemap, ); }; };
122124
10D321962BDB1CB500B39B1B /* GenerateOfflineMapWithLocalBasemapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10D321952BDB1CB500B39B1B /* GenerateOfflineMapWithLocalBasemapView.swift */; };
123125
10D321972BDC3B4900B39B1B /* GenerateOfflineMapWithLocalBasemapView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = 10D321952BDB1CB500B39B1B /* GenerateOfflineMapWithLocalBasemapView.swift */; };
@@ -668,6 +670,7 @@
668670
4C8127682DCD4F18006EF7D2 /* ApplyStretchRendererView.swift in Copy Source Code Files */,
669671
00FA4E5F2DC568DF008A34CF /* AddRastersAndFeatureTablesFromGeopackageView.swift in Copy Source Code Files */,
670672
0072C7FB2DBAC1A0001502CA /* AddIntegratedMeshLayerView.swift in Copy Source Code Files */,
673+
10CFF54C2DCD4DFA0027F144 /* ApplyClassBreaksRendererToSublayerView.swift in Copy Source Code Files */,
671674
1C38915E2DBC3EDC00ADFDDC /* AddWFSLayerView.swift in Copy Source Code Files */,
672675
1C29C95A2DBAE5770074028F /* AddWMTSLayerView.swift in Copy Source Code Files */,
673676
004421912DB96A7800249FEE /* AddFeatureCollectionLayerFromQueryView.swift in Copy Source Code Files */,
@@ -977,6 +980,7 @@
977980
10B782072BE5A058007EAE6C /* GenerateOfflineMapWithCustomParametersView.CustomParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerateOfflineMapWithCustomParametersView.CustomParameters.swift; sourceTree = "<group>"; };
978981
10BD9EB32BF51B4B00ABDBD5 /* GenerateOfflineMapWithCustomParametersView.Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerateOfflineMapWithCustomParametersView.Model.swift; sourceTree = "<group>"; };
979982
10CFF4C92DBAAFAC0027F144 /* AddFeatureLayerWithTimeOffsetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFeatureLayerWithTimeOffsetView.swift; sourceTree = "<group>"; };
983+
10CFF50A2DC2EC990027F144 /* ApplyClassBreaksRendererToSublayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplyClassBreaksRendererToSublayerView.swift; sourceTree = "<group>"; };
980984
10D321922BDB187400B39B1B /* naperville_imagery.tpkx */ = {isa = PBXFileReference; lastKnownFileType = file; path = naperville_imagery.tpkx; sourceTree = "<group>"; };
981985
10D321952BDB1CB500B39B1B /* GenerateOfflineMapWithLocalBasemapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerateOfflineMapWithLocalBasemapView.swift; sourceTree = "<group>"; };
982986
1C0C1C3429D34DAE005C8B24 /* ChangeViewpointView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangeViewpointView.swift; sourceTree = "<group>"; };
@@ -1440,6 +1444,7 @@
14401444
D7C16D172AC5F6C100689E89 /* Animate 3D graphic */,
14411445
D77570BC2A29427200F490CD /* Animate images with image overlay */,
14421446
1C38915F2DC02F0800ADFDDC /* Apply blend renderer to hillshade */,
1447+
10CFF5092DC2EC3E0027F144 /* Apply class breaks renderer to sublayer */,
14431448
4C81273A2DCA9E03006EF7D2 /* Apply colormap renderer to raster */,
14441449
D7F2A02C2CD00F1C0008D981 /* Apply dictionary renderer to feature layer */,
14451450
D70789912CD160FD000DF215 /* Apply dictionary renderer to graphics overlay */,
@@ -1892,6 +1897,14 @@
18921897
path = "Add feature layer with time offset";
18931898
sourceTree = "<group>";
18941899
};
1900+
10CFF5092DC2EC3E0027F144 /* Apply class breaks renderer to sublayer */ = {
1901+
isa = PBXGroup;
1902+
children = (
1903+
10CFF50A2DC2EC990027F144 /* ApplyClassBreaksRendererToSublayerView.swift */,
1904+
);
1905+
path = "Apply class breaks renderer to sublayer";
1906+
sourceTree = "<group>";
1907+
};
18951908
10D321912BDB187400B39B1B /* 85282f2aaa2844d8935cdb8722e22a93 */ = {
18961909
isa = PBXGroup;
18971910
children = (
@@ -4132,6 +4145,7 @@
41324145
E066DD4028610F55004D3D5B /* AddSceneLayerFromServiceView.swift in Sources */,
41334146
D7F8C0392B60564D0072BFA7 /* AddFeaturesWithContingentValuesView.swift in Sources */,
41344147
00F279D62AF418DC00CECAF8 /* AddDynamicEntityLayerView.VehicleCallout.swift in Sources */,
4148+
10CFF50B2DC2EC990027F144 /* ApplyClassBreaksRendererToSublayerView.swift in Sources */,
41354149
D7749AD62AF08BF50086632F /* FindRouteInTransportNetworkView.Model.swift in Sources */,
41364150
D73F06692B5EE73D000B574F /* QueryFeaturesWithArcadeExpressionView.swift in Sources */,
41374151
D7464F1E2ACE04B3007FEE88 /* IdentifyRasterCellView.swift in Sources */,
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright 2025 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 ApplyClassBreaksRendererToSublayerView: View {
19+
/// The map image layer.
20+
@State private var mapImageLayer = ArcGISMapImageLayer(url: URL(string: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer")!)
21+
22+
/// The counties sublayer.
23+
@State private var countiesSublayer: ArcGISMapImageSublayer?
24+
25+
/// The original renderer used to symbolize the sublayer.
26+
@State private var originalRenderer: Renderer?
27+
28+
/// A Boolean value indicating that the class breaks renderer is applied.
29+
@State private var classBreaksRendererIsApplied = false
30+
31+
/// The map with a topographic basemap style.
32+
@State private var map: Map = {
33+
let map = Map(basemapStyle: .arcGISTopographic)
34+
35+
// Sets the initial viewpoint.
36+
map.initialViewpoint = Viewpoint(
37+
boundingGeometry: Envelope(
38+
xMin: -13934661.666904,
39+
yMin: 331181.323482,
40+
xMax: -7355704.998713,
41+
yMax: 9118038.075882,
42+
spatialReference: .webMercator
43+
)
44+
)
45+
return map
46+
}()
47+
48+
/// The error shown in the error alert.
49+
@State private var error: Error?
50+
51+
var body: some View {
52+
MapView(map: map)
53+
.task {
54+
// Adds the map image layer to the map.
55+
map.addOperationalLayer(mapImageLayer)
56+
do {
57+
// Loads the map image layer.
58+
try await mapImageLayer.load()
59+
// Gets the sublayers.
60+
let mapImageSublayers = mapImageLayer.mapImageSublayers
61+
// Gets the third sublayer.
62+
let sublayer = mapImageSublayers[2]
63+
// Loads the sublayer.
64+
try await sublayer.load()
65+
countiesSublayer = sublayer
66+
originalRenderer = sublayer.renderer
67+
} catch {
68+
self.error = error
69+
}
70+
}
71+
.toolbar {
72+
ToolbarItem(placement: .bottomBar) {
73+
Toggle("Change Sublayer Renderer", isOn: $classBreaksRendererIsApplied)
74+
.disabled(countiesSublayer == nil)
75+
}
76+
}
77+
.onChange(of: classBreaksRendererIsApplied) {
78+
countiesSublayer?.renderer = if classBreaksRendererIsApplied {
79+
// Applies the class breaks renderer.
80+
.populationRenderer
81+
} else {
82+
// Applies the original renderer.
83+
originalRenderer
84+
}
85+
}
86+
.errorAlert(presentingError: $error)
87+
}
88+
}
89+
90+
private extension Renderer {
91+
/// Creates a class breaks renderer for counties in the US based on their
92+
/// population in 2007.
93+
static var populationRenderer: ClassBreaksRenderer {
94+
// Outline symbol.
95+
let lineSymbol = SimpleLineSymbol(style: .solid, color: UIColor(white: 0.6, alpha: 1), width: 0.5)
96+
97+
// Symbol for each class break.
98+
let symbol1 = SimpleFillSymbol(style: .solid, color: UIColor(red: 0.89, green: 0.92, blue: 0.81, alpha: 1), outline: lineSymbol)
99+
let symbol2 = SimpleFillSymbol(style: .solid, color: UIColor(red: 0.59, green: 0.76, blue: 0.75, alpha: 1), outline: lineSymbol)
100+
let symbol3 = SimpleFillSymbol(style: .solid, color: UIColor(red: 0.38, green: 0.65, blue: 0.71, alpha: 1), outline: lineSymbol)
101+
let symbol4 = SimpleFillSymbol(style: .solid, color: UIColor(red: 0.27, green: 0.49, blue: 0.59, alpha: 1), outline: lineSymbol)
102+
let symbol5 = SimpleFillSymbol(style: .solid, color: UIColor(red: 0.16, green: 0.33, blue: 0.47, alpha: 1), outline: lineSymbol)
103+
104+
// Class breaks.
105+
let classBreak1 = ClassBreak(description: "-99 to 8,560", label: "-99 to 8,560", minValue: -99, maxValue: 8_560, symbol: symbol1)
106+
let classBreak2 = ClassBreak(description: "> 8,560 to 18,109", label: "> 8,560 to 18,109", minValue: 8_561, maxValue: 18_109, symbol: symbol2)
107+
let classBreak3 = ClassBreak(description: "> 18,109 to 35,501", label: "> 18,109 to 35,501", minValue: 18_110, maxValue: 35_501, symbol: symbol3)
108+
let classBreak4 = ClassBreak(description: "> 35,501 to 86,100", label: "> 35,501 to 86,100", minValue: 35_502, maxValue: 86_100, symbol: symbol4)
109+
let classBreak5 = ClassBreak(description: "> 86,100 to 10,110,975", label: "> 86,100 to 10,110,975", minValue: 86_101, maxValue: 10_110_975, symbol: symbol5)
110+
111+
return ClassBreaksRenderer(fieldName: "POP2007", classBreaks: [classBreak1, classBreak2, classBreak3, classBreak4, classBreak5])
112+
}
113+
}
114+
115+
#Preview {
116+
ApplyClassBreaksRendererToSublayerView()
117+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Apply class breaks renderer to sublayer
2+
3+
Apply a renderer to a sublayer.
4+
5+
![Image of Apply class breaks renderer to sublayer sample](apply-class-breaks-renderer-to-sublayer.png)
6+
7+
## Use case
8+
9+
A layer showing animal populations contains sublayers for different species. A renderer could be applied which gives each sublayer a different color, so that populations of each species can be compared visually.
10+
11+
## How to use the sample
12+
13+
Wait for the map image layer to load. Tap the 'Change Sublayer Renderer' button to apply a unique value renderer to see different population ranges in the counties sub-layer data.
14+
15+
## How it works
16+
17+
1. Create an `ArcGISMapImageLayer` from its URL.
18+
2. After it is done loading, get its map image sublayers.
19+
3. Get the `MapImageSublayer` you want.
20+
4. Create a `ClassBreaksRenderer` with a collection of `ClassBreak`s for different population ranges.
21+
5. Set class breaks renderer as the renderer of the sublayer.
22+
23+
## Relevant API
24+
25+
* ArcGISMapImageLayer
26+
* ArcGISMapImageSubLayer
27+
* ClassBreak
28+
* ClassBreaksRenderer
29+
30+
## About the data
31+
32+
This application displays census data from an ArcGIS Server map service. It contains various population statistics, including total population for each county in 2007.
33+
34+
## Additional information
35+
36+
The service hosting the layer must support dynamic layers to be able to change the rendering of sublayers.
37+
38+
## Tags
39+
40+
class breaks, dynamic layer, dynamic rendering, renderer, sublayer, symbology, visualization
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"category": "Visualization",
3+
"description": "Apply a renderer to a sublayer.",
4+
"ignore": false,
5+
"images": [
6+
"apply-class-breaks-renderer-to-sublayer.png"
7+
],
8+
"keywords": [
9+
"class breaks",
10+
"dynamic layer",
11+
"dynamic rendering",
12+
"renderer",
13+
"sublayer",
14+
"symbology",
15+
"visualization",
16+
"ArcGISMapImageLayer",
17+
"ArcGISMapImageSubLayer",
18+
"ClassBreak",
19+
"ClassBreaksRenderer"
20+
],
21+
"redirect_from": [],
22+
"relevant_apis": [
23+
"ArcGISMapImageLayer",
24+
"ArcGISMapImageSubLayer",
25+
"ClassBreak",
26+
"ClassBreaksRenderer"
27+
],
28+
"snippets": [
29+
"ApplyClassBreaksRendererToSublayerView.swift"
30+
],
31+
"title": "Apply class breaks renderer to sublayer"
32+
}
220 KB
Loading

0 commit comments

Comments
 (0)