diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 208b52c..1555a91 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 = 98 - versionName = "0.2.3.98" + versionCode = 103 + versionName = "0.3.0.103" base.archivesName = "navi-$versionName" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/kouros/navigation/ui/MapView.kt b/app/src/main/java/com/kouros/navigation/ui/MapView.kt index 2f08337..586bdaf 100644 --- a/app/src/main/java/com/kouros/navigation/ui/MapView.kt +++ b/app/src/main/java/com/kouros/navigation/ui/MapView.kt @@ -92,7 +92,7 @@ fun MapView( duration = 1.seconds ) } - NavigationImage(paddingValues, width, height / 6, "", dark) + NavigationImage(paddingValues, width, height / 6, "", dark, tilt) } } } diff --git a/common/car/src/androidTest/java/com/kouros/navigation/car/OverpassTest.kt b/common/car/src/androidTest/java/com/kouros/navigation/car/OverpassTest.kt index cf986aa..eacbbb5 100644 --- a/common/car/src/androidTest/java/com/kouros/navigation/car/OverpassTest.kt +++ b/common/car/src/androidTest/java/com/kouros/navigation/car/OverpassTest.kt @@ -17,31 +17,45 @@ class OverpassTest { @Test fun `maxSpeed Schmalkaldener 30 `() { val curLocation = location(11.582495, 48.186863) - executeSpeedTest( curLocation, "Schmalkaldener Straße", emptyList(), 90, 30) + executeSpeedTest(curLocation, "Schmalkaldener Straße", emptyList(), 90, 30) } @Test fun `maxSpeed Ingolstädter 50 `() { val curLocation = location(11.584384, 48.186338) - executeSpeedTest( curLocation, "Ingolstädter Straße", listOf("B13"), 180, 50) + executeSpeedTest(curLocation, "Ingolstädter Straße", listOf("B13"), 180, 50) } @Test - fun `maxSpeed Isarring 50 `() { - val curLocation = location(11.5989114, 48.1694783) - executeSpeedTest( curLocation, "Isarring", listOf("B2R"), 190, 60) + fun `maxSpeed Isarring `() { + var curLocation = location(11.5999989, 48.1732329) + executeSpeedTest(curLocation, "Isarring", listOf("B2R"), 200, 60) + + curLocation = location(11.5995437, 48.1703931) + executeSpeedTest(curLocation, "Isarring", listOf("B2R"), 204, 50) + + val locations = listOf( + location( 11.5964253, 48.1658679), + location( 11.5960449, 48.1650396), + location( 11.5959427, 48.1645584), + location( 11.5959714, 48.1641186), + ) + locations.forEach { + executeSpeedTest(it, "Biedersteiner", listOf("B2R"), 190, 60) + } } + @Test fun `maxSpeed A94 `() { val curLocation = location(11.88117, 48.16595) - executeSpeedTest( curLocation, "", listOf("A94", "E552"), 90, 130) + executeSpeedTest(curLocation, "", listOf("A94", "E552"), 90, 130) } @Test fun `maxSpeed Fendsbach `() { val curLocation = location(11.94989, 48.21522) - executeSpeedTest( curLocation, "Fendsbach", listOf("St 2331"), 0, 60) + executeSpeedTest(curLocation, "Fendsbach", listOf("St 2331"), 0, 60) } @Test @@ -53,10 +67,10 @@ class OverpassTest { location(11.5850147, 48.1774400) ) - executeSpeedTest( locations[0], "Leopoldstraße", emptyList(), 0, 50) - executeSpeedTest( locations[1], "Leopoldstraße", emptyList(), 180, 30) - executeSpeedTest( locations[2], "Leopoldstraße", emptyList(), 180, 30) - executeSpeedTest( locations[3], "Leopoldstraße", emptyList(), 180, 50) + executeSpeedTest(locations[0], "Leopoldstraße", emptyList(), 0, 50) + executeSpeedTest(locations[1], "Leopoldstraße", emptyList(), 180, 30) + executeSpeedTest(locations[2], "Leopoldstraße", emptyList(), 180, 30) + executeSpeedTest(locations[3], "Leopoldstraße", emptyList(), 180, 50) } @@ -64,11 +78,11 @@ class OverpassTest { fun `maxSpeed Egnatia `() { val locations = listOf( location(20.645487, 39.552875), - location(20.686672, 39.838547), + location(20.686672, 39.838547), ) - executeSpeedTest( locations[0], "", listOf("E90", "E92"), 100, 120) - executeSpeedTest( locations[1], "", listOf("E853"), 320, 90) + executeSpeedTest(locations[0], "", listOf("E90", "E92"), 100, 120) + executeSpeedTest(locations[1], "", listOf("E853"), 320, 90) } fun executeSpeedTest( diff --git a/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt b/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt index a6190a7..5f8c381 100644 --- a/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt +++ b/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt @@ -36,9 +36,9 @@ class RouteModelTest { val distance = listOf( 1025.5, - 989.8, - 963.5, - 923.7, + 1012.8, + 979.5, + 972.7, 915.8, 914.6, 871.0, @@ -130,7 +130,7 @@ class RouteModelTest { } else { assertEquals(stepData.currentManeuverType, ManeuverType.TYPE_TURN_NORMAL_LEFT.value) } - assertEquals(stepData.leftStepDistance, 301.0, 1.0) + assertEquals(stepData.leftStepDistance, 297.0, 1.0) } @Test @@ -142,7 +142,7 @@ class RouteModelTest { val stepData = routeModel.currentStep() assertEquals(stepData.currentManeuverType, ManeuverType.TYPE_TURN_NORMAL_LEFT.value) assertEquals(stepData.instruction, "Schenkendorfstraße") - assertEquals(stepData.leftStepDistance, 170.0, 10.0) + assertEquals(stepData.leftStepDistance, 116.0, 10.0) assertEquals(stepData.lane.size, 4) assertEquals(stepData.lane.first().valid, true) assertEquals(stepData.lane.last().valid, false) @@ -227,10 +227,15 @@ class RouteModelTest { @Test fun `leftStepDistance Inglolstädter `() { - val location: Location = location(11.584578, 48.183653) + + var location = location( 11.584352, 48.186771) routeModel.updateLocation(location, NavigationViewModel(TomTomRepository())) - val step = routeModel.currentStep() - assertEquals(step.leftStepDistance, 645.0, 1.0) + var step = routeModel.currentStep() + assertEquals(step.leftStepDistance, 1019.0, 1.0) + location = location(11.584578, 48.183653) + routeModel.updateLocation(location, NavigationViewModel(TomTomRepository())) + step = routeModel.currentStep() + assertEquals(step.leftStepDistance, 638.0, 1.0) } @Test diff --git a/common/car/src/main/java/com/kouros/navigation/car/DeviceLocationManager.kt b/common/car/src/main/java/com/kouros/navigation/car/DeviceLocationManager.kt index 8582158..1d4d3ea 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/DeviceLocationManager.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/DeviceLocationManager.kt @@ -107,8 +107,8 @@ class DeviceLocationManager( val lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) if (lastLocation != null) { if (setIndividualLocation) { - onInitialLocation(a22) - onLocationUpdate(a22) + onInitialLocation(lastLocation) + onLocationUpdate(lastLocation) } else { onInitialLocation(lastLocation) onLocationUpdate(lastLocation) 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 4bb031c..8728623 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 @@ -122,16 +122,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa */ private val lifecycleObserver: LifecycleObserver = object : DefaultLifecycleObserver { - override fun onPause(owner: LifecycleOwner) { - Log.d(TAG, "NavigationSession paused") - super.onPause(owner) - } - - override fun onResume(owner: LifecycleOwner) { - Log.d(TAG, "NavigationSession resumed") - super.onResume(owner) - } - override fun onDestroy(owner: LifecycleOwner) { if (::navigationManager.isInitialized) { navigationManager.clearNavigationManagerCallback() @@ -465,25 +455,23 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa */ private fun handleNavigationLocation(location: Location) { val startTime = System.currentTimeMillis() - routeModel.updateLocation(location, navigationViewModel) // 30 ms - val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations()) // 160 ms + routeModel.updateLocation(location, navigationViewModel) + val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations()) val streetName = routeModel.currentStep().street - if (checkLocationDeviation(location, snappedLocation, streetName)) { // 1 ms + if (checkLocationDeviation(location, snappedLocation, streetName)) { if (routeModel.navState.arrived) return if (guidanceAudio == 1) { handleGuidanceAudio() } val currentDate = LocalDateTime.now(ZoneOffset.UTC) - checkTraffic(currentDate, snappedLocation) // 0 ms - updateSpeedCamera(snappedLocation) // 0 ms - checkRoute(currentDate, snappedLocation) // 0 ms - updateNavigationScreen() // 660 ms - val endTime = System.currentTimeMillis() - startTime -// Log.d(TAG, "updateNavigationScreen: $endTime") + checkTraffic(currentDate, snappedLocation) + updateSpeedCamera(snappedLocation) + checkRoute(currentDate, snappedLocation) + updateNavigationScreen() checkArrival() - } - + val endTime = System.currentTimeMillis() - startTime + //Log.d(TAG, "handleNavigationLocation: $endTime") } /** @@ -565,7 +553,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa * Called when user starts navigation */ override fun startNavigation() { - Log.d(TAG, "startNavigation") surfaceRenderer.navigation = true surfaceRenderer.viewStyle = ViewStyle.VIEW navigationManager.navigationStarted() @@ -584,7 +571,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa * Called when user exits navigation or arrives at destination. */ override fun stopNavigation() { - Log.d(TAG, "stopNavigation") surfaceRenderer.navigation = false routeModel.stopNavigation() navigationManager.navigationEnded() @@ -636,7 +622,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa * Starts navigation and invalidates the screen. */ override fun onRouteReceived(route: String) { - Log.d(TAG, "onRouteReceived") if (route.isNotEmpty()) { this.route = route if (routeModel.isNavigating()) { @@ -722,7 +707,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa * Loads a route to the specified place and sets it as the destination. */ override fun navigateToPlace(place: Place) { - Log.d(TAG, "navigateToPlace ${place.street}") var prevDestination = Place() if (surfaceRenderer.navigation) { prevDestination = routeModel.navState.destination diff --git a/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt b/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt index fca44a0..68eba70 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt @@ -340,7 +340,8 @@ class SurfaceRenderer( width, height, streetName, - darkMode + darkMode, + tilt ) } LaunchedEffect(position, viewStyle) { 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 0156b34..a788316 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 @@ -23,6 +23,7 @@ import androidx.compose.ui.text.drawText import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.kouros.data.R @@ -45,6 +46,7 @@ import org.maplibre.compose.expressions.dsl.image import org.maplibre.compose.expressions.dsl.interpolate import org.maplibre.compose.expressions.dsl.zoom import org.maplibre.compose.expressions.value.ColorValue +import org.maplibre.compose.expressions.value.DpValue import org.maplibre.compose.layers.Anchor import org.maplibre.compose.layers.FillLayer import org.maplibre.compose.layers.LineLayer @@ -101,17 +103,21 @@ fun MapLibre( cameraState = cameraState, baseStyle = baseStyle, - ) { + ) { getBaseSource(id = "openmaptiles")?.let { tiles -> if (!showBuildings) { BuildingLayer(tiles) } if (viewStyle == ViewStyle.AMENITY_VIEW) { - val lastLocation = location(cameraState.position.target.longitude, cameraState.position.target.latitude) + val lastLocation = location( + cameraState.position.target.longitude, + cameraState.position.target.latitude + ) Puck(cameraState, lastLocation) AmenityLayer(route) } else { - RouteLayer(route, traffic!!) + RouteLayer(route) + TrafficLayer(traffic!!) StartEndLayer(route) //RouteLayerPoint(route ) } @@ -146,72 +152,60 @@ fun StartEndLayer(routeData: String?) { } @Composable -fun RouteLayer(routeData: String?, trafficData: Map) { +fun RouteLayer(routeData: String?) { if (!routeData.isNullOrEmpty()) { val routes = rememberGeoJsonSource(GeoJsonData.JsonString(routeData)) LineLayer( id = "routes-casing", source = routes, - color = const(Color.White), - width = - interpolate( - type = exponential(1.2f), - input = zoom(), - 5 to const(0.4.dp), - 6 to const(0.8.dp), - 7 to const(2.0.dp), - 20 to const(24.dp), - ), + color = const(Color.Green), + width = routeLineWidth(base = 1.dp, isCasing = true), ) LineLayer( id = "routes", source = routes, color = const(RouteColor), - width = - interpolate( - type = exponential(1.2f), - input = zoom(), - 5 to const(0.7.dp), - 6 to const(1.0.dp), - 7 to const(2.4.dp), - 20 to const(26.dp), - ), + width = routeLineWidth(base = 1.dp, isCasing = false), ) } +} + +@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 = - interpolate( - type = exponential(1.2f), - input = zoom(), - 5 to const(0.4.dp), - 6 to const(0.6.dp), - 7 to const(1.8.dp), - 20 to const(20.dp), - ), + width = routeLineWidth(base = 2.dp, isCasing = true), ) LineLayer( id = "traffic-${it.key}", source = traffic, color = trafficColor(it.key), - width = - interpolate( - type = exponential(1.2f), - input = zoom(), - 5 to const(0.4.dp), - 6 to const(0.5.dp), - 7 to const(1.6.dp), - 20 to const(18.dp), - ), + width = routeLineWidth(base = 2.dp, isCasing = false), ) - } } +/** + * Reusable helper for consistent line widths across all route layers. + */ +@Composable +private fun routeLineWidth(base: Dp, isCasing: Boolean = false): Expression { + val extra = if (isCasing) 1.dp else 0.dp + val width = base + extra + + return interpolate( + type = exponential(1.1f), + input = zoom(), + 5 to const(width + 3.dp), + 10 to const(width + 8.dp), + 15 to const(width + 13.dp), + 20 to const(width + 18.dp), + ) +} @Composable fun RouteLayerPoint(routeData: String?) { @@ -244,7 +238,7 @@ fun trafficColor(key: String): Expression { "slow" -> return const(Color(0xFFC43E3E)) "stationary" -> return const(Color(0xFF910A0A)) "heavy" -> return const(Color(0xFF6B0404)) - "roadworks" -> return const(Color(0xFF443506)) + "roadworks" -> return const(Color(0xFFDAA707)) } return const(Color.Blue) } @@ -325,12 +319,13 @@ fun DrawNavigationImages( height: Int, streetName: String?, darkMode: Boolean, + tilt: Double, ) { - NavigationImage(padding, width, height, streetName, darkMode) + NavigationImage(padding, width, height, streetName, darkMode, tilt) if (speed != null) { CurrentSpeed(width, height, speed, maxSpeed) } - if (speed != null && maxSpeed > 0) { + if (speed != null && maxSpeed > 0) { MaxSpeed(width, height, maxSpeed, speed) } //DebugInfo(width, height, lat!!) @@ -342,7 +337,8 @@ fun NavigationImage( width: Int, height: Int, streetName: String?, - darkMode: Boolean + darkMode: Boolean, + tilt: Double ) { val imageSize = (height / 8) @@ -362,12 +358,17 @@ fun NavigationImage( textMeasurerStreet.measure(street, styleStreet, overflow = TextOverflow.Ellipsis) } + val scaleY = if (tilt == 0.0) { + 1F + } else { + 0.7F + } Box(contentAlignment = Alignment.Center, modifier = Modifier.padding(padding)) { Canvas( modifier = Modifier .size(imageSize.dp, imageSize.dp) ) { - scale(scaleX = 1f, scaleY = 0.7f) { + scale(scaleX = 1f, scaleY = scaleY) { drawCircle(navigationColor.copy(alpha = 0.3f)) } } @@ -377,19 +378,19 @@ fun NavigationImage( tint = navigationColor.copy(alpha = 0.7f), modifier = Modifier .size(imageSize.dp, imageSize.dp) - .scale(scaleX = 1f, scaleY = 0.7f), + .scale(scaleX = 1f, scaleY = scaleY), ) Canvas( modifier = Modifier - .size(textLayoutStreet.size.width.dp, textLayoutStreet.size.height.dp * 6 ) + .size(textLayoutStreet.size.width.dp, textLayoutStreet.size.height.dp * 6) ) { if (street.isNotEmpty()) { val topLeftX = center.x - textLayoutStreet.size.width / 2 val topLeftY = center.y + textLayoutStreet.size.height drawRoundRect( topLeft = Offset( - x = topLeftX , + x = topLeftX, y = topLeftY, ), color = if (darkMode) NavigationColorLight else Color.White, @@ -431,7 +432,8 @@ private fun CurrentSpeed( val textMeasurerSpeed = rememberTextMeasurer() val textMeasurerKm = rememberTextMeasurer() - val speed = if (isMetricSystem()) (curSpeed * 3.6).toInt().toString() else (curSpeed * 3.6 * 0.6214).toInt().toString() + val speed = if (isMetricSystem()) (curSpeed * 3.6).toInt() + .toString() else (curSpeed * 3.6 * 0.6214).toInt().toString() val kmh = if (isMetricSystem()) "km/h" else "mph" diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt index c2fdfa4..eeeff03 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt @@ -126,10 +126,9 @@ class RouteCarModel : RouteModel() { .setRemainingTimeColor(CarColor.GREEN) .setRemainingDistanceColor(CarColor.BLUE) if (traffic > 0) { - travelBuilder.setTripText(createDelay(traffic)) travelBuilder.setTripIcon(createCarIcon(carContext, R.drawable.traffic_jam_48px)) + travelBuilder.setTripText(createDelay(traffic)) } - if (navState.travelMessage.isNotEmpty()) { travelBuilder.setTripIcon(createCarIcon(carContext, R.drawable.warning_24px)) travelBuilder.setTripText(CarText.create(navState.travelMessage)) 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 20775ec..0b7839a 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 @@ -4,9 +4,9 @@ import androidx.compose.ui.graphics.Color val NavigationColorLight = Color(0xFF17A119) -val NavigationColorDark = Color(0xFF03411F) +val NavigationColorDark = Color(0xFF07568C) -val RouteColor = Color(0xFF195D02) +val RouteColor = Color(0xFF5201B4) val SpeedColor = Color(0xFF262525) diff --git a/common/data/src/main/java/com/kouros/navigation/data/Route.kt b/common/data/src/main/java/com/kouros/navigation/data/Route.kt index 895201f..688a1c7 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/Route.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/Route.kt @@ -13,6 +13,7 @@ import com.kouros.navigation.data.tomtom.TomTomRoute import com.kouros.navigation.data.valhalla.ValhallaResponse import com.kouros.navigation.data.valhalla.ValhallaRoute import com.kouros.navigation.utils.location +import kotlinx.coroutines.selects.whileSelect import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.jsonObject @@ -102,16 +103,22 @@ data class Route( return if (isRouteValid()) { legs().first().steps[currentStepIndex] } else { - Step(maneuver = Maneuver(waypoints = emptyList(), location = location(0.0, 0.0))) + Step(maneuver = Maneuver(waypoints = emptyList(), location = location(0.0, 0.0), leftDistance = emptyList())) } } + /** + * Maneuver locations to snap location + */ fun maneuverLocations(): List { + val wayPointIndex = currentStep().waypointIndex val waypoints = currentStep().maneuver.waypoints val points = mutableListOf() - for (loc in waypoints) { - val point = Point.fromLngLat(loc[0], loc[1]) - points.add(point) + for ((index,loc) in waypoints.withIndex()) { + if (index >= wayPointIndex && points.size < 20) { + val point = Point.fromLngLat(loc[0], loc[1]) + points.add(point) + } } return points } diff --git a/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt b/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt index 9abd541..d7f7361 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt @@ -12,6 +12,7 @@ import com.kouros.navigation.utils.GeoUtils.createCenterLocation import com.kouros.navigation.utils.GeoUtils.createLineStringCollection import com.kouros.navigation.utils.GeoUtils.decodePolyline import com.kouros.navigation.utils.location +import kotlin.math.absoluteValue class OsrmRoute { @@ -27,8 +28,22 @@ class OsrmRoute { val steps = mutableListOf() leg.steps.forEach { step -> val intersections = mutableListOf() + val leftDistance = mutableListOf() + var lastLocation = location(0.0,0.0) val points = decodePolyline(step.geometry, 5) waypoints.addAll(points) + // calculate left step distance for each point + var leftStepDistance = step.distance + leftDistance.add(leftStepDistance.toFloat()) + points.forEach { + if (lastLocation.latitude != 0.0) { + val curLocation = location(it[0], it[1]) + val dist = curLocation.distanceTo(lastLocation).absoluteValue + leftStepDistance -= dist + leftDistance.add(leftStepDistance.toFloat()) + } + lastLocation = location(it[0], it[1]) + } val maneuver = RouteManeuver( bearingBefore = step.maneuver.bearingBefore, bearingAfter = step.maneuver.bearingAfter, @@ -38,7 +53,8 @@ class OsrmRoute { location = location( step.maneuver.location[0], step.maneuver.location[1] - ) + ), + leftDistance = leftDistance ) step.intersections.forEach { it2 -> if (it2.location[0] != 0.0) { diff --git a/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt b/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt index 69141b3..0599411 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt @@ -56,7 +56,6 @@ class Overpass { """.trimMargin() Log.d("OverpassApi", "Overpass Query: $searchQuery") - val connection = (URL(overpassUrl).openConnection() as HttpURLConnection).apply { requestMethod = "POST" setRequestProperty("Accept", "application/json") diff --git a/common/data/src/main/java/com/kouros/navigation/data/overpass/Tags.kt b/common/data/src/main/java/com/kouros/navigation/data/overpass/Tags.kt index e19139c..581456c 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/overpass/Tags.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/overpass/Tags.kt @@ -9,6 +9,7 @@ data class Tags( val lanes: String = "", val lit: String = "", val maxspeed: String = "0", + @SerializedName("maxspeed:variable") val maxSpeedVariable: String = "", val name: String = "", val oneway: String = "", val ref: String = "", diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt index df77a1e..47b0425 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt @@ -12,6 +12,7 @@ data class Maneuver( val street: String = "", val message: String = "", val pointIndex: Int = 0, + val leftDistance: List ) enum class ManeuverType(val value: Int) { 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 8ed5478..3658045 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 @@ -40,8 +40,8 @@ class TomTomRepository : NavigationRepository() { } if (useLocal) { return fetchUrl( - //"http://192.168.1.37/tomtom_routing.json", - "http://192.168.1.37/verona.json", + "http://192.168.1.37/tomtom_routing.json", + //"http://192.168.1.37/verona.json", false ) } diff --git a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt index bbef338..3816833 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt @@ -13,7 +13,9 @@ import com.kouros.navigation.data.route.Summary import com.kouros.navigation.utils.GeoUtils.createCenterLocation import com.kouros.navigation.utils.GeoUtils.createLineStringCollection import com.kouros.navigation.utils.GeoUtils.decodePolyline +import com.kouros.navigation.utils.isNumeric import com.kouros.navigation.utils.location +import kotlin.math.absoluteValue import com.kouros.navigation.data.route.Maneuver as RouteManeuver @@ -53,6 +55,27 @@ class TomTomRoute { val instruction = route.guidance.instructions[index] val street = lastInstruction.street ?: "" val maneuverStreet = instruction.street ?: "" + val leftDistance = mutableListOf() + var lastLocation = location(0.0, 0.0) + stepDistance = + route.guidance.instructions[index].routeOffsetInMeters - stepDistance + stepDuration = + route.guidance.instructions[index].travelTimeInSeconds - stepDuration + // calculate left step distance for each point + var leftStepDistance = stepDistance + leftDistance.add(leftStepDistance.toFloat()) + points.subList( + lastPointIndex, + instruction.pointIndex + 1, + ).forEach { + if (lastLocation.latitude != 0.0) { + val curLocation = location(it[0], it[1]) + val dist = curLocation.distanceTo(lastLocation).absoluteValue + leftStepDistance -= dist + leftDistance.add(leftStepDistance.toFloat()) + } + lastLocation = location(it[0], it[1]) + } val maneuver = RouteManeuver( bearingBefore = 0, bearingAfter = 0, @@ -67,9 +90,9 @@ class TomTomRoute { ), street = maneuverStreet, message = instruction.message, - pointIndex = instruction.pointIndex + pointIndex = instruction.pointIndex, + leftDistance = leftDistance ) - lastPointIndex = instruction.pointIndex val intersections = mutableListOf() route.sections?.forEach { section -> @@ -95,10 +118,7 @@ class TomTomRoute { intersections.add(Intersection(waypoints[startIndex], lanes)) } } - stepDistance = - route.guidance.instructions[index].routeOffsetInMeters - stepDistance - stepDuration = - route.guidance.instructions[index].travelTimeInSeconds - stepDuration + val roadNumbers = if (lastInstruction.roadNumbers != null) { lastInstruction.roadNumbers } else { @@ -212,6 +232,7 @@ class TomTomRoute { "TAKE_EXIT" -> { newType = ManeuverType.TYPE_TURN_SLIGHT_RIGHT.value } + "WAYPOINT_RIGHT" -> { newType = ManeuverType.TYPE_WAYPOINT_RIGHT.value } @@ -227,9 +248,9 @@ private fun exitNumber( ) { 0 } else { - try { + if (isNumeric(instruction.exitNumber)) { instruction.exitNumber.toInt() - } catch (e: NumberFormatException) { + } else { 0 } } diff --git a/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRoute.kt b/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRoute.kt index 14163f3..b40770c 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRoute.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRoute.kt @@ -27,7 +27,8 @@ class ValhallaRoute { type = convertType(it), waypoints =waypoints.subList(it.beginShapeIndex, it.endShapeIndex+1), // TODO: calculate from ShapeIndex ! - location = location(0.0, 0.0) + location = location(0.0, 0.0), + leftDistance = emptyList() ) var name = "" 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 e6da9a0..9e4088f 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,12 +2,14 @@ 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 import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.google.gson.Gson import com.google.gson.GsonBuilder import com.kouros.navigation.data.Constants import com.kouros.navigation.data.Constants.SPEED_BEARING_DEVIATION @@ -24,6 +26,7 @@ import com.kouros.navigation.data.overpass.Overpass import com.kouros.navigation.utils.bearingPositive import com.kouros.navigation.utils.countryCodeSpeedLimit import com.kouros.navigation.utils.getSettingsRepository +import com.kouros.navigation.utils.isNumeric import com.kouros.navigation.utils.location import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose @@ -115,7 +118,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo MutableLiveData() } - val gson: com.google.gson.Gson = GsonBuilder().create() + val gson: Gson = GsonBuilder().create() /** * Retrieves recent places from Preferences as a Flow. @@ -432,7 +435,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo val search = mutableListOf() speedElements.filter { it.type == "way"}.forEach { - var distance = 0F + var distance: Float var maxDistance = 1000F var geometryFirstLocation = location(0.0, 0.0) var geometryLastLocation = location(0.0, 0.0) @@ -466,7 +469,11 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo speed = if (element.tags.maxspeed == "none" && element.tags.highway == "motorway") { countryCodeSpeedLimit(countryCode) } else { - element.tags.maxspeed.toInt() + if (isNumeric(element.tags.maxspeed)) { + element.tags.maxspeed.toInt() + } else { + 0 + } } } return speed diff --git a/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt b/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt index 6de65d9..dd9a946 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt @@ -7,6 +7,7 @@ import com.kouros.navigation.data.Constants.NEAREST_LOCATION_DISTANCE import com.kouros.navigation.data.Constants.SPEED_UPDATE_DISTANCE import com.kouros.navigation.utils.location import java.util.concurrent.TimeUnit +import kotlin.math.absoluteValue class RouteCalculator(var routeModel: RouteModel) { @@ -17,10 +18,11 @@ class RouteCalculator(var routeModel: RouteModel) { fun findStep(location: Location) { var nearestDistance = MAXIMUM_LOCATION_DISTANCE for ((index, step) in routeModel.curLeg.steps.withIndex()) { + var distance = 0F if (index >= routeModel.navState.route.currentStepIndex) { for ((wayIndex, waypoint) in step.maneuver.waypoints.withIndex()) { if (wayIndex >= step.waypointIndex) { - val distance = location.distanceTo(location(waypoint[0], waypoint[1])) + distance = location.distanceTo(location(waypoint[0], waypoint[1])) if (distance < nearestDistance) { nearestDistance = distance routeModel.navState.route.currentStepIndex = step.index @@ -28,9 +30,12 @@ class RouteCalculator(var routeModel: RouteModel) { step.wayPointLocation = location(waypoint[0], waypoint[1]) } } + if (nearestDistance < NEAREST_LOCATION_DISTANCE && distance > NEAREST_LOCATION_DISTANCE * 10) { + break + } } } - if (nearestDistance < NEAREST_LOCATION_DISTANCE) { + if (nearestDistance < NEAREST_LOCATION_DISTANCE && distance > NEAREST_LOCATION_DISTANCE * 10) { break } } @@ -69,19 +74,15 @@ class RouteCalculator(var routeModel: RouteModel) { fun leftStepDistance(): Double { val step = routeModel.route.currentStep() var leftDistance = 0F - for (i in step.waypointIndex..