diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1968a1f..0affcf1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,8 +17,8 @@ android { applicationId = "com.kouros.navigation" minSdk = 33 targetSdk = 37 - versionCode = 110 - versionName = "0.3.0.110" + versionCode = 111 + versionName = "0.3.0.111" base.archivesName = "navi-$versionName" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt b/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt index 5fbd75f..ce86ce8 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt @@ -111,7 +111,8 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa var speedCameras = listOf() - var lastRouteDate: LocalDateTime = LocalDateTime.now() + + private var lastRouteCheckLocation = location(0.0, 0.0) var navigationManagerStarted = false @@ -461,10 +462,11 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa * Snaps location to route and checks for deviation requiring reroute. */ private fun handleNavigationLocation(location: Location) { + val start = System.currentTimeMillis() routeModel.updateLocation(location, navigationViewModel) val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations()) val streetName = routeModel.currentStep().street - if (checkLocationDeviation(location, snappedLocation, streetName)) { + if (checkLocationDeviation(location, snappedLocation, streetName)) { if (routeModel.navState.arrived) return if (guidanceAudio == 1) { handleGuidanceAudio() @@ -472,16 +474,22 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa val currentDate = LocalDateTime.now(ZoneOffset.UTC) checkTraffic(currentDate, snappedLocation) updateSpeedCamera(snappedLocation) - checkRoute(currentDate, snappedLocation) + checkRoute(snappedLocation) updateNavigationScreen() checkArrival() } + val end = System.currentTimeMillis() + // Log.d(TAG, "UpdateLoction ${end-start} ms") } /** * Checks if the location deviation is acceptable. */ - private fun checkLocationDeviation(location: Location, snappedLocation: Location, streetName: String): Boolean { + private fun checkLocationDeviation( + location: Location, + snappedLocation: Location, + streetName: String + ): Boolean { val distance = location.distanceTo(snappedLocation) when { distance > MAXIMAL_ROUTE_DEVIATION -> { @@ -493,6 +501,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa distance < MAXIMAL_SNAP_CORRECTION -> { surfaceRenderer.updateLocation(snappedLocation, streetName) } + else -> { surfaceRenderer.updateLocation(location, streetName) } @@ -657,8 +666,10 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa val newRouteModel = RouteModel() newRouteModel.navState = routeModel.navState.copy(routingEngine = routingEngine) newRouteModel.startNavigation(route) - routeModel.curRoute.summary.trafficDelay = newRouteModel.curRoute.summary.trafficDelay - updateNavigationScreen() + if ((routeModel.curRoute.summary.trafficDelay - newRouteModel.curRoute.summary.trafficDelay).absoluteValue > 300) { + routeModel.startNavigation(route) + updateNavigationScreen() + } } @@ -767,7 +778,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa * Periodically requests speed camera information near the current location. */ private fun updateSpeedCamera(location: Location) { - if (lastCameraSearch % 100 == 0) { + if (lastCameraSearch % 200 == 0) { navigationViewModel.getSpeedCameras(location, 5.0) } if (speedCameras.isNotEmpty() && lastCameraSearch % 15 == 0) { @@ -811,24 +822,36 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa /** * Checks if a new route is needed based on the time since the last update. */ - private fun checkRoute(currentDate: LocalDateTime, location: Location) { - val duration = Duration.between(currentDate, lastRouteDate) - val routeUpdate = routeModel.curRoute.summary.duration / 4 - if (duration.abs().seconds > routeUpdate) { - lastRouteDate = currentDate - val destination = location( - routeModel.navState.destination.longitude, - routeModel.navState.destination.latitude - ) - navigationViewModel.loadRoute( - carContext, - location, - listOf(destination), - surfaceRenderer.carOrientation - ) + private fun checkRoute(location: Location) { + val distance = location.distanceTo(lastRouteCheckLocation) + if (distance > checkDistance(routeModel)) { + Log.d(TAG, "CheckRoute $distance ${checkDistance(routeModel)}") + if (lastRouteCheckLocation.latitude != 0.0) { + val destination = location( + routeModel.navState.destination.longitude, + routeModel.navState.destination.latitude + ) + navigationViewModel.loadRoute( + carContext, + location, + listOf(destination), + surfaceRenderer.carOrientation + ) + } + lastRouteCheckLocation = location } } + private fun checkDistance(routeModel: RouteModel): Double { + val factor = when (routeModel.curRoute.summary.distance) { + in 0.0..20000.0 -> 4 + in 20000.0..100000.0 -> 5 + in 10000.0..200000.0 -> 6 + else -> 7 + } + return (routeModel.curRoute.summary.distance / factor) + } + override fun invalidateNavigationScreen() { navigationScreen.invalidate() } diff --git a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt index cc0fa49..e915450 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt @@ -28,9 +28,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.kouros.data.R -import com.kouros.navigation.data.ClosedColor import com.kouros.navigation.data.Constants -import com.kouros.navigation.data.Constants.homeVogelhart import com.kouros.navigation.data.HeavyColor import com.kouros.navigation.data.LaneColor import com.kouros.navigation.data.NavigationColorDark @@ -43,7 +41,6 @@ import com.kouros.navigation.data.SlowColor import com.kouros.navigation.data.SpeedColor import com.kouros.navigation.data.StationaryColor import com.kouros.navigation.data.ViewStyle -import com.kouros.navigation.utils.GeoUtils.createEndCollection import com.kouros.navigation.utils.GeoUtils.createPointCollection import com.kouros.navigation.utils.isMetricSystem import com.kouros.navigation.utils.location @@ -143,7 +140,7 @@ fun MapLibre( @Composable fun StartEndLayer(routeData: String?) { if (!routeData.isNullOrEmpty()) { - val end = createEndCollection(routeData) + val end = createPointCollection(routeData) val routes = rememberGeoJsonSource(GeoJsonData.JsonString(end)) val img = image(painterResource(R.drawable.sports_score_48px), drawAsSdf = true) SymbolLayer( @@ -186,23 +183,48 @@ fun RouteLayer(routeData: String?) { @Composable fun TrafficLayer(trafficData: Map) { trafficData.forEach { - val traffic = rememberGeoJsonSource(GeoJsonData.JsonString(it.value)) - LineLayer( - id = "traffic-${it.key}-casing", - source = traffic, - color = const(Color.White), - width = routeLineWidth(base = 2.dp, isCasing = true), - ) - LineLayer( - id = "traffic-${it.key}", - source = traffic, - color = trafficColor(it.key), - width = routeLineWidth(base = 1.dp, isCasing = false), - ) + if (it.key == "closed") { + ClosedLayer(it) + } else { + val traffic = rememberGeoJsonSource(GeoJsonData.JsonString(it.value)) + LineLayer( + id = "traffic-${it.key}-casing", + source = traffic, + color = const(Color.White), + width = routeLineWidth(base = 2.dp, isCasing = true), + ) + LineLayer( + id = "traffic-${it.key}", + source = traffic, + color = trafficColor(it.key), + width = routeLineWidth(base = 1.dp, isCasing = false), + ) + } // TrafficIcons(it) } } +@Composable +private fun ClosedLayer(entry: Map.Entry) { + val routes = rememberGeoJsonSource(GeoJsonData.JsonString(entry.value)) + val img = image(painterResource(R.drawable.do_not_disturb_on_24px), drawAsSdf = true) + SymbolLayer( + id = "closed-layer", + source = routes, + iconColor = const(Color.Red), + iconImage = img, + iconSize = + interpolate( + type = exponential(2.0f), + input = zoom(), + 5 to const(2.0f), + 10 to const(2.0f), + 15 to const(3.0f), + 20 to const(4.0f), + ), + ) +} + @Composable private fun TrafficIcons(entry: Map.Entry) { val featureCollection = FeatureCollection.fromJson(entry.value) @@ -307,7 +329,6 @@ fun trafficColor(key: String): Expression { "stationary" -> return const(StationaryColor) "heavy" -> return const(HeavyColor) "roadworks" -> return const(RoadworksColor) - "closed" -> return const(ClosedColor) "lane" -> return const(LaneColor) } return const(Color.Blue) diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt index 1a54cea..49d2713 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt @@ -56,14 +56,14 @@ class Simulation { longitude = point[0] bearing = curBearing speedAccuracyMetersPerSecond = 1.0f // ~1 m/s - speed = 9.0f + speed = 10.0f time = System.currentTimeMillis() elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos() } // Update your app's state as if a real GPS update occurred updateLocation(fakeLocation) // Wait before moving to the next point (e.g., every 1 second) - delay(500) + delay(1500) lastLocation = fakeLocation } } diff --git a/common/data/src/main/java/com/kouros/navigation/data/Color.kt b/common/data/src/main/java/com/kouros/navigation/data/Color.kt index 291f661..e29090b 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/Color.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/Color.kt @@ -3,7 +3,7 @@ package com.kouros.navigation.data import androidx.compose.ui.graphics.Color val NavigationColorLight = Color(0xFF17A119) -val NavigationColorDark = Color(0xFF1055DE) +val NavigationColorDark = Color(0xFF2B007A) val RouteColor = Color(0xFF5201B4) @@ -18,7 +18,6 @@ val SlowColor = Color(0xFFC43E3E) val StationaryColor = Color(0xFF910A0A) val HeavyColor = Color(0xFF6B0404) val RoadworksColor = Color(0xFFDAA707) -val ClosedColor = Color(0xFF280C10) val LaneColor = Color(0xFFEFBAC1) val PharmacyColor = Color(0xFF054603) \ No newline at end of file diff --git a/common/data/src/main/java/com/kouros/navigation/data/Data.kt b/common/data/src/main/java/com/kouros/navigation/data/Data.kt index 8523eff..af347c2 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/Data.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/Data.kt @@ -166,6 +166,8 @@ object Constants { const val TILT = 60.0 const val TANKER_KOENIG_DELAY = 300000 + + const val LAST_CHECK_ROUTE_DISTANCE = 5000 } /** diff --git a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRepository.kt b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRepository.kt index dfc2266..70e76ed 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRepository.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRepository.kt @@ -21,7 +21,7 @@ const val tomtomTrafficUrl = "https://api.tomtom.com/traffic/services/5/incident private const val tomtomFields = "{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}" -val useLocal = BuildConfig.DEBUG +val useLocal = BuildConfig.DEBUG val useLocalTraffic = BuildConfig.DEBUG diff --git a/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt b/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt index 9968a6f..d464056 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt @@ -2,7 +2,6 @@ package com.kouros.navigation.model import android.content.Context import android.location.Location -import android.util.Log import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.toMutableStateList import androidx.lifecycle.MutableLiveData @@ -14,7 +13,6 @@ import com.kouros.navigation.data.Constants import com.kouros.navigation.data.Constants.FAVORITES import com.kouros.navigation.data.Constants.SPEED_BEARING_DEVIATION import com.kouros.navigation.data.Constants.SPEED_UPDATE_DISTANCE -import com.kouros.navigation.data.Constants.TAG import com.kouros.navigation.data.Constants.TANKER_KOENIG_DELAY import com.kouros.navigation.data.NavigationRepository import com.kouros.navigation.data.Place @@ -30,7 +28,8 @@ import com.kouros.navigation.data.overpass.Elements import com.kouros.navigation.data.overpass.Overpass import com.kouros.navigation.repository.SettingsRepository import com.kouros.navigation.utils.GeoUtils.snapLocation -import com.kouros.navigation.utils.GeoUtils.transferCoordinates +import com.kouros.navigation.utils.GeoUtils.buildLineStrings +import com.kouros.navigation.utils.GeoUtils.buildPointCollection import com.kouros.navigation.utils.bearingPositive import com.kouros.navigation.utils.countryCodeSpeedLimit import com.kouros.navigation.utils.getSettingsRepository @@ -290,25 +289,25 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo val incidents = mutableMapOf() val queuing = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Queuing traffic") } - incidents["queuing"] = transferCoordinates(queuing) + incidents["queuing"] = buildLineStrings(queuing) val stationary = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Stationary traffic") } - incidents["stationary"] = transferCoordinates(stationary) + incidents["stationary"] = buildLineStrings(stationary) val slow = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Slow traffic") } - incidents["slow"] = transferCoordinates(slow) + incidents["slow"] = buildLineStrings(slow) val heavy = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Heavy traffic") } - incidents["heavy"] = transferCoordinates(heavy) + incidents["heavy"] = buildLineStrings(heavy) val roadworks = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Roadworks") } - incidents["roadworks"] = transferCoordinates(roadworks) + incidents["roadworks"] = buildLineStrings(roadworks) val laneClosed = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Lane closed") } - incidents["lane"] = transferCoordinates(laneClosed) + incidents["lane"] = buildLineStrings(laneClosed) val closed = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Closed") } - incidents["closed"] = transferCoordinates(closed) + incidents["closed"] = buildPointCollection(closed) return incidents } diff --git a/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt b/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt index ea9b945..1762d33 100644 --- a/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt +++ b/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt @@ -10,6 +10,7 @@ import org.maplibre.spatialk.geojson.Feature import org.maplibre.spatialk.geojson.dsl.addFeature import org.maplibre.spatialk.geojson.dsl.buildFeatureCollection import org.maplibre.spatialk.geojson.dsl.buildLineString +import org.maplibre.spatialk.geojson.dsl.buildMultiPoint import org.maplibre.spatialk.geojson.toJson import org.maplibre.turf.TurfMeasurement import org.maplibre.turf.TurfMisc @@ -136,7 +137,7 @@ object GeoUtils { return points } - fun createEndCollection(geoJson: String): String { + fun createPointCollection(geoJson: String): String { val featureCollection = FeatureCollection.fromJson(geoJson) val geometry = featureCollection.features()!!.first().geometry() val coordinates = (geometry as LineString) @@ -187,41 +188,66 @@ object GeoUtils { return isInside } - fun transferCoordinates(features: List): String { + fun buildLineStrings(features: List): String { return buildFeatureCollection { features.forEach { val movedCoordinates = arrayListOf() - val coordinates = it.geometry() as LineString - val geo = coordinates.coordinates() - var bearing = 0.0F - for (index in 0.. + add( + ft ) - movedCoordinates.add(point) - } - val lineString = buildLineString { - movedCoordinates.forEach { ft -> - add( - ft - ) - } - } - addFeature { - geometry = lineString - properties = buildJsonObject { null } } } + addFeature { + geometry = lineString + properties = buildJsonObject { null } + } + } + }.toJson() + } + + fun buildPointCollection(features: List): String { + return buildFeatureCollection { + features.forEach { + val movedCoordinates = arrayListOf() + val coordinates = it.geometry() as LineString + val geo = coordinates.coordinates() + var point = org.maplibre.spatialk.geojson.Point(geo.first().longitude(), geo.first().latitude()) + movedCoordinates.add(point) + point = org.maplibre.spatialk.geojson.Point(geo.last().longitude(), geo.last().latitude()) + movedCoordinates.add(point) + val multiPoint = buildMultiPoint { + movedCoordinates.forEach { ft -> + add( + ft + ) + } + } + addFeature { + geometry = multiPoint + properties = buildJsonObject { null } + } + } }.toJson() } diff --git a/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt b/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt index 22ca404..de1106b 100644 --- a/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt +++ b/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt @@ -49,18 +49,20 @@ object NavigationUtils { } fun calculateZoom(speed: Double?): Double { + val zoom = 16.5 if (speed == null) { - return 17.0 + return zoom } val speedKmh = (speed * 3.6).toInt() - val zoom = when (speedKmh) { - in 0..10 -> 17.0 - in 11..30 -> 17.5 - in 31..65 -> 17.0 - in 66..70 -> 16.5 - else -> 16.0 + return when (speedKmh) { + in 0..10 -> zoom + 1.0 + in 11..30 -> zoom + 0.5 + in 31..65 -> zoom + in 66..70 -> zoom - 0.5 + in 71..90 -> zoom - 1.0 + in 91..100 -> zoom - 2.0 + else -> zoom - 3.0 } - return zoom } fun previewZoom(centerLocation: Location, previewDistance: Double): Double { diff --git a/common/data/src/main/res/drawable/do_not_disturb_on_24px.xml b/common/data/src/main/res/drawable/do_not_disturb_on_24px.xml new file mode 100644 index 0000000..3d31aa1 --- /dev/null +++ b/common/data/src/main/res/drawable/do_not_disturb_on_24px.xml @@ -0,0 +1,10 @@ + + +