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 the following:
-
An ArcGIS Location Platform or ArcGIS Online account.
-
A development and deployment environment that meets the system requirements.
-
An IDE for Android development in Kotlin.
Develop or download
You have two options for completing this tutorial:
Option 1: Develop the code
Open an Android Studio project
-
Open the project you created by completing the Display a map tutorial.
-
Continue with the following instructions to apply renderers and label definitions to a feature layer based on attribute values.
-
Modify the old project for use in this new tutorial.
-
On your file system, delete the .idea folder, if present, at the top level of your project.
-
In the Android view, 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 <resources> <string name="app_name">Style a feature layer</string> </resources>
-
In the Android view, open Gradle Scripts > settings.gradle.kts.
Change the value of
root
to "Style a feature layer".Project.name settings.gradle.ktsUse dark colors for code blocks dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() maven { url = uri("https://esri.jfrog.io/artifactory/arcgis") } } } 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.
-
Add import statements
In the Android view, open app > kotlin+java > com.example.app > MainScreen.kt. Replace the import statements with the imports needed for this tutorial.
@file:OptIn(ExperimentalMaterial3Api::class)
package com.example.app.screens
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.arcgismaps.Color
import com.arcgismaps.data.ServiceFeatureTable
import com.arcgismaps.mapping.ArcGISMap
import com.arcgismaps.mapping.BasemapStyle
import com.arcgismaps.mapping.Viewpoint
import com.arcgismaps.mapping.labeling.ArcadeLabelExpression
import com.arcgismaps.mapping.labeling.LabelDefinition
import com.arcgismaps.mapping.layers.FeatureLayer
import com.arcgismaps.mapping.symbology.ClassBreak
import com.arcgismaps.mapping.symbology.ClassBreaksRenderer
import com.arcgismaps.mapping.symbology.FontStyle
import com.arcgismaps.mapping.symbology.FontWeight
import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
import com.arcgismaps.mapping.symbology.SimpleFillSymbol
import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
import com.arcgismaps.mapping.symbology.SimpleLineSymbol
import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
import com.arcgismaps.mapping.symbology.SimpleRenderer
import com.arcgismaps.mapping.symbology.TextSymbol
import com.arcgismaps.mapping.symbology.UniqueValue
import com.arcgismaps.mapping.symbology.UniqueValueRenderer
import com.arcgismaps.toolkit.geoviewcompose.MapView
import com.example.app.R
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.
-
In
create
add four read-onlyMap() String
variables: 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.MainScreen.ktUse dark colors for code blocks 54 55 64 65 66 67 68 69 70 71 72 73Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. fun createMap(): ArcGISMap { val parksAndOpenSpaces = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Parks_and_Open_Space/FeatureServer/0" val trails = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails/FeatureServer/0" val trailheads = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0" val trailheadImage = "https://static.arcgis.com/images/Symbols/NPS/npsPictograph_0231b.png" return ArcGISMap(BasemapStyle.ArcGISTopographic).apply { initialViewpoint = Viewpoint( latitude = 34.0270, longitude = -118.8050, scale = 72000.0 ) } }
-
In the
apply
block forArcGISMap
, insert code that addsFeatureLayer
s to the map'soperationalLayers
. For now, thelist
function has no parameters. In this tutorial, you will be adding each new feature layer to the list of parameters.Of() MainScreen.ktUse dark colors for code blocks 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 79 80 81Add line. Add line. Add line. Add line. Add line. Add line. Add line. fun createMap(): ArcGISMap { val parksAndOpenSpaces = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Parks_and_Open_Space/FeatureServer/0" val trails = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails/FeatureServer/0" val trailheads = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0" val trailheadImage = "https://static.arcgis.com/images/Symbols/NPS/npsPictograph_0231b.png" return ArcGISMap(BasemapStyle.ArcGISTopographic).apply { initialViewpoint = Viewpoint( latitude = 34.0270, longitude = -118.8050, scale = 72000.0 ) operationalLayers.addAll( listOf( ) ) } }
-
Add a top-level helper function named
create
that takes a feature service URI as an argument and returns aFeature Layer() FeatureLayer
created from the URI.MainScreen.ktUse dark colors for code blocks Add line. Add line. Add line. Add line. Add line. Add line. fun createFeatureLayer(featureServiceUri: String): FeatureLayer { // Create a service feature table from a Uri val serviceFeatureTable = ServiceFeatureTable(featureServiceUri) // Return a feature layer created from the service feature table return FeatureLayer.createWithFeatureTable(serviceFeatureTable) }
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 top-level function named
create
.Open Space 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.MainScreen.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. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. fun createOpenSpaceLayer(featureServiceUri: String): FeatureLayer { // Create fill symbol objects to represent the parks and open spaces layer val magentaFillSymbol = SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.fromRgba(255, 0, 255), null) val greenFillSymbol = SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.green, null) val blueFillSymbol = SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.fromRgba(0, 0, 255), null) val redFillSymbol = SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.red, null) // Create a unique value for natural areas, regional open spaces, local parks, and regional recreation parks val naturalAreas = UniqueValue( description = "Natural Areas", label = "Natural Areas", symbol = magentaFillSymbol, values = listOf("Natural Areas") ) val regionalOpenSpace = UniqueValue( description = "Regional Open Space", label = "Regional Open Space", symbol = greenFillSymbol, values = listOf("Regional Open Space") ) val localPark = UniqueValue( description = "Local Park", label = "Local Park", symbol = blueFillSymbol, values = listOf("Local Park") ) val regionalRecreationPark = UniqueValue( description = "Regional Recreation Park", label = "Regional Recreation Park", symbol = redFillSymbol, values = listOf("Regional Recreation Park") ) // Create a unique value list with the fill symbols val uniqueValuesList = listOf( naturalAreas, regionalOpenSpace, localPark, regionalRecreationPark ) // Create and assign a unique value renderer to the feature layer val openSpacesUniqueValueRenderer = UniqueValueRenderer( fieldNames = listOf("TYPE"), uniqueValues = uniqueValuesList, defaultLabel = "Open Spaces", defaultSymbol = null ) // Create a parks and open spaces feature layer val featureLayer = createFeatureLayer(featureServiceUri).apply { // Style the feature layer using the unique value renderer renderer = openSpacesUniqueValueRenderer // Set the layer opacity to semi-transparent opacity = 0.2f } return featureLayer }
-
In
create
, add the Parks and Open Space feature layer to the map's operational layers by callingMap() create
.Open Space Layer() MainScreen.ktUse dark colors for code blocks 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 77 78 79 80 81 82 83Add line. fun createMap(): ArcGISMap { val parksAndOpenSpaces = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Parks_and_Open_Space/FeatureServer/0" val trails = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails/FeatureServer/0" val trailheads = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0" val trailheadImage = "https://static.arcgis.com/images/Symbols/NPS/npsPictograph_0231b.png" return ArcGISMap(BasemapStyle.ArcGISTopographic).apply { initialViewpoint = Viewpoint( latitude = 34.0270, longitude = -118.8050, scale = 72000.0 ) operationalLayers.addAll( listOf( createOpenSpaceLayer(parksAndOpenSpaces), ) ) } }
-
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 top-level function named
create
.Trails 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 MainScreen.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. 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. fun createTrailsLayer(featureServiceUri: String): FeatureLayer { // Create simple symbol objects to represent the trails layer val firstClassSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.fromRgba(255, 0, 255), 3.0f) val secondClassSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.fromRgba(255, 0, 255), 4.0f) val thirdClassSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.fromRgba(255, 0, 255), 5.0f) val fourthClassSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.fromRgba(255, 0, 255), 6.0f) val fifthClassSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.fromRgba(255, 0, 255), 7.0f) // Create 5 class breaks val firstClassBreak = ClassBreak( description = "Under 500", label = "0 - 500", minValue = 0.0, maxValue = 500.0, symbol = firstClassSymbol ) val secondClassBreak = ClassBreak( description = "501 to 1000", label = "501 - 1000", minValue = 501.0, maxValue = 1000.0, symbol = secondClassSymbol ) val thirdClassBreak = ClassBreak( description = "1001 to 1500", label = "1001 - 1500", minValue = 1001.0, maxValue = 1500.0, symbol = thirdClassSymbol ) val fourthClassBreak = ClassBreak( description = "1501 to 2000", label = "1501 - 2000", minValue = 1501.0, maxValue = 2000.0, symbol = fourthClassSymbol ) val fifthClassBreak = ClassBreak( description = "2001 to 2300", label = "2001 - 2300", minValue = 2001.0, maxValue = 2300.0, symbol = 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) // Create a trails feature layer val featureLayer = createFeatureLayer(featureServiceUri).apply { // Style the feature layer using the class breaks renderer renderer = elevationClassBreaksRenderer // Set the layer opacity to semi-transparent opacity = 0.75f } return featureLayer }
-
Update
create
to call the newMap() create
function.Trails Layer() MainScreen.ktUse dark colors for code blocks 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 79 80 81 82 83 84 85Add line. fun createMap(): ArcGISMap { val parksAndOpenSpaces = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Parks_and_Open_Space/FeatureServer/0" val trails = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails/FeatureServer/0" val trailheads = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0" val trailheadImage = "https://static.arcgis.com/images/Symbols/NPS/npsPictograph_0231b.png" return ArcGISMap(BasemapStyle.ArcGISTopographic).apply { initialViewpoint = Viewpoint( latitude = 34.0270, longitude = -118.8050, scale = 72000.0 ) operationalLayers.addAll( listOf( createOpenSpaceLayer(parksAndOpenSpaces), createTrailsLayer(trails), ) ) } }
-
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 yellowred ("
). Another way to symbolize these features would be to create a UniqueValueRenderer
that applies a different symbol for these values.
-
Add a top-level function named
create
that uses aBike Only Trails Layer() definitionExpression
to filter for trails that permit bikes.MainScreen.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. fun createBikeOnlyTrailsLayer(featureServiceUri: String): FeatureLayer { // Create blue dot style simple line symbol to represent the bike trails val bikeTrailSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Dot, Color.fromRgba(0, 0, 255), 2.0f) // Create a simple renderer for the feature layer val bikeTrailRenderer = SimpleRenderer(bikeTrailSymbol) // Create a bike trails feature layer val featureLayer = createFeatureLayer(featureServiceUri).apply { // Style the feature layer using the simple renderer renderer = bikeTrailRenderer // Write a definition expression to filter for trails that permit the use of bikes definitionExpression = "USE_BIKE = 'Yes'" } return featureLayer }
-
Add another top-level function named
create
with a definition expression to filter for trails that don't allow bikes.No Bike Trails Layer() MainScreen.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. fun createNoBikeTrailsLayer(featureServiceUri: String): FeatureLayer { // Create a yellow dot style simple line symbol to represent no bike trails val noBikeTrailSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Dot, Color.fromRgba(255, 255, 0), 2.0f) // Create a simple renderer for the feature layer val noBikeTrailRenderer = SimpleRenderer(noBikeTrailSymbol) // Create a no-bike trails feature layer val featureLayer = createFeatureLayer(featureServiceUri).apply { // Style the feature layer using the simple renderer renderer = noBikeTrailRenderer // Write a definition expression to filter for trails that don't permit the use of bikes definitionExpression = "USE_BIKE = 'No'" } return featureLayer }
-
Update
create
to call the newMap() create
andBike Only Trails Layer() create
functions.No Bike Trails Layer() MainScreen.ktUse dark colors for code blocks 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 81 83 84 85 86 87 88 89Add line. Add line. fun createMap(): ArcGISMap { val parksAndOpenSpaces = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Parks_and_Open_Space/FeatureServer/0" val trails = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails/FeatureServer/0" val trailheads = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0" val trailheadImage = "https://static.arcgis.com/images/Symbols/NPS/npsPictograph_0231b.png" return ArcGISMap(BasemapStyle.ArcGISTopographic).apply { initialViewpoint = Viewpoint( latitude = 34.0270, longitude = -118.8050, scale = 72000.0 ) operationalLayers.addAll( listOf( createOpenSpaceLayer(parksAndOpenSpaces), createTrailsLayer(trails), createBikeOnlyTrailsLayer(trails), createNoBikeTrailsLayer(trails), ) ) } }
-
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 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.
-
Add a top-level function named
create
.Trailheads Layer() Use a
PictureMarkerSymbol
to draw a trailhead hiker image. Use theLabelDefinition
to label each trailhead by its name.MainScreen.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. fun createTrailheadsLayer(featureServiceUri: String, trailheadImage: String): FeatureLayer { // 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) // Create the label definition val trailHeadsDefinition = makeLabelDefinition(labelAttribute = "TRL_NAME") // Create a trail heads feature layer val featureLayer: FeatureLayer = createFeatureLayer(featureServiceUri).apply { // Style the feature layer using the simple renderer renderer = simpleRenderer // Set labeling on the layer to be enabled labelsEnabled = true // Add the label definition to the layer's label definition collection labelDefinitions.add(trailHeadsDefinition) } return featureLayer }
-
Create a top-level function named
make
that defines a label definition based on the passed in feature layer attribute. This helper function will also define the label placement and text symbol.Label Definition() MainScreen.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. fun makeLabelDefinition(labelAttribute: String): LabelDefinition { // Create a text symbol for the label definition val labelTextSymbol = TextSymbol().apply { color = Color.white size = 12.0f haloColor = Color.red haloWidth = 1.0f fontFamily = "Arial" fontStyle = FontStyle.Italic fontWeight = FontWeight.Normal } // Create an Arcade label expression based on the field name val labelExpression = ArcadeLabelExpression("\$feature.$labelAttribute") // Create and return the label definition return LabelDefinition(labelExpression, labelTextSymbol) }
-
Update
create
to call the newMap() create
function.Trailheads Layer() MainScreen.ktUse dark colors for code blocks 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 85 86 87 88 89 90 91Add line. fun createMap(): ArcGISMap { val parksAndOpenSpaces = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Parks_and_Open_Space/FeatureServer/0" val trails = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails/FeatureServer/0" val trailheads = "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0" val trailheadImage = "https://static.arcgis.com/images/Symbols/NPS/npsPictograph_0231b.png" return ArcGISMap(BasemapStyle.ArcGISTopographic).apply { initialViewpoint = Viewpoint( latitude = 34.0270, longitude = -118.8050, scale = 72000.0 ) operationalLayers.addAll( listOf( createOpenSpaceLayer(parksAndOpenSpaces), createTrailsLayer(trails), createBikeOnlyTrailsLayer(trails), createNoBikeTrailsLayer(trails), createTrailheadsLayer(trailheads, trailheadImage) ) ) } }
-
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
Alternatively, you can download the tutorial solution, as follows.
Option 2: Download the solution
-
Click the Download solution link in the right-hand side of this page.
-
Unzip the file to a location on your machine.
-
Run Android Studio.
-
Go to File > Open.... Navigate to the solution folder and click Open.
On Windows: If you are in the Welcome to Android Studio dialog, click Open and navigate to the solution folder. Then click Open.
Since the downloaded solution does not contain authentication credentials, you must first set up authentication to create credentials, and then add the developer credentials to the solution.
Set up authentication
To access the secure ArcGIS location services used in this tutorial, you must implement API key authentication or user authentication using an ArcGIS Location Platform or an ArcGIS Online account.
You can implement API key authentication or user authentication in this tutorial. Compare the differences below:
API key authentication
- Users are not required to sign in.
- Requires creating an API key credential with the correct privileges.
- API keys are long-lived access tokens.
- Service usage is billed to the API key owner/developer.
- Simplest authentication method to implement.
- Recommended approach for new ArcGIS developers.
Learn more in API key authentication.
User authentication
- Users are required to sign in with an ArcGIS account.
- User accounts must have privilege to access the ArcGIS services used in application.
- Requires creating OAuth credentials.
- Application uses a redirect URL and client ID.
- Service usage is billed to the organization of the user signed into the application.
Learn more in User authentication.
Create a new API key access token with privileges to access the secure resources used in this tutorial.
-
Complete the Create an API key tutorial and create an API key with the following privilege(s):
- Privileges
- Location services > Basemaps
- Privileges
-
Copy and paste the API key access token into a safe location. It will be used in a later step.
Set developer credentials in the solution
To allow your app users to access ArcGIS location services, use the developer credentials that you created in the Set up authentication step to authenticate requests for resources.
-
In the Android view of Android Studio, open app > kotlin+java > com.example.app > MainActivity. Set the
Authentication
toMode .
.API _KEY MainActivity.ktUse dark colors for code blocks class MainActivity : ComponentActivity() { private enum class AuthenticationMode { API_KEY, USER_AUTH } private val authenticationMode = AuthenticationMode.API_KEY
-
Set the
api
property with your API key access token.Key MainActivity.ktUse dark colors for code blocks override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) when (authenticationMode) { AuthenticationMode.API_KEY -> { ArcGISEnvironment.apiKey = ApiKey.create("YOUR_ACCESS_TOKEN") }
Best Practice: The access token is stored directly in the code as a convenience for this tutorial. Do not store credentials directly in source code in a production environment.
Run the app
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: