Learn how to apply renderers and label definitions to a feature layer based on attribute values.
Applications can display feature layer data with different styles to enhance the visualization. The type of
Renderer
you choose depends on your application. A
SimpleRenderer
applies the same symbol to all features, a
UniqueValueRenderer
applies a different symbol to each unique attribute value, and a
ClassBreaksRenderer
applies a symbol to a range of numeric values. Renderers are responsible for accessing the data and applying the appropriate symbol to each feature when the layer draws. You can also use a
LabelDefinition
to show attribute information for features. Visit the Styles and data visualization documentation to learn more about styling layers.
You can also author, style and save web maps, web scenes, and layers as portal items and then add them to the map in your application. Visit the following tutorials to learn more about adding portal items.
In this tutorial, you will apply different renderers to enhance the visualization of three feature layers with data for the Santa Monica Mountains: Trailheads with a single symbol, Trails based on elevation change and bike use, and Parks and Open Spaces based on the type of park.
Prerequisites
Before starting this tutorial:
-
You need an ArcGIS Location Platform or ArcGIS Online account.
-
Confirm that your system meets the system requirements.
-
An IDE for Android development in Kotlin.
Steps
Open an Android Studio project
-
To start this tutorial, complete the Display a map tutorial. Or download and unzip the Display a map solution in a new folder.
-
Modify the old project for use in this new tutorial. Expand More info for instructions.
-
On your file system, delete the .idea folder, if present, at the top level of your project.
-
In the Android tool window, open app > res > values > strings.xml.
In the
<string name="app
element, change the text content to Style a feature layer._name" > strings.xmlUse dark colors for code blocks 1 2 4 5Change line <resources> <string name="app_name">Style a feature layer</string> </resources>
-
In the Android tool window, open Gradle Scripts > settings.gradle.
Change the value of
root
to "Style a feature layer".Project.name settings.gradleUse dark colors for code blocks 23 24Change line rootProject.name = "Style a feature layer" include ':app'
-
Click File > Sync Project with Gradle files. Android Studio will recognize your changes and create a new .idea folder.
-
-
If you downloaded the solution, get an access token and set the API key.
-
Go to the Create an API key tutorial to obtain a new API key access token using your ArcGIS Location Platform or ArcGIS Online account. Ensure that the following privilege is enabled: Location services > Basemaps > Basemap styles service. Copy the access token as it will be used in the next step.
-
In Android Studio: in the Android tool window, open app > java > com.example.app > MainActivity.
-
In the
set
method, find theApi Key For App() ArcGIS
call and paste your access token inside the double quotes, replacing YOUR_ACCESS_TOKEN.Runtime Environment.set Api Key(" YOUR _ACCESS _TOKE N") MainActivity.ktUse dark colors for code blocks private fun setApiKeyForApp(){ ArcGISRuntimeEnvironment.setApiKey("YOUR_ACCESS_TOKEN") }
-
Add import statements
Replace app-specific imports statements with the imports needed for this tutorial.
package com.example.app
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.graphics.Color
import com.esri.arcgisruntime.ArcGISRuntimeEnvironment
import com.esri.arcgisruntime.arcgisservices.LabelDefinition
import com.esri.arcgisruntime.data.ServiceFeatureTable
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.labeling.ArcadeLabelExpression
import com.esri.arcgisruntime.mapping.view.MapView
import com.esri.arcgisruntime.symbology.ClassBreaksRenderer
import com.esri.arcgisruntime.symbology.ClassBreaksRenderer.ClassBreak
import com.esri.arcgisruntime.symbology.PictureMarkerSymbol
import com.esri.arcgisruntime.symbology.SimpleFillSymbol
import com.esri.arcgisruntime.symbology.SimpleLineSymbol
import com.esri.arcgisruntime.symbology.SimpleRenderer
import com.esri.arcgisruntime.symbology.TextSymbol
import com.esri.arcgisruntime.symbology.UniqueValueRenderer
import com.esri.arcgisruntime.symbology.UniqueValueRenderer.UniqueValue
import com.example.app.databinding.ActivityMainBinding
Among the imports you are pasting into your app is one from the Android API: import android.graphics.
. You will need this class when setting colors for the symbols.
Create a function to add a feature layer
You can add a feature layer from a feature service hosted in ArcGIS. Each feature layer contains features with a single geometry type (point, line, or polygon), and a set of attributes. Once added to the map, feature layers can be symbolized, styled, and labeled in a variety of ways.
Define variables that store feature service URLs used by the app's layers, and then create a helper function to add a layer to the map's list of operational layers. You will use this code throughout the tutorial as you add and symbolize various layers.
-
Create four read-only
String
properties: three for accessing feature layers, and a fourth for accessing a static image for use in a picture marker symbol. You will use these resources in future steps.MainActivity.ktUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. private val parksAndOpenSpaces = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Parks_and_Open_Space/FeatureServer/0" private val trails = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails/FeatureServer/0" private val trailheads = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0" private val trailheadImage = "https://static.arcgis.com/images/Symbols/NPS/npsPictograph_0231b.png"
-
Add a new function named
add
that takes a feature service URI as an argument, creates a feature layer from the URI, and adds it to the list of the map's operational layers.Feature Layer() MainActivity.ktUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. private fun addFeatureLayer(featureServiceUri: String): FeatureLayer { // Create a service feature table from a Uri. val serviceFeatureTable = ServiceFeatureTable(featureServiceUri) // Create a feature layer from the service feature table. val featureLayer = FeatureLayer(serviceFeatureTable) // Add the feature layer to the map's list of operational layers. map.operationalLayers.add(featureLayer) // return the feature layer return featureLayer }
Add a layer with a unique value renderer
Create a method to apply a different symbol for each type of park area to the Parks and Open Spaces feature layer.
-
Add a new function named
add
just after the newly addedOpen Space Layer() add
function.Feature Layer() UniqueValue
assigns a symbol to a value or values. A unique value renderer uses a collection of unique values to assign the appropriate symbol for each feature it renderers.For this example, the renderer uses a feature's
TYPE
attribute value to apply the correct symbol.MainActivity.ktUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. private fun addOpenSpaceLayer() { // Create a parks and open spaces feature layer and add it to the map view. val featureLayer = addFeatureLayer(parksAndOpenSpaces) // Create fill symbols. val magentaFillSymbol = SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.MAGENTA, null) val greenFillSymbol = SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.GREEN, null) val blueFillSymbol = SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.BLUE, null) val redFillSymbol = SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.RED, null) // Create a unique value for natural areas, regional open spaces, local parks, and regional recreation parks. val naturalAreas = UniqueValue( "Natural Areas", "Natural Areas", magentaFillSymbol, listOf("Natural Areas") ) val regionalOpenSpace = UniqueValue( "Regional Open Space", "Regional Open Space", greenFillSymbol, listOf("Regional Open Space") ) val localPark = UniqueValue( "Local Park", "Local Park", blueFillSymbol, listOf("Local Park") ) val regionalRecreationPark = UniqueValue( "Regional Recreation Park", "Regional Recreation Park", redFillSymbol, listOf("Regional Recreation Park") ) val uniqueValuesList = listOf(naturalAreas, regionalOpenSpace, localPark, regionalRecreationPark) // Create and assign a unique value renderer to the feature layer. val openSpacesUniqueValueRenderer = UniqueValueRenderer(listOf("TYPE"), uniqueValuesList, "Open Spaces", null) featureLayer.renderer = openSpacesUniqueValueRenderer // Set the layer opacity to semi-transparent. featureLayer.opacity = 0.2f }
-
Update
setup
to call the newMap() add
function.Open Space Layer() MainActivity.ktUse dark colors for code blocks 93 94 95 96 97 98 99 100 101 102 103 105 106Add line. // set up your map here. You will call this method from onCreate() private fun setupMap() { // create a map with the BasemapStyle streets map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC) // set the map to be displayed in the layout's MapView mapView.map = map // set the viewpoint, Viewpoint(latitude, longitude, scale) mapView.setViewpoint(Viewpoint(34.0270, -118.8050, 72000.0)) addOpenSpaceLayer() }
-
Click Run > Run > app to run the app.
When the app opens, Parks and Open Spaces feature layer is added to the map. The map displays the different types of parks and open spaces with four unique symbols.
Add a layer with a class breaks renderer
Create a method to apply a different symbol for each of the five ranges of elevation gain to the Trails feature layer.
-
Add a new function named
add
just after theTrails Layer() add
function you created above.Open Space Layer() A
ClassBreak
assigns a symbol to a range of values.For this example, the renderer uses each feature's
ELEV
attribute value to classify it into a defined range (class break) and apply the corresponding symbol._GAIN MainActivity.ktUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. private fun addTrailsLayer() { // Create a trails feature layer and add it to the map view. val featureLayer = addFeatureLayer(trails) // Create simple line symbols. val firstClassSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.MAGENTA, 3.0f) val secondClassSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.MAGENTA, 4.0f) val thirdClassSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.MAGENTA, 5.0f) val fourthClassSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.MAGENTA, 6.0f) val fifthClassSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.MAGENTA, 7.0f) // Create 5 class breaks. val firstClassBreak = ClassBreak("Under 500", "0 - 500", 0.0, 500.0, firstClassSymbol) val secondClassBreak = ClassBreak("501 to 1000", "501 - 1000", 501.0, 1000.0, secondClassSymbol) val thirdClassBreak = ClassBreak("1001 to 1500", "1001 - 1500", 1001.0, 1500.0, thirdClassSymbol) val fourthClassBreak = ClassBreak("1501 to 2000", "1501 - 2000", 1501.0, 2000.0, fourthClassSymbol) val fifthClassBreak = ClassBreak("2001 to 2300", "2001 - 2300", 2001.0, 2300.0, fifthClassSymbol) val elevationBreaks = listOf( firstClassBreak, secondClassBreak, thirdClassBreak, fourthClassBreak, fifthClassBreak ) // Create and assign a class breaks renderer to the feature layer. val elevationClassBreaksRenderer = ClassBreaksRenderer("ELEV_GAIN", elevationBreaks) featureLayer.renderer = elevationClassBreaksRenderer // Set the layer opacity to semi-transparent. featureLayer.opacity = 0.75f }
-
Update
setup
to call the newMap() add
function.Trails Layer() MainActivity.ktUse dark colors for code blocks 93 94 95 96 97 98 99 100 101 102 103 104 105 107 108Add line. // set up your map here. You will call this method from onCreate() private fun setupMap() { // create a map with the BasemapStyle streets map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC) // set the map to be displayed in the layout's MapView mapView.map = map // set the viewpoint, Viewpoint(latitude, longitude, scale) mapView.setViewpoint(Viewpoint(34.0270, -118.8050, 72000.0)) addOpenSpaceLayer() addTrailsLayer() }
-
Click Run > Run > app to run the app.
When the app opens, the Trails feature layer is added to the map. The map displays trails with different symbols depending on trail elevation.
Add layers with definition expressions
You can use a definition expression to define a subset of features to display. Features that do not meet the expression criteria are not displayed by the layer. In the following steps, you will create two methods that use a definition expression to apply a symbol to a subset of features in the Trails feature layer.
FeatureLayer.DefinitionExpression
uses a SQL expression to limit the features available for query and display. Your code will create two layers that each display a different subset of trails based on the value for the USE
field. Trails that allow bikes will be symbolized with a blue symbol ("
) and those that don't will be yellow ("
). Another way to symbolize these features would be to create a
UniqueValueRenderer
that applies a different symbol for these values.
-
Add a function named
add
with a definition expression to filter for trails that permit bikes. Add this function just after the newly addedBike Only Trails Layer() add
function.Trails Layer() MainActivity.ktUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. private fun addBikeOnlyTrailsLayer() { // Create a trails feature layer and add it to the map view. val featureLayer = addFeatureLayer(trails) // Write a definition expression to filter for trails that permit the use of bikes. featureLayer.definitionExpression = "USE_BIKE = 'Yes'" // Create and assign a simple renderer to the feature layer. val bikeTrailSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.DOT, Color.BLUE, 2.0f) val bikeTrailRenderer = SimpleRenderer(bikeTrailSymbol) featureLayer.renderer = bikeTrailRenderer }
-
Add another function named
add
with a definition expression to filter for trails that don't allow bikes. Add this function just after theNo Bike Trails Layer() add
function.Bike Only Trails Layer() MainActivity.ktUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. private fun addNoBikeTrailsLayer() { // Create a trails feature layer and add it to the map view. val featureLayer: FeatureLayer = addFeatureLayer(trails) // Write a definition expression to filter for trails that don't permit the use of bikes. featureLayer.definitionExpression = "USE_BIKE = 'No'" // Create and assign a simple renderer to the feature layer. val noBikeTrailSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.DOT, Color.YELLOW, 2.0f) val noBikeTrailRenderer = SimpleRenderer(noBikeTrailSymbol) featureLayer.renderer = noBikeTrailRenderer }
-
Update
setup
to call the newMap() add
andBike Only Trails Layer() add
functions.No Bike Trails Layer() MainActivity.ktUse dark colors for code blocks 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 109 111 112Add line. Add line. // set up your map here. You will call this method from onCreate() private fun setupMap() { // create a map with the BasemapStyle streets map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC) // set the map to be displayed in the layout's MapView mapView.map = map // set the viewpoint, Viewpoint(latitude, longitude, scale) mapView.setViewpoint(Viewpoint(34.0270, -118.8050, 72000.0)) addOpenSpaceLayer() addTrailsLayer() addBikeOnlyTrailsLayer() addNoBikeTrailsLayer() }
-
Click Run > Run > app to run the app.
When the app opens, two Trails feature layers are added to the map. One shows where bikes are permitted and the other where they are prohibited.
Symbolize a layer with a picture symbol and label features with an attribute
Create a method to style trailheads with hiker images and labels for the Trailheads feature layer.
Feature layers, graphics overlays, and map image layer sublayers in your ArcGIS Runtime app can be labeled using a combination of attribute values, text strings, and values calculated with an expression. You can determine how labels are positioned and prioritized, and how conflicts between overlapping labels are automatically and dynamically resolved.
For feature layers, graphics overlays, and map image sublayers, labeling is implemented using a collection of
LabelDefinition
objects to define what labels look like (font, size, color, angle, and so on), the scale at which they display, the text they contain, how they handle overlaps, and so on.
If you want to label everything in your layer or overlay to look identical, you can define a single label definition. If you want to use different label formatting for different attribute values, you can add as many label definitions as you need to define distinct sets of geoelements for labeling.
-
Create a helper function named
make
that defines a label definition based on the passed in feature layer attribute. This function will also define the label placement and text symbol. Add this function just after theLabel Definition() add
function you created earlier.No Bike Trails Layer() MainActivity.ktUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. private fun makeLabelDefinition(labelAttribute: String): LabelDefinition { // Create a new text symbol. val labelTextSymbol = TextSymbol().apply { color = Color.WHITE size = 12.0f haloColor = Color.RED haloWidth = 1.0f fontFamily = "Arial" fontStyle = TextSymbol.FontStyle.ITALIC fontWeight = TextSymbol.FontWeight.NORMAL } // Create a new Arcade label expressions based on the field name. val labelExpression = ArcadeLabelExpression("\$feature." + labelAttribute) // Create and return the label definition. return LabelDefinition(labelExpression, labelTextSymbol) }
-
Add a function named
add
. Add this function just after the newTrailheads Layer() make
function.Label Definition() Use a
PictureMarkerSymbol
to draw a trailhead hiker image. Use theLabelDefinition
to label each trailhead by its name.MainActivity.ktUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. private fun addTrailheadsLayer() { // Create a trail heads feature layer and add it to the map view. val featureLayer: FeatureLayer = addFeatureLayer(trailheads) // Create a new picture marker symbol that uses the trailhead image. val pictureMarkerSymbol = PictureMarkerSymbol(trailheadImage).apply { height = 18.0f width = 18.0f } // Create a new simple renderer based on the picture marker symbol. val simpleRenderer = SimpleRenderer(pictureMarkerSymbol) // Set the feature layer's renderer and enable labels. featureLayer.renderer = simpleRenderer featureLayer.isLabelsEnabled = true // Create the label definition. val trailHeadsDefinition = makeLabelDefinition("TRL_NAME") // Add the label definition to the layer's label definition collection. featureLayer.labelDefinitions.add(trailHeadsDefinition) }
-
Update
setup
to call the newMap() add
function.Trailheads Layer() MainActivity.ktUse dark colors for code blocks 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 113 114Add line. // set up your map here. You will call this method from onCreate() private fun setupMap() { // create a map with the BasemapStyle streets map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC) // set the map to be displayed in the layout's MapView mapView.map = map // set the viewpoint, Viewpoint(latitude, longitude, scale) mapView.setViewpoint(Viewpoint(34.0270, -118.8050, 72000.0)) addOpenSpaceLayer() addTrailsLayer() addBikeOnlyTrailsLayer() addNoBikeTrailsLayer() addTrailheadsLayer() }
-
Click Run > Run > app to run the app.
When the app opens, all the layers you've created and symbolized are displayed on the map.
- Parks and open spaces are displayed with four unique symbols
- Trails use different symbols (line widths) depending on trail elevation
- Trails are blue where bikes are permitted and red where they are prohibited
- Trailheads are displayed with a hiker icon and labels display each trail's name
What's next?
Learn how to use additional API features, ArcGIS location services, and ArcGIS tools in these tutorials: