@@ -40,15 +40,36 @@ import androidx.compose.ui.geometry.Offset
4040import androidx.compose.ui.graphics.Color
4141import androidx.compose.ui.graphics.Shadow
4242import androidx.compose.ui.graphics.StrokeCap
43+ import androidx.compose.ui.platform.LocalDensity
4344import androidx.compose.ui.text.style.TextAlign
45+ import androidx.compose.ui.unit.Density
4446import androidx.compose.ui.unit.Dp
4547import androidx.compose.ui.unit.dp
4648import androidx.compose.ui.unit.em
4749import androidx.compose.ui.unit.sp
50+ import com.google.android.gms.maps.Projection
4851import com.google.maps.android.compose.CameraPositionState
4952import com.google.maps.android.ktx.utils.sphericalDistance
5053import kotlinx.coroutines.delay
5154
55+ internal fun calculateDistance (
56+ projection : Projection ,
57+ width : Dp ,
58+ density : Density
59+ ): Int {
60+ val widthInPixels = with (density) {
61+ width.toPx().toInt()
62+ }
63+
64+ val upperLeftLatLng = projection.fromScreenLocation(Point (0 , 0 ))
65+ val upperRightLatLng =
66+ projection.fromScreenLocation(Point (widthInPixels, 0 ))
67+
68+ val canvasWidthMeters = upperLeftLatLng.sphericalDistance(upperRightLatLng)
69+
70+ return (canvasWidthMeters * 8 / 9 ).toInt()
71+ }
72+
5273public val DarkGray : Color = Color (0xFF3a3c3b )
5374private val defaultWidth: Dp = 65 .dp
5475private val defaultHeight: Dp = 50 .dp
@@ -75,37 +96,12 @@ public fun ScaleBar(
7596 lineColor : Color = DarkGray ,
7697 shadowColor : Color = Color .White ,
7798) {
78- // This is the core logic for calculating the scale of the map.
79- //
80- // `remember` with a key (`cameraPositionState.position.zoom`) is used for performance.
81- // It ensures that the calculation inside is only re-executed when the zoom level changes.
82- // This is important because we don't need to recalculate the scale every time the map pans,
83- // only when the zoom level changes.
84- //
85- // `derivedStateOf` is a Compose state function that creates a new state object that is
86- // derived from other state objects. The calculation inside `derivedStateOf` is only
87- // re-executed when one of the state objects it reads from changes. In this case, it's
88- // `cameraPositionState.projection`. This is another performance optimization that
89- // prevents unnecessary recalculations.
99+ val density = LocalDensity .current
90100 val horizontalLineWidthMeters by remember(cameraPositionState.position.zoom) {
91101 derivedStateOf {
92- // The projection is used to convert between screen coordinates (pixels) and
93- // geographical coordinates (LatLng). It can be null if the map is not ready yet.
94- val projection = cameraPositionState.projection ? : return @derivedStateOf 0
95-
96- // We get the geographical coordinates of two points on the screen: the top-left
97- // corner (0, 0) and a point to the right of it, at the width of the scale bar.
98- val upperLeftLatLng = projection.fromScreenLocation(Point (0 , 0 ))
99- val upperRightLatLng =
100- projection.fromScreenLocation(Point (0 , width.value.toInt()))
101-
102- // We then calculate the spherical distance between these two points in meters.
103- // This gives us the distance that the scale bar represents on the map.
104- val canvasWidthMeters = upperLeftLatLng.sphericalDistance(upperRightLatLng)
105-
106- // We take 8/9th of the canvas width to provide some padding on the right side
107- // of the scale bar.
108- (canvasWidthMeters * 8 / 9 ).toInt()
102+ cameraPositionState.projection?.let {
103+ calculateDistance(it, width, density)
104+ } ? : 0
109105 }
110106 }
111107
0 commit comments