Apply unique values with alternate symbols

View on GitHub
Sample viewer app

Apply a unique value with alternate symbols at different scales.

ApplyUniqueValuesWithAlternateSymbols

Use case

When a layer is symbolized with unique value symbology, you can specify the visible scale range for each unique value. This is an effective strategy to limit the amount of detailed data at smaller scales without having to make multiple versions of the layer, each with a unique definition query.

Once scale ranges are applied to unique values, you can further refine the appearance of features within those scale ranges by establishing alternate symbols to different parts of the symbol class scale range.

How to use the sample

Zoom in and out of the map to see alternate symbols at each scale. The symbology changes according to the following scale ranges: 0-5000, 5000-10000, 10000-20000. To go back to the initial viewpoint, tap "Reset Viewpoint".

How it works

  1. Create a FeatureLayer using the service url and add it to the map's list of operational layers.

  2. Create two alternate symbols (a blue square and a yellow diamond) to be used as alternate symbols. To create an alternate symbol:

    a. Create a symbol using SimpleMarkerSymbol.

    b. Convert the simple marker symbol to a MultilayerPointSymbol using SimpleMarkerSymbol.toMultilayerSymbol().

    c. Set the valid scale range through reference properties on the multilayer point symbols blue square and yellow diamond by calling multilayerPointSymbol.referenceProperties = SymbolReferenceProperties(double minScale, double maxScale).

  3. Create a third multilayer symbol to be used to create a UniqueValue class.

  4. Create a unique value using the red triangle and the list of alternate symbols.

  5. Create a UniqueValueRenderer and add the unique value.

  6. Create a purple diamond simple marker and convert it to a multilayer symbol to be used as the default symbol.

  7. Set the default symbol on the unique value renderer to the purple diamond using uniqueValueRenderer.defaultSymbol.

  8. Set the uniqueValueRenderer.fieldNames on the unique value renderer to "req_type".

  9. Apply this unique value renderer to the featureLayer.renderer.

Relevant API

  • MultilayerPointSymbol
  • SimpleMarkerSymbol
  • SymbolReferenceProperties
  • UniqueValue
  • UniqueValueRenderer

About the data

The San Francisco 311 incidents layer in this sample displays point features related to crime incidents such as grafitti and tree damage that have been reported by city residents.

Tags

alternate symbols, multilayer symbol, scale based rendering, simple marker symbol, symbol reference properties, symbology, unique value, unique value renderer

Sample Code

MainActivity.kt
Use dark colors for code blocksCopy
                                                                                                                                                                                               
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/* Copyright 2022 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.
 *
 */

package com.esri.arcgisruntime.sample.applyuniquevalueswithalternatesymbols

import android.graphics.Color
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.esri.arcgisruntime.ArcGISRuntimeEnvironment
import com.esri.arcgisruntime.data.ServiceFeatureTable
import com.esri.arcgisruntime.geometry.Point
import com.esri.arcgisruntime.geometry.SpatialReferences
import com.esri.arcgisruntime.layers.FeatureLayer
import com.esri.arcgisruntime.mapping.ArcGISMap
import com.esri.arcgisruntime.mapping.BasemapStyle
import com.esri.arcgisruntime.mapping.Viewpoint
import com.esri.arcgisruntime.mapping.view.AnimationCurve
import com.esri.arcgisruntime.mapping.view.MapView
import com.esri.arcgisruntime.sample.applyuniquevalueswithalternatesymbols.databinding.ActivityMainBinding
import com.esri.arcgisruntime.symbology.SimpleMarkerSymbol
import com.esri.arcgisruntime.symbology.Symbol
import com.esri.arcgisruntime.symbology.SymbolReferenceProperties
import com.esri.arcgisruntime.symbology.UniqueValueRenderer
import kotlin.math.roundToInt


class MainActivity : AppCompatActivity() {

    private val TAG = MainActivity::class.java.simpleName

    // create feature table using the feature service URL
    private val featureTable = ServiceFeatureTable("https://sampleserver6.arcgisonline.com/arcgis/rest/services/SF311/FeatureServer/0")

    // create a feature layer using the feature table
    private val featureLayer = FeatureLayer(featureTable)

    // create a center map point in San Francisco, CA
    private val centerPoint = Point(-13631205.660131, 4546829.846004, SpatialReferences.getWebMercator())

