diff --git a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/app.kt b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/app.kt index f8adf973..df98ad6b 100644 --- a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/app.kt +++ b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/app.kt @@ -157,7 +157,7 @@ fun DemoMapControls( if (Platform.supportsBlending) { Box(modifier = modifier.fillMaxSize().padding(8.dp)) { DisappearingScaleBar( - metersPerDp = cameraState.metersPerDpAtTarget, + lengthPerDp = cameraState.lengthPerDpAtTarget, zoom = cameraState.position.zoom, modifier = Modifier.align(Alignment.TopStart), ) diff --git a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/CameraStateDemo.kt b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/CameraStateDemo.kt index ec4bf187..a4a32040 100644 --- a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/CameraStateDemo.kt +++ b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/CameraStateDemo.kt @@ -25,7 +25,7 @@ import dev.sargunv.maplibrecompose.demoapp.DemoOrnamentSettings import dev.sargunv.maplibrecompose.demoapp.DemoScaffold import dev.sargunv.maplibrecompose.demoapp.format import io.github.dellisd.spatialk.geojson.Position -import kotlin.math.roundToInt +import io.github.kevincianfarini.alchemist.unit.LengthUnit.International.Meter private val CHICAGO = Position(latitude = 41.878, longitude = -87.626) @@ -53,14 +53,14 @@ object CameraStateDemo : Demo { Row(modifier = Modifier.safeDrawingPadding().wrapContentSize(Alignment.Center)) { val pos = cameraState.position - val scale = cameraState.metersPerDpAtTarget + val scale = cameraState.lengthPerDpAtTarget Cell("Latitude", pos.target.latitude.format(3), Modifier.weight(1.4f)) Cell("Longitude", pos.target.longitude.format(3), Modifier.weight(1.4f)) Cell("Zoom", pos.zoom.format(2), Modifier.weight(1f)) Cell("Bearing", pos.bearing.format(2), Modifier.weight(1f)) Cell("Tilt", pos.tilt.format(2), Modifier.weight(1f)) - Cell("Scale", "${scale.roundToInt()}m", Modifier.weight(1f)) + Cell("Scale", "${scale.toLong(Meter)}m", Modifier.weight(1f)) } } } diff --git a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/docs/Material3.kt b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/docs/Material3.kt index da51d0cc..10029c3b 100644 --- a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/docs/Material3.kt +++ b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/docs/Material3.kt @@ -33,7 +33,7 @@ fun Material3() { ) Box(modifier = Modifier.fillMaxSize().padding(8.dp)) { - ScaleBar(cameraState.metersPerDpAtTarget, modifier = Modifier.align(Alignment.TopStart)) + ScaleBar(cameraState.lengthPerDpAtTarget, modifier = Modifier.align(Alignment.TopStart)) CompassButton(cameraState, modifier = Modifier.align(Alignment.TopEnd)) AttributionButton(styleState, modifier = Modifier.align(Alignment.BottomEnd)) } @@ -50,7 +50,7 @@ fun Material3() { Box(modifier = Modifier.fillMaxSize().padding(8.dp)) { DisappearingScaleBar( - metersPerDp = cameraState.metersPerDpAtTarget, + lengthPerDp = cameraState.lengthPerDpAtTarget, zoom = cameraState.position.zoom, modifier = Modifier.align(Alignment.TopStart), ) // (1)! diff --git a/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/DisappearingScaleBar.kt b/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/DisappearingScaleBar.kt index cbf231ed..0cac6988 100644 --- a/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/DisappearingScaleBar.kt +++ b/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/DisappearingScaleBar.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import dev.sargunv.maplibrecompose.material3.defaultScaleBarMeasures +import io.github.kevincianfarini.alchemist.type.Length import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.delay @@ -24,9 +25,9 @@ import kotlinx.coroutines.delay * An animated scale bar that appears when the [zoom] level of the map changes, and then disappears * after [visibilityDuration]. This composable wraps [ScaleBar] with visibility animations. * - * @param metersPerDp how many meters are displayed in one device independent pixel (dp), i.e. the - * scale. See - * [CameraState.metersPerDpAtTarget][dev.sargunv.maplibrecompose.compose.CameraState.metersPerDpAtTarget] + * @param lengthPerDp the real world distance in one device independent pixel (dp), i.e. the scale. + * See + * [CameraState.lengthPerDpAtTarget][dev.sargunv.maplibrecompose.compose.CameraState.lengthPerDpAtTarget] * @param zoom zoom level of the map * @param modifier the [Modifier] to be applied to this layout node * @param measures which measures to show on the scale bar. If `null`, measures will be selected @@ -42,7 +43,7 @@ import kotlinx.coroutines.delay */ @Composable public fun DisappearingScaleBar( - metersPerDp: Double, + lengthPerDp: Length, zoom: Double, modifier: Modifier = Modifier, measures: ScaleBarMeasures = defaultScaleBarMeasures(), @@ -71,7 +72,7 @@ public fun DisappearingScaleBar( exit = exitTransition, ) { ScaleBar( - metersPerDp = metersPerDp, + lengthPerDp = lengthPerDp, measures = measures, haloColor = haloColor, color = color, diff --git a/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/ScaleBar.kt b/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/ScaleBar.kt index b75fd6fc..2aee813d 100644 --- a/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/ScaleBar.kt +++ b/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/ScaleBar.kt @@ -23,6 +23,10 @@ import androidx.compose.ui.unit.toSize import dev.sargunv.maplibrecompose.material3.defaultScaleBarMeasures import dev.sargunv.maplibrecompose.material3.drawPathsWithHalo import dev.sargunv.maplibrecompose.material3.drawTextWithHalo +import io.github.kevincianfarini.alchemist.scalar.toLength +import io.github.kevincianfarini.alchemist.type.Length +import io.github.kevincianfarini.alchemist.unit.LengthUnit.International.Nanometer +import kotlin.math.roundToLong /** Which measures to show on the scale bar. */ public data class ScaleBarMeasures( @@ -31,12 +35,12 @@ public data class ScaleBarMeasures( ) /** - * A scale bar composable that shows the current scale of the map in feet, meters or feet and meters - * when zoomed in to the map, changing to miles and kilometers, respectively, when zooming out. + * A scale bar composable that shows the current scale of the map in feet, yards, or meters when + * zoomed in to the map, changing to miles and kilometers when zooming out. * - * @param metersPerDp how many meters are displayed in one device independent pixel (dp), i.e. the - * scale. See - * [CameraState.metersPerDpAtTarget][dev.sargunv.maplibrecompose.compose.CameraState.metersPerDpAtTarget] + * @param lengthPerDp the real world distance in one device independent pixel (dp), i.e. the scale. + * See + * [CameraState.lengthPerDpAtTarget][dev.sargunv.maplibrecompose.compose.CameraState.lengthPerDpAtTarget] * @param modifier the [Modifier] to be applied to this layout node * @param measures which measures to show on the scale bar. If `null`, measures will be selected * based on the system settings or otherwise the user's locale. @@ -48,7 +52,7 @@ public data class ScaleBarMeasures( */ @Composable public fun ScaleBar( - metersPerDp: Double, + lengthPerDp: Length, modifier: Modifier = Modifier, measures: ScaleBarMeasures = defaultScaleBarMeasures(), haloColor: Color = MaterialTheme.colorScheme.surface, @@ -83,8 +87,8 @@ public fun ScaleBar( // scale bar start/end should not overlap horizontally with canvas bounds val maxBarLength = maxWidth - fullStrokeWidth - val params1 = scaleBarParameters(measures.primary, metersPerDp, maxBarLength) - val params2 = measures.secondary?.let { scaleBarParameters(it, metersPerDp, maxBarLength) } + val params1 = scaleBarParameters(measures.primary, lengthPerDp, maxBarLength) + val params2 = measures.secondary?.let { scaleBarParameters(it, lengthPerDp, maxBarLength) } Canvas(modifier.fillMaxSize()) { val fullStrokeWidthPx = fullStrokeWidth.toPx() @@ -102,7 +106,7 @@ public fun ScaleBar( if (true) { // just want a scope here val offsetX = alignment.align( - size = params1.barLength.toPx().toInt(), + size = params1.barWidth.toPx().toInt(), space = (size.width - fullStrokeWidthPx).toInt(), layoutDirection = layoutDirection, ) @@ -110,7 +114,7 @@ public fun ScaleBar( listOf( Offset(offsetX + fullStrokeWidthPx / 2f, 0f + textHeightPx / 2f), Offset(0f, barEndsHeightPx), - Offset(params1.barLength.toPx(), 0f), + Offset(params1.barWidth.toPx(), 0f), Offset(0f, -barEndsHeightPx), ) ) @@ -127,7 +131,7 @@ public fun ScaleBar( if (params2 != null) { val offsetX = alignment.align( - size = params2.barLength.toPx().toInt(), + size = params2.barWidth.toPx().toInt(), space = (size.width - fullStrokeWidthPx).toInt(), layoutDirection = layoutDirection, ) @@ -135,7 +139,7 @@ public fun ScaleBar( listOf( Offset(offsetX + fullStrokeWidthPx / 2f, y + fullStrokeWidthPx / 2f + barEndsHeightPx), Offset(0f, -barEndsHeightPx), - Offset(params2.barLength.toPx(), 0f), + Offset(params2.barWidth.toPx(), 0f), Offset(0f, +barEndsHeightPx), ) ) @@ -178,24 +182,29 @@ public fun ScaleBar( } } -private data class ScaleBarParams(val barLength: Dp, val text: String) +private data class ScaleBarParams(val barWidth: Dp, val text: String) @Composable private fun scaleBarParameters( measure: ScaleBarMeasure, - metersPerDp: Double, + lengthPerDp: Length, maxBarLength: Dp, ): ScaleBarParams { - val max = maxBarLength.value * metersPerDp / measure.unitInMeters + val max = lengthPerDp * maxBarLength.value.toDouble() val stop = findStop(max, measure.stops) - return ScaleBarParams((stop * measure.unitInMeters / metersPerDp).dp, measure.getText(stop)) + return ScaleBarParams(barWidth = (stop / lengthPerDp).dp, text = measure.getText(stop)) } /** * find the largest stop in the list of stops (sorted in ascending order) that is below or equal * [max]. */ -private fun findStop(max: Double, stops: List): Double { +private fun findStop(max: Length, stops: List): Length { val i = stops.binarySearch { it.compareTo(max) } return if (i >= 0) stops[i] else stops[(-i - 2).coerceAtLeast(0)] } + +// https://github.com/kevincianfarini/alchemist/issues/53 +// https://github.com/kevincianfarini/alchemist/issues/54 +private operator fun Length.times(other: Double) = + (this.toLong(Nanometer) * other).roundToLong().toLength(Nanometer) diff --git a/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/ScaleBarMeasure.kt b/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/ScaleBarMeasure.kt index 16346462..10ae7ac6 100644 --- a/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/ScaleBarMeasure.kt +++ b/lib/maplibre-compose-material3/src/commonMain/kotlin/dev/sargunv/maplibrecompose/material3/controls/ScaleBarMeasure.kt @@ -7,21 +7,17 @@ import dev.sargunv.maplibrecompose.material3.generated.kilometers_symbol import dev.sargunv.maplibrecompose.material3.generated.meters_symbol import dev.sargunv.maplibrecompose.material3.generated.miles_symbol import dev.sargunv.maplibrecompose.material3.generated.yards_symbol -import io.github.kevincianfarini.alchemist.scalar.feet -import io.github.kevincianfarini.alchemist.scalar.kilometers -import io.github.kevincianfarini.alchemist.scalar.meters -import io.github.kevincianfarini.alchemist.scalar.miles -import io.github.kevincianfarini.alchemist.scalar.nanometers -import io.github.kevincianfarini.alchemist.scalar.yards +import io.github.kevincianfarini.alchemist.scalar.toLength import io.github.kevincianfarini.alchemist.type.Length import io.github.kevincianfarini.alchemist.unit.LengthUnit import io.github.kevincianfarini.alchemist.unit.LengthUnit.International.Kilometer import io.github.kevincianfarini.alchemist.unit.LengthUnit.International.Meter +import io.github.kevincianfarini.alchemist.unit.LengthUnit.International.Nanometer import io.github.kevincianfarini.alchemist.unit.LengthUnit.UnitedStatesCustomary.Foot import io.github.kevincianfarini.alchemist.unit.LengthUnit.UnitedStatesCustomary.Mile import io.github.kevincianfarini.alchemist.unit.LengthUnit.UnitedStatesCustomary.Yard import kotlin.math.pow -import kotlin.math.roundToInt +import kotlin.math.roundToLong import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource @@ -36,11 +32,12 @@ public interface ScaleBarMeasure { public data object Metric : ScaleBarMeasure { override val stops: List = - buildStops(mantissas = listOf(1, 2, 5).map { it.meters }, exponents = -1..7) + buildStops(mantissas = listOf(1, 2, 5).map { it.toLength(Meter) }, exponents = -1..7) @Composable override fun getText(stop: Length): String = - if (stop >= 1.kilometers) stop.toDisplayString(Kilometer, Res.string.kilometers_symbol) + if (stop >= 1.toLength(Kilometer)) + stop.toDisplayString(Kilometer, Res.string.kilometers_symbol) else stop.toDisplayString(Meter, Res.string.meters_symbol) } @@ -49,14 +46,15 @@ public interface ScaleBarMeasure { override val stops: List = listOf( - buildStops(mantissas = listOf(1, 2, 5).map { it.feet }, exponents = -1..3).dropLast(1), - buildStops(mantissas = listOf(1, 2, 5).map { it.miles }, exponents = 0..4), + buildStops(mantissas = listOf(1, 2, 5).map { it.toLength(Foot) }, exponents = -1..3) + .dropLast(1), + buildStops(mantissas = listOf(1, 2, 5).map { it.toLength(Mile) }, exponents = 0..4), ) .flatten() @Composable override fun getText(stop: Length): String = - if (stop >= 1.miles) stop.toDisplayString(Mile, Res.string.miles_symbol) + if (stop >= 1.toLength(Mile)) stop.toDisplayString(Mile, Res.string.miles_symbol) else stop.toDisplayString(Foot, Res.string.feet_symbol) } @@ -65,14 +63,15 @@ public interface ScaleBarMeasure { override val stops: List = listOf( - buildStops(mantissas = listOf(1, 2, 5).map { it.yards }, exponents = -1..3).dropLast(2), - buildStops(mantissas = listOf(1, 2, 5).map { it.miles }, exponents = 0..4), + buildStops(mantissas = listOf(1, 2, 5).map { it.toLength(Yard) }, exponents = -1..3) + .dropLast(2), + buildStops(mantissas = listOf(1, 2, 5).map { it.toLength(Mile) }, exponents = 0..4), ) .flatten() @Composable override fun getText(stop: Length): String = - if (stop >= 1.miles) stop.toDisplayString(Mile, Res.string.miles_symbol) + if (stop >= 1.toLength(Mile)) stop.toDisplayString(Mile, Res.string.miles_symbol) else stop.toDisplayString(Yard, Res.string.yards_symbol) } } @@ -92,5 +91,6 @@ private fun buildStops(mantissas: List, exponents: IntRange) = buildList } // https://github.com/kevincianfarini/alchemist/issues/53 +// https://github.com/kevincianfarini/alchemist/issues/54 private operator fun Length.times(other: Double) = - (this.toDouble(LengthUnit.International.Nanometer) * other).roundToInt().nanometers + (this.toLong(Nanometer) * other).roundToLong().toLength(Nanometer) diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/AndroidMap.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/AndroidMap.kt index 835c331b..5e48fba8 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/AndroidMap.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/AndroidMap.kt @@ -24,6 +24,9 @@ import dev.sargunv.maplibrecompose.expressions.value.BooleanValue import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.Position +import io.github.kevincianfarini.alchemist.scalar.toLength +import io.github.kevincianfarini.alchemist.type.Length +import io.github.kevincianfarini.alchemist.unit.LengthUnit.International.Meter import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlin.time.Duration @@ -335,8 +338,10 @@ internal class AndroidMap( .map { Feature.fromJson(it.toJson()) } } - override fun metersPerDpAtLatitude(latitude: Double) = - map.projection.getMetersPerPixelAtLatitude(latitude) + override fun lengthPerDpAtLatitude(latitude: Double): Length { + // https://github.com/kevincianfarini/alchemist/issues/54 + return map.projection.getMetersPerPixelAtLatitude(latitude).toLength(Meter) + } } private fun MLNVisibleRegion.toVisibleRegion() = diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/CameraState.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/CameraState.kt index 38175352..be805662 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/CameraState.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/CameraState.kt @@ -17,6 +17,9 @@ import dev.sargunv.maplibrecompose.expressions.value.BooleanValue import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.Position +import io.github.kevincianfarini.alchemist.scalar.toLength +import io.github.kevincianfarini.alchemist.type.Length +import io.github.kevincianfarini.alchemist.unit.LengthUnit.International.Meter import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.channels.Channel @@ -42,7 +45,8 @@ public class CameraState internal constructor(firstPosition: CameraPosition) { internal val positionState = mutableStateOf(firstPosition) internal val moveReasonState = mutableStateOf(CameraMoveReason.NONE) - internal val metersPerDpAtTargetState = mutableStateOf(0.0) + // https://github.com/kevincianfarini/alchemist/issues/54 + internal val lengthPerDpAtTargetState = mutableStateOf(0.toLength(Meter)) /** how the camera is oriented towards the map */ // if the map is not yet initialized, we store the value to apply it later @@ -57,9 +61,9 @@ public class CameraState internal constructor(firstPosition: CameraPosition) { public val moveReason: CameraMoveReason get() = moveReasonState.value - /** meters per dp at the target position */ - public val metersPerDpAtTarget: Double - get() = metersPerDpAtTargetState.value + /** real world distance per dp at the target position */ + public val lengthPerDpAtTarget: Length + get() = lengthPerDpAtTargetState.value /** suspends until the map has been initialized */ public suspend fun awaitInitialized() { diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/MaplibreMap.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/MaplibreMap.kt index 298157dc..dc4babf3 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/MaplibreMap.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/MaplibreMap.kt @@ -115,8 +115,8 @@ public fun MaplibreMap( map as StandardMaplibreMap styleState.attach(style) rememberedStyle = style - cameraState.metersPerDpAtTargetState.value = - map.metersPerDpAtLatitude(map.getCameraPosition().target.latitude) + cameraState.lengthPerDpAtTargetState.value = + map.lengthPerDpAtLatitude(map.getCameraPosition().target.latitude) } override fun onCameraMoveStarted(map: MaplibreMap, reason: CameraMoveReason) { @@ -126,8 +126,8 @@ public fun MaplibreMap( override fun onCameraMoved(map: MaplibreMap) { map as StandardMaplibreMap cameraState.positionState.value = map.getCameraPosition() - cameraState.metersPerDpAtTargetState.value = - map.metersPerDpAtLatitude(map.getCameraPosition().target.latitude) + cameraState.lengthPerDpAtTargetState.value = + map.lengthPerDpAtLatitude(map.getCameraPosition().target.latitude) } override fun onCameraMoveEnded(map: MaplibreMap) {} diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/MaplibreMap.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/MaplibreMap.kt index 84711a11..c46d84fe 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/MaplibreMap.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/MaplibreMap.kt @@ -7,6 +7,7 @@ import dev.sargunv.maplibrecompose.expressions.value.BooleanValue import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.Position +import io.github.kevincianfarini.alchemist.type.Length import kotlin.time.Duration internal interface MaplibreMap { @@ -54,7 +55,7 @@ internal interface MaplibreMap { predicate: CompiledExpression? = null, ): List - suspend fun asyncMetersPerDpAtLatitude(latitude: Double): Double + suspend fun asyncLengthPerDpAtLatitude(latitude: Double): Length interface Callbacks { fun onStyleChanged(map: MaplibreMap, style: Style?) @@ -120,8 +121,8 @@ internal interface StandardMaplibreMap : MaplibreMap { predicate: CompiledExpression?, ): List = queryRenderedFeatures(rect, layerIds, predicate) - override suspend fun asyncMetersPerDpAtLatitude(latitude: Double): Double = - metersPerDpAtLatitude(latitude) + override suspend fun asyncLengthPerDpAtLatitude(latitude: Double): Length = + lengthPerDpAtLatitude(latitude) fun setStyleUri(styleUri: String) @@ -165,5 +166,5 @@ internal interface StandardMaplibreMap : MaplibreMap { predicate: CompiledExpression? = null, ): List - fun metersPerDpAtLatitude(latitude: Double): Double + fun lengthPerDpAtLatitude(latitude: Double): Length } diff --git a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/WebviewMap.kt b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/WebviewMap.kt index b522c935..8c051a13 100644 --- a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/WebviewMap.kt +++ b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/WebviewMap.kt @@ -8,6 +8,9 @@ import dev.sargunv.maplibrecompose.expressions.value.BooleanValue import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.Position +import io.github.kevincianfarini.alchemist.scalar.toLength +import io.github.kevincianfarini.alchemist.type.Length +import io.github.kevincianfarini.alchemist.unit.LengthUnit.International.Meter import kotlin.time.Duration internal class WebviewMap(private val bridge: WebviewBridge) : MaplibreMap { @@ -139,7 +142,8 @@ internal class WebviewMap(private val bridge: WebviewBridge) : MaplibreMap { return emptyList() } - override suspend fun asyncMetersPerDpAtLatitude(latitude: Double): Double { - return 0.0 + override suspend fun asyncLengthPerDpAtLatitude(latitude: Double): Length { + // https://github.com/kevincianfarini/alchemist/issues/54 + return 0.0.toLength(Meter) } } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/IosMap.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/IosMap.kt index 20d503e3..d5431187 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/IosMap.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/IosMap.kt @@ -56,6 +56,7 @@ import dev.sargunv.maplibrecompose.expressions.value.BooleanValue import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.Position +import io.github.kevincianfarini.alchemist.scalar.meters import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlin.time.Duration @@ -474,5 +475,6 @@ internal class IosMap( ) .map { (it as MLNFeatureProtocol).toFeature() } - override fun metersPerDpAtLatitude(latitude: Double) = mapView.metersPerPointAtLatitude(latitude) + override fun lengthPerDpAtLatitude(latitude: Double) = + mapView.metersPerPointAtLatitude(latitude).meters } diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/JsMap.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/JsMap.kt index 87c7fafb..b23ad424 100644 --- a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/JsMap.kt +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/JsMap.kt @@ -32,6 +32,8 @@ import dev.sargunv.maplibrejs.ScaleControl import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.Position +import io.github.kevincianfarini.alchemist.scalar.meters +import io.github.kevincianfarini.alchemist.type.Length import kotlin.time.Duration import kotlin.time.DurationUnit import kotlin.time.TimeSource @@ -296,8 +298,8 @@ internal class JsMap( .map { Feature.fromJson(JSON.stringify(it)) } } - override fun metersPerDpAtLatitude(latitude: Double): Double { + override fun lengthPerDpAtLatitude(latitude: Double): Length { val point = impl.project(LngLat(impl.getCenter().lng, latitude)) - return impl.unproject(point).distanceTo(impl.unproject(Point(point.x + 1, point.y))) + return impl.unproject(point).distanceTo(impl.unproject(Point(point.x + 1, point.y))).meters } }