Overpass, Traffic

This commit is contained in:
Dimitris
2026-04-27 15:58:04 +02:00
parent 17e68de017
commit 42b3eabe47
22 changed files with 206 additions and 142 deletions
@@ -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(
@@ -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
@@ -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)
@@ -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
@@ -340,7 +340,8 @@ class SurfaceRenderer(
width,
height,
streetName,
darkMode
darkMode,
tilt
)
}
LaunchedEffect(position, viewStyle) {
@@ -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<String, String>) {
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<String, String>) {
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<DpValue> {
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<ColorValue> {
"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"
@@ -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))