TomTom Routing

This commit is contained in:
Dimitris
2026-02-09 08:44:57 +01:00
parent 0d51c6121d
commit e9474695bf
8 changed files with 95 additions and 70 deletions

View File

@@ -87,8 +87,8 @@ class MainActivity : ComponentActivity() {
val routeModel = RouteModel() val routeModel = RouteModel()
var tilt = 50.0 var tilt = 50.0
val useMock = false val useMock = true
val type = 1 // simulate 2 test 3 gpx val type = 3 // simulate 2 test 3 gpx
var currentIndex = 0 var currentIndex = 0
val stepData: MutableLiveData<StepData> by lazy { val stepData: MutableLiveData<StepData> by lazy {
@@ -317,6 +317,7 @@ class MainActivity : ComponentActivity() {
if (isNavigating()) { if (isNavigating()) {
updateLocation(currentLocation, navigationViewModel) updateLocation(currentLocation, navigationViewModel)
stepData.value = currentStep() stepData.value = currentStep()
println("Step: ${stepData.value!!.instruction} ${stepData.value!!.leftStepDistance}")
if (route.currentStepIndex + 1 <= curLeg.steps.size) { if (route.currentStepIndex + 1 <= curLeg.steps.size) {
nextStepData.value = nextStep() nextStepData.value = nextStep()
} }
@@ -390,7 +391,7 @@ class MainActivity : ComponentActivity() {
for ((index, waypoint) in routeModel.curRoute.waypoints.withIndex()) { for ((index, waypoint) in routeModel.curRoute.waypoints.withIndex()) {
if (routeModel.isNavigating()) { if (routeModel.isNavigating()) {
val deviation = 0.0 val deviation = 0.0
if (index in 300..routeModel.curRoute.waypoints.size) { if (index in 0..routeModel.curRoute.waypoints.size) {
mock.setMockLocation(waypoint[1], waypoint[0]) mock.setMockLocation(waypoint[1], waypoint[0])
delay(500L) // delay(500L) //
} }
@@ -408,7 +409,7 @@ class MainActivity : ComponentActivity() {
navigationViewModel navigationViewModel
) )
val step = routeModel.currentStep() val step = routeModel.currentStep()
println("Step: ${step}") println("Step: ${step.instruction} ${step.leftStepDistance}")
if (step.leftStepDistance == 70.0) { if (step.leftStepDistance == 70.0) {
println("") println("")
} }

View File

@@ -33,12 +33,6 @@ abstract class NavigationRepository {
//private val nominatimUrl = "https://kouros-online.de/nominatim/" //private val nominatimUrl = "https://kouros-online.de/nominatim/"
val tomtomApiKey = "678k5v6940cSXXIS5oD92qIrDgW3RBZ3"
private val tomtomUrl = "https://api.tomtom.com/traffic/services/5/incidentDetails"
private val tomtomFields =
"{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}"
abstract fun getRoute( abstract fun getRoute(
context: Context, context: Context,
@@ -48,6 +42,7 @@ abstract class NavigationRepository {
searchFilter: SearchFilter searchFilter: SearchFilter
): String ): String
abstract fun getTraffic(context: Context, location: Location, carOrientation: Float): String
fun getRouteDistance( fun getRouteDistance(
currentLocation: Location, currentLocation: Location,
location: Location, location: Location,
@@ -78,23 +73,7 @@ abstract class NavigationRepository {
) )
} }
fun getTraffic(context: Context, location: Location, carOrientation: Float): String {
val useAsset = false
val bbox = calculateSquareRadius(location.latitude, location.longitude, 15.0)
return if (useAsset) {
val trafficJson = context.resources.openRawResource(R.raw.tomtom_traffic)
trafficJson.bufferedReader().use { it.readText() }
} else {
val trafficResult = fetchUrl(
"$tomtomUrl?key=$tomtomApiKey&bbox=$bbox&fields=$tomtomFields&language=en-GB&timeValidityFilter=present",
false
)
trafficResult.replace(
"{\"incidents\":",
"{\"type\": \"FeatureCollection\", \"features\":"
)
}
}
fun fetchUrl(url: String, authenticator: Boolean): String { fun fetchUrl(url: String, authenticator: Boolean): String {
try { try {

View File

@@ -26,4 +26,12 @@ class OsrmRepository : NavigationRepository() {
val routeLocation = "${currentLocation.longitude},${currentLocation.latitude};${location.longitude},${location.latitude}?steps=true&alternatives=0" val routeLocation = "${currentLocation.longitude},${currentLocation.latitude};${location.longitude},${location.latitude}?steps=true&alternatives=0"
return fetchUrl(routeUrl + routeLocation + exclude, true) return fetchUrl(routeUrl + routeLocation + exclude, true)
} }
override fun getTraffic(
context: Context,
location: Location,
carOrientation: Float
): String {
TODO("Not yet implemented")
}
} }

View File

@@ -5,10 +5,19 @@ import android.location.Location
import com.kouros.data.R import com.kouros.data.R
import com.kouros.navigation.data.NavigationRepository import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.SearchFilter import com.kouros.navigation.data.SearchFilter
import com.kouros.navigation.utils.GeoUtils.calculateSquareRadius
private const val routeUrl = "https://api.tomtom.com/routing/1/calculateRoute/" private const val routeUrl = "https://api.tomtom.com/routing/1/calculateRoute/"
val tomtomApiKey = "678k5v6940cSXXIS5oD92qIrDgW3RBZ3"
val tomtomTrafficUrl = "https://api.tomtom.com/traffic/services/5/incidentDetails"
private val tomtomFields =
"{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}"
class TomTomRepository : NavigationRepository() { class TomTomRepository : NavigationRepository() {
override fun getRoute( override fun getRoute(
context: Context, context: Context,
@@ -17,9 +26,9 @@ class TomTomRepository : NavigationRepository() {
carOrientation: Float, carOrientation: Float,
searchFilter: SearchFilter searchFilter: SearchFilter
): String { ): String {
//val routeJson = context.resources.openRawResource(R.raw.tomom_routing) val routeJson = context.resources.openRawResource(R.raw.tomom_routing)
//val routeJsonString = routeJson.bufferedReader().use { it.readText() } val routeJsonString = routeJson.bufferedReader().use { it.readText() }
//return routeJsonString return routeJsonString
val url = val url =
routeUrl + "${currentLocation.latitude},${currentLocation.longitude}:${location.latitude},${location.longitude}" + routeUrl + "${currentLocation.latitude},${currentLocation.longitude}:${location.latitude},${location.longitude}" +
"/json?vehicleHeading=90&sectionType=traffic&report=effectiveSettings&routeType=eco" + "/json?vehicleHeading=90&sectionType=traffic&report=effectiveSettings&routeType=eco" +
@@ -33,4 +42,22 @@ class TomTomRepository : NavigationRepository() {
false false
) )
} }
override fun getTraffic(context: Context, location: Location, carOrientation: Float): String {
val useAsset = true
val bbox = calculateSquareRadius(location.latitude, location.longitude, 15.0)
return if (useAsset) {
val trafficJson = context.resources.openRawResource(R.raw.tomtom_traffic)
trafficJson.bufferedReader().use { it.readText() }
} else {
val trafficResult = fetchUrl(
"$tomtomTrafficUrl?key=$tomtomApiKey&bbox=$bbox&fields=$tomtomFields&language=en-GB&timeValidityFilter=present",
false
)
trafficResult.replace(
"{\"incidents\":",
"{\"type\": \"FeatureCollection\", \"features\":"
)
}
}
} }

View File

@@ -47,4 +47,12 @@ class ValhallaRepository : NavigationRepository() {
val routeLocation = Json.encodeToString(valhallaLocation) val routeLocation = Json.encodeToString(valhallaLocation)
return fetchUrl(routeUrl + routeLocation, true) return fetchUrl(routeUrl + routeLocation, true)
} }
override fun getTraffic(
context: Context,
location: Location,
carOrientation: Float
): String {
TODO("Not yet implemented")
}
} }

View File

@@ -136,25 +136,25 @@ open class RouteModel() {
} }
return lanes return lanes
var inter = Intersection() // var inter = Intersection()
var nearestDistance = 100000.0f // var nearestDistance = 100000.0f
route.currentStep().intersection.forEach { // route.currentStep().intersection.forEach {
if (it.lane.isNotEmpty()) { // if (it.lane.isNotEmpty()) {
val distance = location.distanceTo(location(it.location[0], it.location[1])) // val distance = location.distanceTo(location(it.location[0], it.location[1]))
val interBearing = location.bearingTo(location(it.location[0], it.location[1])) // val interBearing = location.bearingTo(location(it.location[0], it.location[1]))
if (distance < nearestDistance) { // if (distance < nearestDistance) {
nearestDistance = distance // nearestDistance = distance
if (distance <= NEXT_STEP_THRESHOLD * 3) { // if (distance <= NEXT_STEP_THRESHOLD * 3) {
if (route.routeEngine == RouteEngine.TOMTOM.ordinal // if (route.routeEngine == RouteEngine.TOMTOM.ordinal
|| (interBearing.absoluteValue - route.currentStep().maneuver.bearingAfter.absoluteValue).absoluteValue < 20 // || (interBearing.absoluteValue - route.currentStep().maneuver.bearingAfter.absoluteValue).absoluteValue < 20
) { // ) {
inter = it // inter = it
} // }
} // }
} // }
} // }
} // }
return inter.lane // return inter.lane
} }
fun updateSpeedLimit(location: Location, viewModel: ViewModel) = runBlocking { fun updateSpeedLimit(location: Location, viewModel: ViewModel) = runBlocking {
@@ -188,7 +188,7 @@ open class RouteModel() {
fun currentStep(): StepData { fun currentStep(): StepData {
val distanceToNextStep = leftStepDistance() val distanceToNextStep = leftStepDistance()
// Determine the maneuver type and corresponding icon // Determine the maneuver type and corresponding icon
val currentStep = route.nextStep(1) // This advances the route's state val currentStep = route.nextStep(1)
// Safely get the street name from the maneuver // Safely get the street name from the maneuver
val streetName = currentStep.name val streetName = currentStep.name
val curManeuverType = currentStep.maneuver.type val curManeuverType = currentStep.maneuver.type
@@ -270,7 +270,8 @@ open class RouteModel() {
/** Returns the current [Step] left distance in m. */ /** Returns the current [Step] left distance in m. */
fun leftStepDistance(): Double { fun leftStepDistance(): Double {
val step = route.currentStep() val step = route.currentStep()
var leftDistance = 0.0 println(step.index)
var leftDistance = 0F
for (i in step.waypointIndex..<step.maneuver.waypoints.size - 1) { for (i in step.waypointIndex..<step.maneuver.waypoints.size - 1) {
val loc1 = location(step.maneuver.waypoints[i][0], step.maneuver.waypoints[i][1]) val loc1 = location(step.maneuver.waypoints[i][0], step.maneuver.waypoints[i][1])
val loc2 = val loc2 =

View File

@@ -135,7 +135,7 @@
"source": "openmaptiles", "source": "openmaptiles",
"source-layer": "landuse", "source-layer": "landuse",
"filter": ["==", ["get", "class"], "track"], "filter": ["==", ["get", "class"], "track"],
"paint": {"fill-color": "#DEE3CD"} "paint": {"fill-color": "rgba(67, 67, 65, 1)"}
}, },
{ {
"id": "landuse_cemetery", "id": "landuse_cemetery",
@@ -151,7 +151,7 @@
"source": "openmaptiles", "source": "openmaptiles",
"source-layer": "landuse", "source-layer": "landuse",
"filter": ["==", ["get", "class"], "hospital"], "filter": ["==", ["get", "class"], "hospital"],
"paint": {"fill-color": "#fde"} "paint": {"fill-color": "rgba(51, 45, 48, 1)"}
}, },
{ {
"id": "landuse_school", "id": "landuse_school",
@@ -362,7 +362,7 @@
["==", ["get", "brunnel"], "tunnel"], ["==", ["get", "brunnel"], "tunnel"],
["match", ["get", "class"], ["service", "track"], true, false] ["match", ["get", "class"], ["service", "track"], true, false]
], ],
"layout": {"line-join": "round"}, "layout": {"line-join": "round", "visibility": "none"},
"paint": { "paint": {
"line-color": "#cfcdca", "line-color": "#cfcdca",
"line-dasharray": [0.5, 0.25], "line-dasharray": [0.5, 0.25],
@@ -535,6 +535,7 @@
["==", ["get", "brunnel"], "tunnel"], ["==", ["get", "brunnel"], "tunnel"],
["match", ["get", "class"], ["path", "pedestrian"], true, false] ["match", ["get", "class"], ["path", "pedestrian"], true, false]
], ],
"layout": {"visibility": "none"},
"paint": { "paint": {
"line-color": "hsl(0,0%,100%)", "line-color": "hsl(0,0%,100%)",
"line-dasharray": [1, 0.75], "line-dasharray": [1, 0.75],
@@ -588,7 +589,7 @@
["==", ["get", "brunnel"], "tunnel"], ["==", ["get", "brunnel"], "tunnel"],
["match", ["get", "class"], ["service", "track"], true, false] ["match", ["get", "class"], ["service", "track"], true, false]
], ],
"layout": {"line-join": "round"}, "layout": {"line-join": "round", "visibility": "none"},
"paint": { "paint": {
"line-color": "#fff", "line-color": "#fff",
"line-width": [ "line-width": [
@@ -616,7 +617,7 @@
], ],
"layout": {"line-join": "round"}, "layout": {"line-join": "round"},
"paint": { "paint": {
"line-color": "#fff4c6", "line-color": "rgba(72, 72, 67, 1)",
"line-width": [ "line-width": [
"interpolate", "interpolate",
["exponential", 1.2], ["exponential", 1.2],

View File

@@ -6,39 +6,39 @@ koinAndroid = "4.1.1"
koinAndroidxCompose = "4.1.1" koinAndroidxCompose = "4.1.1"
koinComposeViewmodel = "4.1.1" koinComposeViewmodel = "4.1.1"
koinCore = "4.1.1" koinCore = "4.1.1"
kotlin = "2.3.0" kotlin = "2.3.10"
coreKtx = "1.17.0" coreKtx = "1.17.0"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.3.0" junitVersion = "1.3.0"
espressoCore = "3.7.0" espressoCore = "3.7.0"
kotlinxSerializationJson = "1.9.0" kotlinxSerializationJson = "1.10.0"
lifecycleRuntimeKtx = "2.10.0" lifecycleRuntimeKtx = "2.10.0"
composeBom = "2025.12.01" composeBom = "2026.01.01"
appcompat = "1.7.1" appcompat = "1.7.1"
material = "1.13.0" material = "1.13.0"
carApp = "1.7.0" carApp = "1.7.0"
androidx-car = "1.7.0" androidx-car = "1.7.0"
objectboxKotlin = "5.0.1" objectboxKotlin = "5.1.0"
objectboxProcessor = "5.0.1" objectboxProcessor = "5.0.1"
ui = "1.10.0" ui = "1.10.0"
material3 = "1.4.0" material3 = "1.4.0"
runtimeLivedata = "1.10.0" runtimeLivedata = "1.10.2"
foundation = "1.10.0" foundation = "1.10.0"
maplibre-composeMaterial3 = "0.12.2" maplibre-composeMaterial3 = "0.12.2"
maplibre-compose = "0.12.1" maplibre-compose = "0.12.1"
playServicesLocation = "21.3.0" playServicesLocation = "21.3.0"
runtime = "1.10.0" runtime = "1.10.2"
accompanist = "0.37.3" accompanist = "0.37.3"
uiVersion = "1.10.0" uiVersion = "1.10.2"
uiText = "1.10.0" uiText = "1.10.2"
navigationCompose = "2.9.6" navigationCompose = "2.9.7"
uiToolingPreview = "1.10.0" uiToolingPreview = "1.10.2"
uiTooling = "1.10.0" uiTooling = "1.10.2"
material3WindowSizeClass = "1.4.0" material3WindowSizeClass = "1.4.0"
uiGraphics = "1.10.0" uiGraphics = "1.10.2"
window = "1.5.1" window = "1.5.1"
foundationLayout = "1.10.0" foundationLayout = "1.10.2"
foundationLayoutVersion = "1.10.1" foundationLayoutVersion = "1.10.2"
[libraries] [libraries]