    private val activityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }

    private val mapView: MapView by lazy {
        activityMainBinding.mapView
    }

    private val scaleText: TextView by lazy {
        activityMainBinding.scaleText
    }

    private val resetViewpointButton: Button by lazy {
        activityMainBinding.resetViewpointButton
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(activityMainBinding.root)

        // authentication with an API key or named user is
        // required to access basemaps and other location services
        ArcGISRuntimeEnvironment.setApiKey(BuildConfig.API_KEY)

        // set the unique value renderer on the feature layer
        featureLayer.renderer = createUniqueValueRenderer()

        mapView.apply {
            // create a map with the BasemapType topographic to be displayed in the layout's MapView
            map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC)
            // add the feature layer to the map view's map
            map.operationalLayers.add(featureLayer)
            // set the viewpoint to be centered at San Francisco, CA
            setViewpoint(Viewpoint(centerPoint, 25000.0))
            // update the scale text view as the view point changes
            addViewpointChangedListener { updateScaleLabel() }
        }

        // resets the view point using a ease in out animation
        resetViewpointButton.setOnClickListener {
            mapView.setViewpointAsync(
                Viewpoint(centerPoint, 25000.0),
                5F,
                AnimationCurve.EASE_IN_OUT_SINE
            )
        }
    }

    /**
     * Create the unique values renderer for the feature layer
     */
    private fun createUniqueValueRenderer(): UniqueValueRenderer {
        // create the default symbol
        val symbol = SimpleMarkerSymbol(
            SimpleMarkerSymbol.Style.TRIANGLE,
            Color.RED,
            30F
        )
        // convert the symbol to a multi layer symbol
        val multilayerSymbol = symbol.toMultilayerSymbol().apply {
            referenceProperties = SymbolReferenceProperties(5000.0, 0.0)
        }
        // create alternate symbols for the unique value
        val alternateSymbols = createAlternateSymbols()
        // create a unique value with alternate symbols
        val uniqueValue = UniqueValueRenderer.UniqueValue(
            "unique values based on request type",
            "unique value",
            multilayerSymbol,
            listOf("Damaged Property"),
            alternateSymbols
        )
        // create a unique value renderer
        val uniqueValueRenderer = UniqueValueRenderer().apply {
            // add the unique value
            uniqueValues.add(uniqueValue)
            // set the field name
            fieldNames.add("req_type")
        }
        // create and set the default symbol
        val defaultSymbol = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.DIAMOND, Color.MAGENTA, 15F)
        // set a default symbol for the unique value renderer.
        // This will be use for features that aren't "Damaged Property"
        // or when out of range of the UniqueValue symbols.
        uniqueValueRenderer.defaultSymbol = defaultSymbol.toMultilayerSymbol()

        // set the unique value renderer on the feature layer
        return uniqueValueRenderer
    }

    /**
     * Create alternate symbols for the unique value renderer
     */
    private fun createAlternateSymbols(): List<Symbol> {
        // create the alternate symbol for the mid range scale
        val alternateSymbolBlue = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.SQUARE, Color.BLUE, 30F)
        // convert the symbol to a multilayer symbol
        val alternateSymbolMultilayerBlue = alternateSymbolBlue.toMultilayerSymbol().apply {
            // set the reference properties
            referenceProperties = SymbolReferenceProperties(10000.0, 5000.0)
        }
        // create the alternate symbol for the high range scale
        val alternateSymbolYellow = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.DIAMOND, Color.YELLOW, 30F)
        // convert the symbol to a multilayer symbol
        val alternateSymbolMultilayerYellow = alternateSymbolYellow.toMultilayerSymbol().apply {
            // set the reference properties
            referenceProperties = SymbolReferenceProperties(20000.0, 10000.0)
        }
        // return both alternate symbols
        return listOf(alternateSymbolMultilayerBlue, alternateSymbolMultilayerYellow)
    }

    /**
     * Update the label to display the current scale
     */
    private fun updateScaleLabel() {
        // formats numbers using comma
        val formattedScale = "%,d".format(mapView.mapScale.roundToInt())
        // updates the text view with the current scale
        scaleText.text = "Current scale: 1:$formattedScale"
    }

    override fun onPause() {
        mapView.pause()
        super.onPause()
    }

    override fun onResume() {
        super.onResume()
        mapView.resume()
    }

    override fun onDestroy() {
        mapView.dispose()
        super.onDestroy()
    }
}

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.