Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c75d9082 authored by Ellen Poe's avatar Ellen Poe
Browse files

Merge branch 'ellenhp/alternate_picker' into 'main'

Alternate route picker

Closes #3

See merge request e/os/cardinal!29
parents b555f3b0 1df97c86
Loading
Loading
Loading
Loading
Loading
+64 −3
Original line number Diff line number Diff line
@@ -20,14 +20,34 @@ package earth.maps.cardinal.data

import androidx.compose.ui.graphics.Color

fun Color.isYellow(): Boolean {
/**
 * Desaturates a color by reducing its saturation by the specified amount.
 * 
 * @param amount The amount to desaturate, where 0.0f means no change and 1.0f means completely grayscale.
 *               Values outside 0.0f..1.0f will be clamped to this range.
 * @return A new Color with reduced saturation.
 */
fun Color.desaturate(amount: Float): Color {
    val clampedAmount = amount.coerceIn(0f, 1f)
    
    val r = red
    val g = green
    val b = blue
    val max = maxOf(r, g, b)
    val min = minOf(r, g, b)
    if (max == min) return false // gray
    
    // If the color is already grayscale, no desaturation needed
    if (max == min) {
        return this
    }
    
    val delta = max - min
    val l = (max + min) / 2f
    
    // Calculate saturation
    val s = if (l <= 0.5f) delta / (max + min) else delta / (2f - max - min)
    
    // Calculate hue
    val h = when (max) {
        r -> 60 * (g - b) / delta
        g -> 60 * (2 + (b - r) / delta)
@@ -35,6 +55,47 @@ fun Color.isYellow(): Boolean {
        else -> 0f
    }
    val hue = if (h < 0) h + 360f else h
    return hue in 30f..75f
    
    // Apply desaturation
    val newSaturation = s * (1f - clampedAmount)
    
    // Convert back to RGB
    return hslToRgb(hue, newSaturation, l, alpha)
}

/**
 * Converts HSL color values to RGB.
 * 
 * @param h Hue in degrees (0-360)
 * @param s Saturation (0-1)
 * @param l Lightness (0-1)
 * @param a Alpha (0-1)
 * @return Color in RGB space
 */
private fun hslToRgb(h: Float, s: Float, l: Float, a: Float = 1f): Color {
    if (s == 0f) {
        // Grayscale
        return Color(l, l, l, a)
    }
    
    val c = (1f - kotlin.math.abs(2f * l - 1f)) * s
    val hPrime = h / 60f
    val x = c * (1f - kotlin.math.abs(hPrime % 2f - 1f))
    val m = l - c / 2f
    
    val (rPrime, gPrime, bPrime) = when {
        hPrime < 1f -> Triple(c, x, 0f)
        hPrime < 2f -> Triple(x, c, 0f)
        hPrime < 3f -> Triple(0f, c, x)
        hPrime < 4f -> Triple(0f, x, c)
        hPrime < 5f -> Triple(x, 0f, c)
        else -> Triple(c, 0f, x)
    }
    
    return Color(
        red = rPrime + m,
        green = gPrime + m,
        blue = bPrime + m,
        alpha = a
    )
}
+27 −10
Original line number Diff line number Diff line
@@ -30,12 +30,11 @@ import kotlin.math.sqrt
 */
object GeoUtils {

    private const val EARTH_RADIUS_METERS = 6371000.0
    private const val METERS_TO_KILOMETERS = 1000.0
    private const val METERS_TO_MILES = 1609.34
    private const val METERS_TO_FEET = 3.28084

    private const val SHORT_DISTANCE_THRESHOLD_METERS = 200.0

    /**
     * Formats a distance in meters to a human-readable string based on the unit preference.
     *
@@ -112,10 +111,31 @@ object GeoUtils {
        val a = sin(deltaLat / 2).pow(2) + cos(lat1) * cos(lat2) * sin(deltaLon / 2).pow(2)
        val c = 2 * atan2(sqrt(a), sqrt(1 - a))

        // Earth's radius in meters
        val earthRadius = 6371000.0
        return EARTH_RADIUS_METERS * c
    }

        return earthRadius * c
    /**
     * Calculates the distance between two points using a locally cartesian approximation.
     * This is faster than haversine for short distances but less accurate for long distances.
     * Uses the mean of the two latitudes for the longitude scaling factor.
     *
     * @param latLng1 First point
     * @param latLng2 Second point
     * @return Distance in meters
     */
    fun fastDistance(latLng1: LatLng, latLng2: LatLng): Double {
        // Calculate mean latitude for longitude scaling
        val meanLat = (latLng1.latitude + latLng2.latitude) / 2.0
        val meanLatRad = Math.toRadians(meanLat)

        // Convert latitude and longitude differences to meters
        val latDiff = (latLng2.latitude - latLng1.latitude) * Math.toRadians(EARTH_RADIUS_METERS)
        val lonDiff = (latLng2.longitude - latLng1.longitude) * Math.toRadians(
            EARTH_RADIUS_METERS * cos(meanLatRad)
        )

        // Calculate cartesian distance
        return sqrt(latDiff * latDiff + lonDiff * lonDiff)
    }

    /**
@@ -126,13 +146,10 @@ object GeoUtils {
     * @return A BoundingBox representing the area around the center point
     */
    fun createBoundingBoxAroundPoint(center: LatLng, radiusMeters: Double): BoundingBox {
        // Earth's radius in meters
        val earthRadius = 6371000.0

        // Calculate the approximate delta in degrees for the given radius
        val latDelta = Math.toDegrees(radiusMeters / earthRadius)
        val latDelta = Math.toDegrees(radiusMeters / EARTH_RADIUS_METERS)
        val lonDelta =
            Math.toDegrees(radiusMeters / (earthRadius * cos(Math.toRadians(center.latitude))))
            Math.toDegrees(radiusMeters / (EARTH_RADIUS_METERS * cos(Math.toRadians(center.latitude))))

        // Create the bounding box with north, south, east, west boundaries
        return BoundingBox(
+4 −0
Original line number Diff line number Diff line
@@ -25,4 +25,8 @@ data class LatLng(
    fun distanceTo(other: LatLng): Double {
        return GeoUtils.haversineDistance(this, other)
    }

    fun fastDistanceTo(other: LatLng): Double {
        return GeoUtils.fastDistance(this, other)
    }
}
 No newline at end of file
+9 −4
Original line number Diff line number Diff line
@@ -25,9 +25,10 @@ import kotlinx.coroutines.flow.asStateFlow
import uniffi.ferrostar.Route

data class RouteState(
    val route: Route? = null,
    val routes: List<Route> = emptyList(),
    val isLoading: Boolean = false,
    val error: String? = null
    val error: String? = null,
    val selectedRouteIndex: Int? = null,
)

class RouteStateRepository {
@@ -38,8 +39,12 @@ class RouteStateRepository {
        _routeState.value = _routeState.value.copy(isLoading = isLoading)
    }

    fun setRoute(route: Route?) {
        _routeState.value = _routeState.value.copy(route = route, isLoading = false, error = null)
    fun setRoutes(routes: List<Route>) {
        _routeState.value = _routeState.value.copy(routes = routes, isLoading = false, error = null)
    }

    fun selectRoute(index: Int) {
        _routeState.value = _routeState.value.copy(selectedRouteIndex = index)
    }

    fun setError(error: String?) {
+1 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ abstract class RoutingOptions {
        val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
            .create()
        val wrapper = object {
            val alternates = 5
            val costing_options = mapOf(costingType to this@RoutingOptions)
        }
        return gson.toJson(wrapper)
Loading