RouteCalculator

This commit is contained in:
Dimitris
2026-05-06 19:49:43 +02:00
parent 25ba7d7bf6
commit 8b612d5b80
9 changed files with 451 additions and 41 deletions
@@ -4,6 +4,7 @@ import android.location.Location
import android.location.LocationManager import android.location.LocationManager
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.kouros.navigation.car.navigation.Simulation
import com.kouros.navigation.data.Constants.homeHohenwaldeck import com.kouros.navigation.data.Constants.homeHohenwaldeck
import com.kouros.navigation.data.RouteEngine import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.overpass.Overpass import com.kouros.navigation.data.overpass.Overpass
@@ -11,6 +12,7 @@ import com.kouros.navigation.data.route.ManeuverType
import com.kouros.navigation.data.tomtom.TomTomRepository import com.kouros.navigation.data.tomtom.TomTomRepository
import com.kouros.navigation.model.NavigationViewModel import com.kouros.navigation.model.NavigationViewModel
import com.kouros.navigation.model.RouteModel import com.kouros.navigation.model.RouteModel
import com.kouros.navigation.utils.GeoUtils.snapLocation
import com.kouros.navigation.utils.getSettingsRepository import com.kouros.navigation.utils.getSettingsRepository
import com.kouros.navigation.utils.location import com.kouros.navigation.utils.location
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -21,6 +23,8 @@ import org.junit.runner.RunWith
import org.junit.Assert.* import org.junit.Assert.*
import org.junit.Before import org.junit.Before
import org.maplibre.compose.expressions.dsl.step
import org.maplibre.geojson.Point
import kotlin.collections.forEach import kotlin.collections.forEach
/** /**
@@ -33,6 +37,348 @@ class RouteModelTest {
val routeModel = RouteModel() val routeModel = RouteModel()
val location = Location(LocationManager.GPS_PROVIDER) val location = Location(LocationManager.GPS_PROVIDER)
val leftDistance = listOf(
64.0,
0.0,
143.7469482421875,
111.36382293701172,
101.35636901855469,
0.0,
356.0272216796875,
250.2962646484375,
245.09136962890625,
213.86209106445312,
173.69473266601562,
90.41661834716797,
40.58605194091797,
22.74074363708496,
12.33098030090332,
0.0,
1046.3043212890625,
1026.2342529296875,
1012.8703002929688,
979.4789428710938,
972.7660522460938,
971.6541137695312,
914.8672485351562,
873.6181030273438,
831.3056030273438,
781.1799926757812,
719.982666015625,
653.1173095703125,
578.4839477539062,
566.2300415039062,
490.4866943359375,
482.66766357421875,
451.45355224609375,
443.6345520019531,
391.28887939453125,
346.71197509765625,
316.65283203125,
237.5792236328125,
141.71861267089844,
78.22917175292969,
55.94073486328125,
41.31438064575195,
31.061260223388672,
20.943891525268555,
16.43439483642578,
0.0,
346.5630798339844,
339.0434875488281,
329.3117370605469,
321.055908203125,
304.0730285644531,
285.6786193847656,
272.10870361328125,
233.37289428710938,
213.9094696044922,
198.87030029296875,
164.74563598632812,
136.4317626953125,
91.64434051513672,
82.11692810058594,
60.832786560058594,
33.411705017089844,
0.0,
338.5975341796875,
318.1346435546875,
211.02232360839844,
201.35411071777344,
187.75144958496094,
179.0858154296875,
170.42019653320312,
123.4335708618164,
90.03872680664062,
74.55345916748047,
60.95069122314453,
39.9212532043457,
0.0,
314.19586181640625,
306.1593017578125,
289.4617004394531,
223.2646484375,
129.7374725341797,
76.47386932373047,
56.635841369628906,
52.30287170410156,
33.61341857910156,
20.613277435302734,
0.0,
5621.87744140625,
5612.591796875,
5587.8486328125,
5483.76806640625,
5447.275390625,
5422.7744140625,
5398.21875,
5373.927734375,
5358.28955078125,
5347.1455078125,
5339.36181640625,
5314.6181640625,
5303.107421875,
5278.109375,
5253.859375,
5244.88720703125,
5200.0400390625,
5151.9326171875,
5138.63916015625,
5085.87548828125,
5054.32080078125,
5012.51220703125,
4962.390625,
4938.140625,
4898.97607421875,
4862.1416015625,
4788.78564453125,
4767.880859375,
4761.89013671875,
4756.5390625,
4749.23388671875,
4724.98388671875,
4645.017578125,
4596.826171875,
4573.98583984375,
4554.85009765625,
4521.48388671875,
4499.232421875,
4484.6064453125,
4471.07763671875,
4457.54931640625,
4415.10986328125,
4389.8359375,
4359.6376953125,
4347.59716796875,
4342.62109375,
4334.59375,
4320.33447265625,
4303.408203125,
4289.998046875,
4261.86962890625,
4215.78515625,
4207.08251953125,
4192.81201171875,
4080.384521484375,
4065.3955078125,
4009.8798828125,
3975.99853515625,
3971.5283203125,
3953.9345703125,
3915.152587890625,
3877.74365234375,
3865.702880859375,
3857.67578125,
3850.37060546875,
3845.019287109375,
3843.161865234375,
3819.26904296875,
3803.01171875,
3686.892578125,
3661.30908203125,
3629.2001953125,
3613.607177734375,
3591.255859375,
3580.988525390625,
3573.37060546875,
3563.377685546875,
3552.08349609375,
3537.639404296875,
3502.25146484375,
3484.319580078125,
3445.313720703125,
3437.883544921875,
3431.69873046875,
3413.72021484375,
3405.05224609375,
3340.725830078125,
3312.782470703125,
3302.888671875,
3292.994873046875,
3240.986328125,
3229.830322265625,
3185.277099609375,
3096.74755859375,
2981.00146484375,
2907.40869140625,
2865.943115234375,
2859.75830078125,
2851.719482421875,
2813.998779296875,
2778.027587890625,
2767.468505859375,
2729.052001953125,
2694.80712890625,
2651.47265625,
2613.131591796875,
2602.36376953125,
2588.665771484375,
2549.015380859375,
2530.110107421875,
2521.1689453125,
2510.401123046875,
2478.26953125,
2437.71337890625,
2400.48681640625,
2350.667236328125,
2320.4267578125,
2294.857666015625,
2268.75830078125,
2260.833740234375,
2249.615234375,
2229.476318359375,
2210.573486328125,
2192.78271484375,
2184.963623046875,
2148.23974609375,
2114.8818359375,
2105.955078125,
2052.49951171875,
2013.5179443359375,
2006.804931640625,
1959.955810546875,
1869.44775390625,
1787.787109375,
1740.873046875,
1719.6939697265625,
1554.079833984375,
1395.2606201171875,
1349.453369140625,
1296.93359375,
1144.57080078125,
1030.08203125,
972.0895385742188,
926.4032592773438,
921.95556640625,
911.9205322265625,
902.9940185546875,
887.3560180664062,
803.21728515625,
774.1542358398438,
767.318603515625,
762.62841796875,
754.703857421875,
751.3680419921875,
738.9346923828125,
725.406005859375,
710.6475219726562,
668.3836669921875,
615.11279296875,
605.7323608398438,
572.6571044921875,
552.6024780273438,
545.567138671875,
474.7272644042969,
459.36187744140625,
445.29119873046875,
427.3172607421875,
406.21124267578125,
378.6969909667969,
371.8613586425781,
362.934814453125,
347.29681396484375,
329.3663330078125,
320.4398193359375,
301.3039855957031,
237.61085510253906,
112.25553131103516,
94.07954406738281,
75.73666381835938,
0.0,
1023.4342651367188,
1003.37939453125,
973.33203125,
901.591796875,
805.4495849609375,
776.5214233398438,
723.6364135742188,
713.1954956054688,
693.5875854492188,
646.0399169921875,
565.5601196289062,
509.7200622558594,
484.1024169921875,
422.78350830078125,
276.693603515625,
265.35223388671875,
187.42837524414062,
160.74212646484375,
97.36228942871094,
29.534751892089844,
18.390586853027344,
0.0,
462.20318603515625,
450.08526611328125,
439.66217041015625,
397.6007080078125,
346.669921875,
290.0765380859375,
270.7193603515625,
208.91537475585938,
191.0126495361328,
177.56549072265625,
165.6016082763672,
117.09382629394531,
108.90425109863281,
75.23577117919922,
37.74562072753906,
0.0,
940.9969482421875,
921.5716552734375,
891.173583984375,
876.2035522460938,
865.4982299804688,
851.6742553710938,
808.6068725585938,
793.0664672851562,
757.3865356445312,
716.390380859375,
701.561767578125,
696.2328491210938,
657.3816528320312,
641.1264038085938,
584.9859619140625,
581.8070678710938,
552.8590087890625,
473.6039733886719,
401.5708923339844,
369.44659423828125,
360.9601745605469,
353.1898498535156,
309.3813781738281,
269.4887390136719,
226.76416015625,
141.3342742919922,
135.6678466796875,
126.8246078491211,
94.33628845214844,
68.04391479492188,
0.0,
149.6364288330078,
118.28071594238281,
0.0,
)
val distance = listOf( val distance = listOf(
1046.5, 1046.5,
1026.5, 1026.5,
@@ -203,30 +549,20 @@ class RouteModelTest {
@Test @Test
fun simulate() { fun simulate() {
val viewModel = NavigationViewModel(TomTomRepository())
for ((index, waypoint) in routeModel.curRoute.waypoints.withIndex()) { for ((index, waypoint) in routeModel.curRoute.waypoints.withIndex()) {
if (routeModel.isNavigating()) { if (routeModel.isNavigating()) {
val curLocation = location(waypoint[0], waypoint[1]) val curLocation = location(waypoint[0], waypoint[1])
if (index in 0..routeModel.curRoute.waypoints.size) { if (index in 0..routeModel.curRoute.waypoints.size) {
//runBlocking { delay(1000) }
val start = System.currentTimeMillis()
routeModel.updateLocation(curLocation, NavigationViewModel(TomTomRepository())) routeModel.updateLocation(curLocation, NavigationViewModel(TomTomRepository()))
val stepData = routeModel.currentStep() val stepData = routeModel.currentStep()
//println("${stepData.instruction} ${System.currentTimeMillis() - start}") assertEquals(stepData.leftStepDistance, leftDistance[index], 1.0)
if (stepData.lane.isNotEmpty()) {
// println(stepData.street)
stepData.lane.forEach {
// println("${it.indications} ${it.valid}")
}
}
// val nextData = routeModel.nextStep()
} }
} }
} }
} }
@Test @Test
fun `leftStepDistance Inglolstädter `() { fun `leftStepDistance Ingolstädter `() {
var location = location(11.584352, 48.186771) var location = location(11.584352, 48.186771)
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository())) routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()))
var step = routeModel.currentStep() var step = routeModel.currentStep()
@@ -264,5 +600,29 @@ class RouteModelTest {
val end = System.currentTimeMillis() - start val end = System.currentTimeMillis() - start
println("Time $end") println("Time $end")
} }
@Test
fun `check leftStepDistance Vogelhart Gpx `() {
routeModel.navState = routeModel.navState.copy(routeBearing = 270F)
var location = location(11.57927955, 48.18554854)
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()))
var stepData = routeModel.currentStep()
assertEquals(stepData.leftStepDistance, 62.0, 1.0)
location = location(11.57919950, 48.18556317)
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()))
stepData = routeModel.currentStep()
assertEquals(stepData.leftStepDistance, 58.0, 1.0)
location = location(11.57906832, 48.18556405)
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()))
stepData = routeModel.currentStep()
assertEquals(stepData.leftStepDistance, 48.0, 1.0)
routeModel.navState = routeModel.navState.copy(routeBearing = 270F)
location = location(11.57875945, 48.18558161)
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()))
//assertEquals(routeModel.currentStep().leftStepDistance, 0.0, 1.0)
}
} }
@@ -28,8 +28,8 @@ class Simulation {
) { ) {
if (routeModel.navState.route.isRouteValid()) { if (routeModel.navState.route.isRouteValid()) {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
//gpxSimulation(routeModel, lifecycleScope, updateLocation) gpxSimulation(routeModel, lifecycleScope, updateLocation)
currentSimulation(routeModel, lifecycleScope, updateLocation) //currentSimulation(routeModel, lifecycleScope, updateLocation)
} else { } else {
currentSimulation(routeModel, lifecycleScope, updateLocation) currentSimulation(routeModel, lifecycleScope, updateLocation)
} }
@@ -81,7 +81,7 @@ class Simulation {
runBlocking { runBlocking {
simulationJob = launch(Dispatchers.IO) { simulationJob = launch(Dispatchers.IO) {
route = TomTomRepository().fetchUrl( route = TomTomRepository().fetchUrl(
"https://kouros-online.de/vh.gpx", "https://kouros-online.de/VH.gpx",
false false
) )
} }
@@ -121,7 +121,7 @@ class Simulation {
if (duration > 100) { if (duration > 100) {
// delay(duration / 4) // delay(duration / 4)
} }
delay(500) delay(200)
lastTime = p.time lastTime = p.time
lastLocation = fakeLocation lastLocation = fakeLocation
} }
@@ -3,15 +3,16 @@ package com.kouros.navigation.car.screen
import androidx.car.app.CarContext import androidx.car.app.CarContext
import androidx.car.app.Screen import androidx.car.app.Screen
import androidx.car.app.model.Action import androidx.car.app.model.Action
import androidx.car.app.model.CarColor
import androidx.car.app.model.CarIcon import androidx.car.app.model.CarIcon
import androidx.car.app.model.GridItem import androidx.car.app.model.GridItem
import androidx.car.app.model.GridTemplate import androidx.car.app.model.GridTemplate
import androidx.car.app.model.Header import androidx.car.app.model.Header
import androidx.car.app.model.ItemList import androidx.car.app.model.ItemList
import androidx.car.app.model.ListTemplate
import androidx.car.app.model.Row
import androidx.car.app.model.Template import androidx.car.app.model.Template
import androidx.compose.ui.graphics.Color
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import androidx.core.graphics.toColorInt
import com.kouros.data.R import com.kouros.data.R
import com.kouros.navigation.car.SurfaceRenderer import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.data.Category import com.kouros.navigation.data.Category
@@ -80,15 +81,50 @@ class CategoriesScreen(
} }
} }
fun carIcon(context: CarContext, category: String, index: Int): CarIcon { fun carIcon(context: CarContext, category: String, index: Int): CarIcon {
val customCarColor =
CarColor.createCustom(android.graphics.Color.MAGENTA, android.graphics.Color.MAGENTA)
if (index == -1) { if (index == -1) {
val resId = when (category) { val icon = when (category) {
CHARGING_STATION -> R.drawable.ev_station_24px CHARGING_STATION -> CarIcon.Builder(
FUEL_STATION -> R.drawable.local_gas_station_24 IconCompat.createWithResource(
PHARMACY -> R.drawable.local_pharmacy_24px context,
else -> R.drawable.restaurant_24px R.drawable.ev_station_24px
)
).setTint(CarColor.GREEN)
FUEL_STATION -> CarIcon.Builder(
IconCompat.createWithResource(
context,
R.drawable.local_gas_station_24
)
).setTint(CarColor.BLUE)
PHARMACY -> CarIcon.Builder(
IconCompat.createWithResource(
context,
R.drawable.local_pharmacy_24px
)
).setTint(CarColor.RED)
RESTAURANT -> CarIcon.Builder(
IconCompat.createWithResource(
context,
R.drawable.restaurant_24px
)
).setTint(customCarColor)
else -> CarIcon.Builder(
IconCompat.createWithResource(
context,
R.drawable.traffic_jam_48px
)
).setTint(CarColor.YELLOW)
} }
return CarIcon.Builder(IconCompat.createWithResource(context, resId)).build() return icon.build()
} else { } else {
return CarIcon.Builder( return CarIcon.Builder(
createNumberIcon( createNumberIcon(
@@ -222,10 +222,9 @@ open class NavigationScreen(
return NavigationTemplate.Builder() return NavigationTemplate.Builder()
.setNavigationInfo( .setNavigationInfo(
MessageInfo.Builder( MessageInfo.Builder(
message carContext.getString(R.string.arrived_exclamation_msg)
//carContext.getString(R.string.arrived_exclamation_msg)
) )
// .setText(street) .setText(message)
.setImage( .setImage(
CarIcon.Builder( CarIcon.Builder(
IconCompat.createWithResource( IconCompat.createWithResource(
@@ -76,7 +76,10 @@ class PlaceListScreen(
val itemListBuilder = ItemList.Builder() val itemListBuilder = ItemList.Builder()
.setNoItemsMessage(carContext.getString(R.string.no_places)) .setNoItemsMessage(carContext.getString(R.string.no_places))
recentPlaces.filter { it.category == category }.forEach { recentPlaces.filter { it.category == category }.forEach {
val street = it.street val street = it.street.ifEmpty {
it.name
}
val row = Row.Builder() val row = Row.Builder()
.setImage(contactIcon(null, it.category)) .setImage(contactIcon(null, it.category))
.setTitle("$street ${it.city}") .setTitle("$street ${it.city}")
@@ -64,10 +64,10 @@ class TomTomRoute {
// calculate left step distance for each point // calculate left step distance for each point
var leftStepDistance = stepDistance var leftStepDistance = stepDistance
leftDistance.add(leftStepDistance.toFloat()) leftDistance.add(leftStepDistance.toFloat())
points.subList( val subPoints = points.subList(
lastPointIndex, lastPointIndex,
instruction.pointIndex + 1, instruction.pointIndex + 1)
).forEach { subPoints.forEach {
if (lastLocation.latitude != 0.0) { if (lastLocation.latitude != 0.0) {
val curLocation = location(it[0], it[1]) val curLocation = location(it[0], it[1])
val dist = curLocation.distanceTo(lastLocation).absoluteValue val dist = curLocation.distanceTo(lastLocation).absoluteValue
@@ -80,10 +80,7 @@ class TomTomRoute {
bearingBefore = 0, bearingBefore = 0,
bearingAfter = 0, bearingAfter = 0,
type = convertType(instruction.maneuver), type = convertType(instruction.maneuver),
waypoints = points.subList( waypoints = subPoints.map { location(it[0], it[1]) },
lastPointIndex,
instruction.pointIndex + 1,
).map { location(it[0], it[1]) },
exit = exitNumber(instruction), exit = exitNumber(instruction),
location = location( location = location(
instruction.point.longitude, instruction.point.latitude instruction.point.longitude, instruction.point.latitude
@@ -8,6 +8,7 @@ import com.kouros.navigation.data.Constants.MAXIMUM_LOCATION_DISTANCE
import com.kouros.navigation.data.Constants.NEAREST_LOCATION_DISTANCE import com.kouros.navigation.data.Constants.NEAREST_LOCATION_DISTANCE
import com.kouros.navigation.data.Constants.SPEED_UPDATE_DISTANCE import com.kouros.navigation.data.Constants.SPEED_UPDATE_DISTANCE
import com.kouros.navigation.data.Constants.TAG import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.utils.bearingPositive
import com.kouros.navigation.utils.location import com.kouros.navigation.utils.location
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@@ -20,28 +21,42 @@ class RouteCalculator(var routeModel: RouteModel) {
fun findStep(location: Location) { fun findStep(location: Location) {
var nearestDistance = MAXIMUM_LOCATION_DISTANCE var nearestDistance = MAXIMUM_LOCATION_DISTANCE
var count = 0
var lastDistance = 0F
var increaseDistance = 0
for ((index, step) in routeModel.curLeg.steps.withIndex()) { for ((index, step) in routeModel.curLeg.steps.withIndex()) {
count++
var distance = 0F var distance = 0F
if (index >= routeModel.navState.route.currentStepIndex) { if (index >= routeModel.navState.route.currentStepIndex) {
for ((wayIndex, waypoint) in step.maneuver.waypoints.withIndex()) { for ((wayIndex, waypoint) in step.maneuver.waypoints.withIndex()) {
count++
if (wayIndex >= step.waypointIndex) { if (wayIndex >= step.waypointIndex) {
distance = location.distanceTo(waypoint) distance = location.distanceTo(waypoint)
if (distance < nearestDistance) { if (distance < nearestDistance ) {
nearestDistance = distance nearestDistance = distance
routeModel.navState.route.currentStepIndex = step.index routeModel.navState.route.currentStepIndex = step.index
step.waypointIndex = wayIndex step.waypointIndex = wayIndex
step.wayPointLocation = waypoint step.wayPointLocation = waypoint
} }
} }
if (nearestDistance < NEAREST_LOCATION_DISTANCE && distance > NEAREST_LOCATION_DISTANCE * 5) { if (stopSearch(nearestDistance, distance) || increaseDistance > 20) {
break
}
if (distance > lastDistance) {
increaseDistance++
}
lastDistance = distance
}
}
if (stopSearch(nearestDistance, distance) || increaseDistance > 20) {
break break
} }
} }
Log.d(TAG, "FindStep $nearestDistance $count $increaseDistance")
} }
if (nearestDistance < NEAREST_LOCATION_DISTANCE && distance > NEAREST_LOCATION_DISTANCE * 5) {
break private fun stopSearch(nearestDistance: Float, distance: Float): Boolean {
} return nearestDistance < NEAREST_LOCATION_DISTANCE && distance > NEAREST_LOCATION_DISTANCE * 10
}
} }
fun travelLeftTime(): Double { fun travelLeftTime(): Double {
+2 -2
View File
@@ -1,9 +1,9 @@
[versions] [versions]
agp = "9.2.0" agp = "9.2.1"
androidGpxParser = "2.3.1" androidGpxParser = "2.3.1"
androidSdkTurf = "6.0.1" androidSdkTurf = "6.0.1"
datastore = "1.2.1" datastore = "1.2.1"
gradle = "9.2.0" gradle = "9.2.1"
koinAndroid = "4.2.1" koinAndroid = "4.2.1"
koinAndroidxCompose = "4.2.1" koinAndroidxCompose = "4.2.1"
koinComposeViewmodel = "4.2.1" koinComposeViewmodel = "4.2.1"