Show scale bar

View on GitHubSample viewer app

Add a scale bar to visually gauge distances on a map.

Image of show scale bar

Use case

Allows a user to have a visual reference for distances when navigating the map.

How to use the sample

Zoom in or out of the map. The scale bar will automatically display the appropriate scale based on zoom level. Units can be in metric and/or imperial based on locale/optional settings.

How it works

  1. Create an ArcGISMap and add it to a MapView composable.
  2. Listen and track callback changes from onUnitsPerDipChanged, onViewpointChangedForCenterAndScale, onSpatialReferenceChanged using the composable MapView.
  3. Add the Scalebar composable positioned on top of the MapView
  4. Pass in the latest values of unitsPerDip, viewpoint, spatialReference and use a preferred maxWidth into the ScaleBar.

Relevant API

  • ArcGISMap
  • MapView
  • Scalebar
  • UnitSystem

Additional information

The scale will be accurate for the center of the map, and in general more accurate at larger scales (zoomed in). This means at smaller scales (zoomed out), the reading may be inaccurate at the extremes of the visible extent.

This sample uses the scale bar toolkit component, which requires the toolkit. For information about setting up the toolkit, as well as code for the underlying component, visit the the developer's documentation page.

Tags

map, measure, scale, toolkit

Sample Code

ShowScaleBarViewModel.ktShowScaleBarViewModel.ktMainActivity.ktShowScaleBarScreen.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
/* Copyright 2025 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.arcgismaps.sample.showscalebar.components

import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableDoubleStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.arcgismaps.geometry.SpatialReference
import com.arcgismaps.mapping.ArcGISMap
import com.arcgismaps.mapping.BasemapStyle
import com.arcgismaps.mapping.Viewpoint
import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
import kotlinx.coroutines.launch
import kotlin.time.Duration

class ShowScaleBarViewModel(application: Application) : AndroidViewModel(application) {
    // Create a message dialog view model for handling error messages
    val messageDialogVM = MessageDialogViewModel()

    val arcGISMap = ArcGISMap(BasemapStyle.ArcGISStreets).apply {
        initialViewpoint = Viewpoint(33.723271, -117.945793, 30452.0)
    }

    // Scale bar properties updated by the composable mapview.
    var viewpoint by mutableStateOf<Viewpoint?>(null)
        private set
    var unitsPerDip by mutableDoubleStateOf(Double.NaN)
        private set
    var spatialReference by mutableStateOf<SpatialReference?>(null)
        private set

    init {
        viewModelScope.launch {
            arcGISMap.load().onFailure { error ->
                messageDialogVM.showMessageDialog(
                    title = "Failed to load map",
                    description = error.message.toString()
                )
            }
        }
    }

    fun updateViewpoint(newViewpoint: Viewpoint?) {
        viewpoint = newViewpoint
    }

    fun updateUnitsPerDip(newUnitsPerDip: Double) {
        unitsPerDip = newUnitsPerDip
    }

    fun updateSpacialReference(newSpacialReference: SpatialReference?) {
        spatialReference = newSpacialReference
    }
}


/**
 * Convert a duration to a string representation of seconds.
 */
fun Duration.durationToSeconds(): String {
    return if (this == Duration.INFINITE) "Infinite" else this.inWholeSeconds.toString()
}

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