TomTom Routing

This commit is contained in:
Dimitris
2026-02-07 12:56:45 +01:00
parent eac5b56bcb
commit 0d51c6121d
50 changed files with 8923 additions and 5084 deletions

View File

@@ -58,7 +58,7 @@ dependencies {
implementation(libs.kotlinx.serialization.json)
implementation(libs.maplibre.compose)
implementation("androidx.compose.material:material-icons-extended:1.7.8")
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)

View File

@@ -29,11 +29,11 @@ import java.net.URL
abstract class NavigationRepository {
//private val nominatimUrl = "https://nominatim.openstreetmap.org/"
private val nominatimUrl = "https://nominatim.openstreetmap.org/"
private val nominatimUrl = "https://kouros-online.de/nominatim/"
//private val nominatimUrl = "https://kouros-online.de/nominatim/"
private val tomtomApiKey = "678k5v6940cSXXIS5oD92qIrDgW3RBZ3"
val tomtomApiKey = "678k5v6940cSXXIS5oD92qIrDgW3RBZ3"
private val tomtomUrl = "https://api.tomtom.com/traffic/services/5/incidentDetails"
@@ -55,18 +55,19 @@ abstract class NavigationRepository {
searchFilter: SearchFilter,
context: Context
): Double {
val route = getRoute(context, currentLocation, location, carOrientation, searchFilter)
val routeModel = RouteModel()
routeModel.startNavigation(route, context)
return routeModel.curRoute.summary.distance
//val route = getRoute(context, currentLocation, location, carOrientation, searchFilter)
//val routeModel = RouteModel()
//routeModel.startNavigation(route, context)
// return routeModel.curRoute.summary.distance
return 0.0
}
fun searchPlaces(search: String, location: Location): String {
val box = calculateSquareRadius(location.latitude, location.longitude, 20.0)
val box = calculateSquareRadius(location.latitude, location.longitude, 100.0)
val viewbox = "&bounded=1&viewbox=${box}"
return fetchUrl(
"${nominatimUrl}search?q=$search&format=jsonv2&addressdetails=true$viewbox",
false
true
)
}

View File

@@ -22,7 +22,7 @@ data class Route(
val routeEngine: Int,
val routes: List<com.kouros.navigation.data.route.Routes>,
var currentStep: Int = 0,
var currentStepIndex: Int = 0,
) {
data class Builder(
@@ -51,15 +51,15 @@ data class Route(
jsonObject["trip"].toString(),
ValhallaResponse::class.java
)
ValhallaRoute().mapJsonToValhalla(routeJson, this)
ValhallaRoute().mapToRoute(routeJson, this)
}
RouteEngine.OSRM.ordinal -> {
val osrmJson = gson.fromJson(route, OsrmResponse::class.java)
OsrmRoute().mapToOsrm(osrmJson, this)
OsrmRoute().mapToRoute(osrmJson, this)
}
else -> {
val tomtomJson = gson.fromJson(route, TomTomResponse::class.java)
TomTomRoute().mapToOsrm(tomtomJson, this)
TomTomRoute().mapToRoute(tomtomJson, this)
}
}
}
@@ -91,21 +91,23 @@ data class Route(
}
}
fun isRouteValid(): Boolean {
return routes.isNotEmpty() && legs().isNotEmpty()
}
fun currentStep(): Step {
return if (routes.isNotEmpty() && legs().isNotEmpty()) {
legs().first().steps[currentStep]
return if (isRouteValid()) {
legs().first().steps[currentStepIndex]
} else {
Step(maneuver = Maneuver(waypoints = emptyList(), location = location(0.0, 0.0)))
}
}
fun nextStep(): Step {
val nextIndex = currentStep + 1
return if (nextIndex < legs().first().steps.size) {
fun nextStep(steps : Int): Step {
val nextIndex = currentStepIndex + steps
return if (isRouteValid() && nextIndex < legs().first().steps.size) {
legs().first().steps[nextIndex]
} else {
throw IndexOutOfBoundsException("No next maneuver available.")
currentStep()
}
}

View File

@@ -5,7 +5,7 @@ import com.google.gson.annotations.SerializedName
data class Legs (
@SerializedName("steps" ) var steps : ArrayList<Steps> = arrayListOf(),
@SerializedName("steps" ) var steps : List<Steps> = listOf(),
@SerializedName("weight" ) var weight : Double = 0.0,
@SerializedName("summary" ) var summary : String = "",
@SerializedName("duration" ) var duration : Double = 0.0,

View File

@@ -1,6 +1,7 @@
package com.kouros.navigation.data.osrm
import com.kouros.navigation.data.Route
import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.route.Intersection
import com.kouros.navigation.data.route.Lane
import com.kouros.navigation.data.route.Leg
@@ -14,7 +15,7 @@ import com.kouros.navigation.utils.location
class OsrmRoute {
fun mapToOsrm(routeJson: OsrmResponse, builder: Route.Builder) {
fun mapToRoute(routeJson: OsrmResponse, builder: Route.Builder) {
val routes = mutableListOf<com.kouros.navigation.data.route.Routes>()
var stepIndex = 0
@@ -61,7 +62,7 @@ class OsrmRoute {
distance = step.distance / 1000,
duration = step.duration,
maneuver = maneuver,
intersection = intersections
//intersection = intersections
)
steps.add(step)
stepIndex += 1
@@ -80,7 +81,7 @@ class OsrmRoute {
routes.add(newRoute)
}
builder
.routeType(1)
.routeType(RouteEngine.OSRM.ordinal)
.routes(routes)
}

View File

@@ -3,6 +3,6 @@ package com.kouros.navigation.data.route
import java.util.Collections
data class Intersection(
val location: ArrayList<Double> = arrayListOf(0.0, 0.0),
val location: List<Double> = listOf(0.0, 0.0),
val lane : List<Lane> = Collections.emptyList<Lane>(),
)

View File

@@ -1,5 +1,6 @@
package com.kouros.navigation.data.route
data class Leg(
var steps : List<Step> = arrayListOf()
var steps : List<Step> = arrayListOf(),
var intersection: List<Intersection> = arrayListOf()
)

View File

@@ -0,0 +1,5 @@
package com.kouros.navigation.data.tomtom
data class Cause(
val mainCauseCode: Int
)

View File

@@ -0,0 +1,6 @@
package com.kouros.navigation.data.tomtom
data class Guidance(
val instructionGroups: List<InstructionGroup>,
val instructions: List<Instruction>
)

View File

@@ -0,0 +1,21 @@
package com.kouros.navigation.data.tomtom
data class Instruction(
val combinedMessage: String,
val countryCode: String,
val drivingSide: String,
val instructionType: String,
val junctionType: String,
val maneuver: String,
val message: String,
val point: Point,
val pointIndex: Int,
val possibleCombineWithNext: Boolean,
val roadNumbers: List<String>,
val routeOffsetInMeters: Int,
val signpostText: String,
val street: String = "",
val travelTimeInSeconds: Int,
val turnAngleInDecimalDegrees: Int,
val exitNumber: String? = "0",
)

View File

@@ -0,0 +1,8 @@
package com.kouros.navigation.data.tomtom
data class InstructionGroup(
val firstInstructionIndex: Int,
val groupLengthInMeters: Int,
val groupMessage: String,
val lastInstructionIndex: Int
)

View File

@@ -0,0 +1,6 @@
package com.kouros.navigation.data.tomtom
data class Lane(
val directions: List<String>,
val follow: String
)

View File

@@ -0,0 +1,7 @@
package com.kouros.navigation.data.tomtom
data class Leg(
val encodedPolyline: String,
val encodedPolylinePrecision: Int,
val summary: SummaryX
)

View File

@@ -0,0 +1,6 @@
package com.kouros.navigation.data.tomtom
data class Point(
val latitude: Double,
val longitude: Double
)

View File

@@ -1,5 +0,0 @@
package com.kouros.navigation.data.tomtom
data class Report(
val effectiveSettings: List<EffectiveSetting>
)

View File

@@ -0,0 +1,8 @@
package com.kouros.navigation.data.tomtom
data class Route(
val guidance: Guidance,
val legs: List<Leg>,
val sections: List<Section>,
val summary: SummaryX
)

View File

@@ -0,0 +1,16 @@
package com.kouros.navigation.data.tomtom
import java.util.Collections
data class Section(
val delayInSeconds: Int,
val effectiveSpeedInKmh: Int,
val endPointIndex: Int,
val eventId: String,
val laneSeparators: List<String>,
val lanes: List<Lane>? = Collections.emptyList<Lane>(),
val magnitudeOfDelay: Int,
val sectionType: String,
val simpleCategory: String,
val startPointIndex: Int,
)

View File

@@ -0,0 +1,10 @@
package com.kouros.navigation.data.tomtom
data class SummaryX(
val arrivalTime: String,
val departureTime: String,
val lengthInMeters: Int,
val trafficDelayInSeconds: Int,
val trafficLengthInMeters: Int,
val travelTimeInSeconds: Int
)

View File

@@ -0,0 +1,36 @@
package com.kouros.navigation.data.tomtom
import android.content.Context
import android.location.Location
import com.kouros.data.R
import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.SearchFilter
private const val routeUrl = "https://api.tomtom.com/routing/1/calculateRoute/"
class TomTomRepository : NavigationRepository() {
override fun getRoute(
context: Context,
currentLocation: Location,
location: Location,
carOrientation: Float,
searchFilter: SearchFilter
): String {
//val routeJson = context.resources.openRawResource(R.raw.tomom_routing)
//val routeJsonString = routeJson.bufferedReader().use { it.readText() }
//return routeJsonString
val url =
routeUrl + "${currentLocation.latitude},${currentLocation.longitude}:${location.latitude},${location.longitude}" +
"/json?vehicleHeading=90&sectionType=traffic&report=effectiveSettings&routeType=eco" +
"&traffic=true&avoid=unpavedRoads&travelMode=car" +
"&vehicleMaxSpeed=120&vehicleCommercial=false" +
"&instructionsType=text&language=en-GB&sectionType=lanes" +
"&routeRepresentation=encodedPolyline" +
"&vehicleEngineType=combustion&key=$tomtomApiKey"
return fetchUrl(
url,
false
)
}
}

View File

@@ -0,0 +1,6 @@
package com.kouros.navigation.data.tomtom
data class TomTomResponse(
val formatVersion: String,
val routes: List<Route>
)

View File

@@ -1,63 +1,120 @@
package com.kouros.navigation.data.tomtom
import com.kouros.navigation.data.Route
import com.kouros.navigation.data.osrm.OsrmResponse
import com.kouros.navigation.data.osrm.OsrmRoute.ManeuverType
import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.route.Intersection
import com.kouros.navigation.data.route.Lane
import com.kouros.navigation.data.route.Leg
import com.kouros.navigation.data.route.Maneuver
import com.kouros.navigation.data.route.Step
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.location
import com.kouros.navigation.data.route.Maneuver as RouteManeuver
/**
curl -X GET "https://api.tomtom.com/routing/1/calculateRoute/\
48.1856548,11.57928:48.1183,11.59485/json?\
vehicleHeading=90&sectionType=traffic\
&report=effectiveSettings&routeType=eco\
&traffic=true&avoid=unpavedRoadimport com.kouros.navigation.data.route.Maneuver as RouteManeuvers&travelMode=car\
&vehicleMaxSpeed=120&vehicleCommercial=false\
&instructionsType=text&language=en-GB&sectionType=lanes\
&routeRepresentation=encodedPolyline\
&vehicleEngineType=combustion&key=678k5v6940cSXXIS5oD92qIrDgW3RBZ3"
*/
class TomTomRoute {
fun mapToOsrm(routeJson: TomTomResponse, builder: Route.Builder) {
fun mapToRoute(routeJson: TomTomResponse, builder: Route.Builder) {
val routes = mutableListOf<com.kouros.navigation.data.route.Routes>()
routeJson.routes.forEach { route ->
val legs = mutableListOf<Leg>()
val waypoints = mutableListOf<List<Double>>()
val legs = mutableListOf<Leg>()
var stepIndex = 0
var points = listOf<List<Double>>()
val summary = Summary(
route.summary.travelTimeInSeconds.toDouble(),
route.summary.lengthInMeters.toDouble() / 1000
route.summary.lengthInMeters.toDouble()
)
route.legs.forEach { leg ->
points = decodePolyline(leg.encodedPolyline, leg.encodedPolylinePrecision)
waypoints.addAll(points)
}
route.guidance.instructions.forEach { instruction ->
instruction.exitNumber
// val maneuver = RouteManeuver(
// // bearingBefore = step.maneuver.bearingBefore,
// //bearingAfter = step.maneuver.bearingAfter,
// type = convertType(instruction.maneuver),
// waypoints = points.subList(section.startPointIndex, section.endPointIndex + 1),
// exit = instruction.exitNumber.toInt(),
// location = location(
// instruction.point.longitude, instruction.point.latitude
// )
// )
var stepDistance = 0.0
var stepDuration = 0.0
val allIntersections = mutableListOf<Intersection>()
val steps = mutableListOf<Step>()
for (index in 0..< route.guidance.instructions.size) {
val instruction = route.guidance.instructions[index]
val nextPointIndex = nextPointIndex(index, route)
val maneuver = RouteManeuver(
bearingBefore = 0,
bearingAfter = 0,
type = convertType(instruction.maneuver),
waypoints = points.subList(
instruction.pointIndex,
route.guidance.instructions[nextPointIndex].pointIndex
),
exit = exitNumber(instruction),
location = location(
instruction.point.longitude, instruction.point.latitude
),
)
val intersections = mutableListOf<Intersection>()
route.sections.forEach { section ->
val lanes = mutableListOf<Lane>()
var startIndex = 0
if (section.startPointIndex <= instruction.pointIndex - 3
&& instruction.pointIndex <= section.endPointIndex
) {
section.lanes?.forEach { itLane ->
val lane = Lane(
location(
waypoints[section.startPointIndex][0],
waypoints[section.startPointIndex][1]
),
itLane.directions.first() == itLane.follow,
itLane.directions
)
startIndex = section.startPointIndex
lanes.add(lane)
}
intersections.add(Intersection(waypoints[startIndex], lanes))
}
}
allIntersections.addAll(intersections)
stepDistance = route.guidance.instructions[nextPointIndex].routeOffsetInMeters - stepDistance
stepDuration = route.guidance.instructions[nextPointIndex].travelTimeInSeconds - stepDuration
val name = instruction.street
val step = Step(
index = stepIndex,
name = name,
distance = stepDistance,
duration = stepDuration,
maneuver = maneuver,
intersection = intersections
)
stepDistance = route.guidance.instructions[nextPointIndex].routeOffsetInMeters.toDouble()
stepDuration = route.guidance.instructions[nextPointIndex].travelTimeInSeconds.toDouble()
steps.add(step)
stepIndex += 1
}
route.sections.forEach { section ->
}
legs.add(Leg(steps, allIntersections))
val routeGeoJson = createLineStringCollection(waypoints)
val centerLocation = createCenterLocation(createLineStringCollection(waypoints))
val newRoute = com.kouros.navigation.data.route.Routes(
legs,
summary,
routeGeoJson,
centerLocation = centerLocation,
waypoints = waypoints
)
routes.add(newRoute)
}
println(routeJson)
builder
.routeType(RouteEngine.TOMTOM.ordinal)
.routes(routes)
}
private fun nextPointIndex(index: Int, route: com.kouros.navigation.data.tomtom.Route): Int {
val nextPointIndex = if (index < route.guidance.instructions.size - 1) {
index + 1
} else {
index + 0
}
return nextPointIndex
}
fun convertType(type: String): Int {
@@ -66,7 +123,73 @@ class TomTomRoute {
"DEPART" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_DEPART
}
"ARRIVE" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION
}
"ARRIVE_LEFT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION_LEFT
}
"ARRIVE_RIGHT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION_RIGHT
}
"STRAIGHT", "FOLLOW" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_STRAIGHT
}
"KEEP_RIGHT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_KEEP_RIGHT
}
"BEAR_RIGHT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_RIGHT
}
"BEAR_LEFT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_LEFT
}
"KEEP_LEFT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_KEEP_LEFT
}
"TURN_LEFT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_NORMAL_LEFT
}
"TURN_RIGHT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_NORMAL_RIGHT
}
"SHARP_LEFT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_LEFT
}
"SHARP_RIGHT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_RIGHT
}
"ROUNDABOUT_RIGHT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CCW
}
"ROUNDABOUT_LEFT" -> {
newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CW
}
}
return newType
}
}
private fun exitNumber(
instruction: Instruction
): Int {
return if ( instruction.exitNumber == null
|| instruction.exitNumber.isEmpty()) {
0
} else {
instruction.exitNumber.toInt()
}
}

View File

@@ -3,6 +3,7 @@ package com.kouros.navigation.data.valhalla
import androidx.car.app.navigation.model.Maneuver
import com.kouros.data.R
import com.kouros.navigation.data.Route
import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.route.Leg
import com.kouros.navigation.data.route.Maneuver as RouteManeuver
import com.kouros.navigation.data.route.Step
@@ -13,7 +14,7 @@ import com.kouros.navigation.utils.location
class ValhallaRoute {
fun mapJsonToValhalla(routeJson: ValhallaResponse, builder: Route.Builder) {
fun mapToRoute(routeJson: ValhallaResponse, builder: Route.Builder) {
val waypoints = decodePolyline(routeJson.legs[0].shape)
val summary = Summary(routeJson.summaryValhalla.time, routeJson.summaryValhalla.length)
val steps = mutableListOf<Step>()
@@ -38,12 +39,9 @@ class ValhallaRoute {
stepIndex += 1
}
builder
.routeType(1)
.routeType(RouteEngine.VALHALLA.ordinal)
// TODO
.routes(emptyList())
//.summary(summary)
//.routeGeoJson(createLineStringCollection(waypoints))
//.waypoints(waypoints)
}
fun convertType(maneuver: Maneuvers): Int {

View File

@@ -6,6 +6,10 @@ import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Matrix
import android.location.Location
import androidx.annotation.DrawableRes
import androidx.car.app.CarContext
import androidx.car.app.model.CarIcon
import androidx.car.app.navigation.model.LaneDirection
import androidx.car.app.navigation.model.Maneuver
import androidx.car.app.navigation.model.Step
import androidx.core.graphics.createBitmap
@@ -15,6 +19,7 @@ import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.Route
import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.StepData
import com.kouros.navigation.data.route.Intersection
import com.kouros.navigation.data.route.Lane
@@ -30,6 +35,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.util.Collections
import java.util.Locale
import java.util.concurrent.TimeUnit
import kotlin.math.absoluteValue
import kotlin.math.roundToInt
@@ -45,9 +51,10 @@ open class RouteModel() {
var lastSpeedLocation: Location = location(0.0, 0.0)
var lastSpeedIndex: Int = 0
var maxSpeed: Int = 0
var location: Location = location(0.0, 0.0)
var lastLocation: Location = location(0.0, 0.0)
var bearing: Float = 0F
var routeBearing: Float = 0F
var currentRouteIndex = 0
val curRoute: Routes
@@ -62,7 +69,13 @@ open class RouteModel() {
.routeEngine(routeEngine)
.route(routeString)
.build()
navigating = true
if (hasLegs()) {
navigating = true
}
}
private fun hasLegs(): Boolean {
return route.routes.isNotEmpty() && route.routes[0].legs.isNotEmpty()
}
fun stopNavigation() {
@@ -76,24 +89,24 @@ open class RouteModel() {
@OptIn(DelicateCoroutinesApi::class)
fun updateLocation(curLocation: Location, viewModel: ViewModel) {
location = curLocation
findStep(location)
updateSpeedLimit(location, viewModel)
findStep(curLocation)
updateSpeedLimit(curLocation, viewModel)
lastLocation = location
}
private fun findStep(location: Location) {
var nearestDistance = 100000.0f
var nearestDistance = 100000f
for ((index, step) in curLeg.steps.withIndex()) {
if (index >= route.currentStep) {
if (index >= route.currentStepIndex) {
for ((wayIndex, waypoint) in step.maneuver.waypoints.withIndex()) {
if (wayIndex >= step.waypointIndex) {
val distance = location.distanceTo(location(waypoint[0], waypoint[1]))
if (distance < nearestDistance) {
nearestDistance = distance
route.currentStep = step.index
route.currentStepIndex = step.index
step.waypointIndex = wayIndex
step.wayPointLocation = location(waypoint[0], waypoint[1])
lastLocation = location
bearing = lastLocation.bearingTo(location)
routeBearing = lastLocation.bearingTo(location)
}
}
if (nearestDistance == 0F) {
@@ -108,6 +121,21 @@ open class RouteModel() {
}
private fun currentLanes(location: Location): List<Lane> {
var lanes = emptyList<Lane>()
if (route.legs().isNotEmpty()) {
route.legs().first().intersection.forEach { it ->
if (it.lane.isNotEmpty()) {
val distance = lastLocation.distanceTo(location(it.location[0], it.location[1]))
val sectionBearing =
lastLocation.bearingTo(location(it.location[0], it.location[1]))
if (distance < 500 && (routeBearing.absoluteValue - sectionBearing.absoluteValue).absoluteValue < 10) {
lanes = it.lane
}
}
}
}
return lanes
var inter = Intersection()
var nearestDistance = 100000.0f
route.currentStep().intersection.forEach {
@@ -117,7 +145,9 @@ open class RouteModel() {
if (distance < nearestDistance) {
nearestDistance = distance
if (distance <= NEXT_STEP_THRESHOLD * 3) {
if ((interBearing.absoluteValue - route.currentStep().maneuver.bearingAfter.absoluteValue).absoluteValue < 20) {
if (route.routeEngine == RouteEngine.TOMTOM.ordinal
|| (interBearing.absoluteValue - route.currentStep().maneuver.bearingAfter.absoluteValue).absoluteValue < 20
) {
inter = it
}
}
@@ -129,22 +159,24 @@ open class RouteModel() {
fun updateSpeedLimit(location: Location, viewModel: ViewModel) = runBlocking {
CoroutineScope(Dispatchers.IO).launch {
val instruction = currentStep().instruction
val levenshtein = Levenshtein()
// speed limit
val distance = lastSpeedLocation.distanceTo(location)
if (distance > 500 || lastSpeedIndex < route.currentStep) {
lastSpeedIndex = route.currentStep
val elements = viewModel.getMaxSpeed(location)
elements.forEach {
if (it.tags.name != null) {
if (isNavigating()) {
val distance =
levenshtein.distance(it.tags.name!!, instruction)
if (distance < 5) {
val speed = it.tags.maxspeed.toInt()
maxSpeed = speed
lastSpeedLocation = location
if (isNavigating()) {
val instruction = currentStep().instruction
val levenshtein = Levenshtein()
// speed limit
val distance = lastSpeedLocation.distanceTo(location)
if (distance > 500 || lastSpeedIndex < route.currentStepIndex) {
lastSpeedIndex = route.currentStepIndex
val elements = viewModel.getMaxSpeed(location)
elements.forEach {
if (it.tags.name != null) {
if (isNavigating()) {
val distance =
levenshtein.distance(it.tags.name!!, instruction)
if (distance < 5) {
val speed = it.tags.maxspeed.toInt()
maxSpeed = speed
lastSpeedLocation = location
}
}
}
}
@@ -154,32 +186,13 @@ open class RouteModel() {
}
fun currentStep(): StepData {
val currentStep = route.currentStep()
// Determine if we should display the current or the next maneuver
val distanceToNextStep = leftStepDistance()
val isNearNextManeuver = distanceToNextStep in 0.0..NEXT_STEP_THRESHOLD
val shouldAdvance =
isNearNextManeuver && route.currentStep < (route.legs().first().steps.size)
// Determine the maneuver type and corresponding icon
var curManeuverType = if (hasArrived(currentStep.maneuver.type)) {
currentStep.maneuver.type
} else {
Maneuver.TYPE_STRAIGHT
}
// Get the single, correct maneuver for this state
val relevantStep = if (shouldAdvance) {
route.nextStep() // This advances the route's state
} else {
route.currentStep()
}
val currentStep = route.nextStep(1) // This advances the route's state
// Safely get the street name from the maneuver
val streetName = relevantStep.name
var exitNumber = currentStep.maneuver.exit
if (shouldAdvance) {
curManeuverType = relevantStep.maneuver.type
exitNumber = relevantStep.maneuver.exit
}
val streetName = currentStep.name
val curManeuverType = currentStep.maneuver.type
val exitNumber = currentStep.maneuver.exit
val maneuverIcon = maneuverIcon(curManeuverType)
maneuverType = curManeuverType
@@ -198,9 +211,8 @@ open class RouteModel() {
)
}
fun nextStep(): StepData {
val step = route.nextStep()
val step = route.nextStep(2)
val maneuverType = step.maneuver.type
val distanceLeft = leftStepDistance()
var text = ""
@@ -226,14 +238,13 @@ open class RouteModel() {
travelLeftDistance(),
listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList())),
step.maneuver.exit
)
}
fun travelLeftTime(): Double {
var timeLeft = 0.0
// time for next step until end step
for (i in route.currentStep + 1..<curLeg.steps.size) {
for (i in route.currentStepIndex + 1..<curLeg.steps.size) {
val step = curLeg.steps[i]
timeLeft += step.duration
}
@@ -270,14 +281,14 @@ open class RouteModel() {
return (leftDistance / 10.0).roundToInt() * 10.0
}
/** Returns the left distance in km. */
/** Returns the left distance in m. */
fun travelLeftDistance(): Double {
var leftDistance = 0.0
for (i in route.currentStep + 1..<curLeg.steps.size) {
for (i in route.currentStepIndex + 1..<curLeg.steps.size) {
val step = route.legs()[0].steps[i]
leftDistance += step.distance
}
leftDistance += leftStepDistance() / 1000
leftDistance += leftStepDistance()
return leftDistance
}
@@ -343,6 +354,100 @@ open class RouteModel() {
|| type == ManeuverType.DestinationLeft.value
}
fun addLanes(stepData: StepData) {
stepData.lane.forEach {
if (it.indications.isNotEmpty() && it.valid) {
Collections.sort<String>(it.indications)
var direction = ""
it.indications.forEach { it2 ->
direction = if (direction.isEmpty()) {
it2.trim()
} else {
"${direction}_${it2.trim()}"
}
}
val laneDirection = addLanes(direction, stepData)
println(laneDirection)
// TODO:
}
}
}
fun addLanes(direction: String, stepData: StepData): Int {
val laneDirection = when (direction.lowercase(Locale.getDefault())) {
"left_straight" -> {
when (stepData.currentManeuverType) {
Maneuver.TYPE_TURN_NORMAL_LEFT -> LaneDirection.SHAPE_NORMAL_LEFT
Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT
else
-> LaneDirection.SHAPE_UNKNOWN
}
}
"left", "slight_left" -> {
when (stepData.currentManeuverType) {
Maneuver.TYPE_TURN_NORMAL_LEFT -> LaneDirection.SHAPE_NORMAL_LEFT
else
-> LaneDirection.SHAPE_UNKNOWN
}
}
"straight" -> {
when (stepData.currentManeuverType) {
Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT
else
-> LaneDirection.SHAPE_UNKNOWN
}
}
"right" -> {
when (stepData.currentManeuverType) {
Maneuver.TYPE_TURN_NORMAL_RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT
else
-> LaneDirection.SHAPE_UNKNOWN
}
}
"right_straight" -> {
when (stepData.currentManeuverType) {
Maneuver.TYPE_TURN_NORMAL_RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT
Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT
else
-> LaneDirection.SHAPE_UNKNOWN
}
}
"left_slight", "slight_left" -> {
when (stepData.currentManeuverType) {
Maneuver.TYPE_TURN_SLIGHT_LEFT -> LaneDirection.SHAPE_SLIGHT_LEFT
else
-> LaneDirection.SHAPE_UNKNOWN
}
}
"right_slight", "slight_right" -> {
when (stepData.currentManeuverType) {
Maneuver.TYPE_TURN_SLIGHT_RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT
else
-> LaneDirection.SHAPE_UNKNOWN
}
}
else -> {
LaneDirection.SHAPE_UNKNOWN
}
}
return laneDirection
}
fun createCarIcon(iconCompat: IconCompat): CarIcon {
return CarIcon.Builder(iconCompat).build()
}
fun createCarIconx(carContext: Context, @DrawableRes iconRes: Int): CarIcon {
return CarIcon.Builder(IconCompat.createWithResource(carContext, iconRes)).build()
}
fun createLaneIcon(context: Context, stepData: StepData): IconCompat {
val bitmaps = mutableListOf<Bitmap>()
@@ -396,6 +501,7 @@ open class RouteModel() {
"${direction}_${it.trim()}"
}
}
direction = direction.lowercase()
return when (direction) {
"left_straight" -> {
when (stepData.currentManeuverType) {
@@ -419,8 +525,8 @@ open class RouteModel() {
"right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT) "${direction}_o" else "${direction}_x"
"left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT) "${direction}_o" else "${direction}_x"
"straight" -> if (stepData.currentManeuverType == Maneuver.TYPE_STRAIGHT) "${direction}_o" else "${direction}_x"
"right_slight" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_RIGHT) "${direction}_o" else "${direction}_x"
"left_slight" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_LEFT) "${direction}_o" else "${direction}_x"
"right_slight", "slight_right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_RIGHT) "${direction}_o" else "${direction}_x"
"left_slight", "slight_left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_LEFT) "${direction}_o" else "${direction}_x"
else -> {
""
}
@@ -438,6 +544,7 @@ open class RouteModel() {
"right_o" -> R.drawable.right_o
"slight_right_x" -> R.drawable.slight_right_x
"slight_right_o" -> R.drawable.slight_right_o
"slight_left_x" -> R.drawable.left_x
"straight_x" -> R.drawable.straight_x
"right_o_straight_x" -> R.drawable.right_o_straight_x
"right_x_straight_x" -> R.drawable.right_x_straight_x
@@ -446,7 +553,7 @@ open class RouteModel() {
"left_o_straight_x" -> R.drawable.left_o_straight_x
"left_x_straight_o" -> R.drawable.left_x_straight_o
else -> {
R.drawable.ic_close_white_24dp
R.drawable.left_x
}
}
}

View File

@@ -280,18 +280,20 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
fun searchPlaces(search: String, location: Location) {
viewModelScope.launch(Dispatchers.IO) {
val placesJson = repository.searchPlaces(search, location)
val gson = GsonBuilder().serializeNulls().create()
val places = gson.fromJson(placesJson, Search::class.java)
val distPlaces = mutableListOf<SearchResult>()
places.forEach {
val plLocation =
location(longitude = it.lon.toDouble(), latitude = it.lat.toDouble())
val distance = plLocation.distanceTo(location)
it.distance = distance
distPlaces.add(it)
if (placesJson.isNotEmpty()) {
val gson = GsonBuilder().serializeNulls().create()
val places = gson.fromJson(placesJson, Search::class.java)
val distPlaces = mutableListOf<SearchResult>()
places.forEach {
val plLocation =
location(longitude = it.lon.toDouble(), latitude = it.lat.toDouble())
val distance = plLocation.distanceTo(location)
it.distance = distance
distPlaces.add(it)
}
val sortedList = distPlaces.sortedWith(compareBy { it.distance })
searchPlaces.postValue(sortedList)
}
val sortedList = distPlaces.sortedWith(compareBy { it.distance })
searchPlaces.postValue(sortedList)
}
}

View File

@@ -90,6 +90,7 @@ object GeoUtils {
}
fun createLineStringCollection(lineCoordinates: List<List<Double>>): String {
// return createPointCollection(lineCoordinates, "Route")
val lineString = buildLineString {
lineCoordinates.forEach {
add(org.maplibre.spatialk.geojson.Point(

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M313,520L537,744L480,800L160,480L480,160L537,216L313,440L800,440L800,520L313,520Z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M120,720L120,640L840,640L840,720L120,720ZM120,520L120,440L840,440L840,520L120,520ZM120,320L120,240L840,240L840,320L120,320Z"/>
</vector>

View File

@@ -24,7 +24,7 @@
},
{
"key": "departAt",
"value": "2026-01-29T08:43:35.397Z"
"value": "2026-02-06T09:09:59.054Z"
},
{
"key": "guidanceVersion",
@@ -44,7 +44,7 @@
},
{
"key": "locations",
"value": "48.18565,11.57928:48.11830,11.59485"
"value": "48.18575,11.57939:48.11654,11.59449"
},
{
"key": "maxAlternatives",
@@ -60,11 +60,11 @@
},
{
"key": "sectionType",
"value": "lanes"
"value": "traffic"
},
{
"key": "sectionType",
"value": "traffic"
"value": "lanes"
},
{
"key": "traffic",
@@ -119,46 +119,28 @@
"routes": [
{
"summary": {
"lengthInMeters": 10879,
"travelTimeInSeconds": 1170,
"trafficDelayInSeconds": 76,
"trafficLengthInMeters": 1727,
"departureTime": "2026-01-29T09:43:35+01:00",
"arrivalTime": "2026-01-29T10:03:05+01:00"
"lengthInMeters": 11116,
"travelTimeInSeconds": 1148,
"trafficDelayInSeconds": 0,
"trafficLengthInMeters": 0,
"departureTime": "2026-02-06T10:09:59+01:00",
"arrivalTime": "2026-02-06T10:29:07+01:00"
},
"legs": [
{
"summary": {
"lengthInMeters": 10879,
"travelTimeInSeconds": 1170,
"trafficDelayInSeconds": 76,
"trafficLengthInMeters": 1727,
"departureTime": "2026-01-29T09:43:35+01:00",
"arrivalTime": "2026-01-29T10:03:05+01:00"
"lengthInMeters": 11116,
"travelTimeInSeconds": 1148,
"trafficDelayInSeconds": 0,
"trafficLengthInMeters": 0,
"departureTime": "2026-02-06T10:09:59+01:00",
"arrivalTime": "2026-02-06T10:29:07+01:00"
},
"encodedPolyline": "sfbeHmqteAEjDQEy@GQ?wDQFkEH{G?M?sA@kB?_FAeC?o@?[@_@\\Ab@CVAz@CJA@?dBGhAGjAExAGlBEvBKdCKTAfCKLAv@ELA|AGnAGt@ClCKjDQpBIf@BXDPDPBF@ZB?S@SAYAUKi@Go@Cc@BgBBs@Bg@JyAJiAXqBDWNs@TaA\\mAFa@Po@`BwF?YL]FSFSl@iB^kALc@L]Ro@f@yAf@{AFQNe@dAoCdBgEx@qBTi@BITe@L[L_@^}@HSXu@pB}El@eAb@e@f@[h@QZCRAL?j@HRFh@Vf@XLJhAn@lAv@TLtAz@r@`@bAn@pAv@f@X~@j@z@h@vBpA`@VHDFFJFf@X`CzApAh@f@L`@Fz@@f@AXEVEVEhA[h@Yn@e@PQFEJKRWV[PW`@w@t@}AHQN]~BiFP]`AoBh@aADGTa@t@aAt@{@PQJKJGFG@Cd@]XSxDmCf@a@n@o@TY\\g@LQHMJSLUP[f@iAPg@b@yAFODMNi@FS|@qCVaAHUHUn@wBHYh@eBpAkEjBiGfAeDj@yADMFQd@sAf@kAJUh@qAf@eAt@sAn@iALSN[p@kAVc@JOLSj@w@z@}@x@q@pAu@p@_@j@Sl@MLCRCb@E`@?^?L@`ABz@?N@~AFdADJ@rAH`DVpCVrAJd@BfHp@zGl@pAJ|ALnGp@jEh@fBJpAFF?P@N@ZCtC]r@GJCFCLCD?TEVEXGhAYzAg@NGv@]`@QJEvB_AXMVK\\Qb@Qn@QJCNAZC^ENA`@FnBb@xEtA^H^JnCl@z@r@`@Pr@TtBlA~C`Bn@\\xAl@PF`@LrAVlCh@bBLl@BlBJdG\\RDjCHn@?pB?xB?R@`@@GxAC^?ZInBIfCAvC?r@@dD@n@@b@@^D`C?TDxAFbBHdB@VHp@RjAJb@NNH`@VlBFf@PzARhBFd@@LRbBFh@\\nC@FNhAb@lEj@tDPpABTBRZlBTdBXjBn@xEBLDTRpAR~@Fb@",
"encodedPolyline": "sfbeHarteAE~DQEy@GQ?wDQFkEH{G?M?sA@kB?_FAeC?o@?[@_@\\Ab@CVAz@CJA@?dBGhAGjAExAGlBEvBKdCKTAfCKLAv@ELA|AGnAGt@ClCKjDQpBIf@BXDPDPBF@ZB?S@SAYAUKi@Go@Cc@BgBBs@Bg@JyAJiAXqBDWNs@TaA\\mAFa@Po@`BwF?YL]FSFSl@iB^kALc@L]Ro@f@yAf@{AFQNe@dAoCdBgEx@qBTi@BITe@L[L_@^}@HSXu@pB}El@eAb@e@f@[h@QZCRAL?j@HRFh@Vf@XLJhAn@lAv@TLtAz@r@`@bAn@pAv@f@X~@j@z@h@vBpA`@VHDFFJFf@X`CzApAh@f@L`@Fz@@f@AXEVEVEhA[h@Yn@e@PQFEJKRWV[PW`@w@t@}AHQN]~BiFP]`AoBh@aADGTa@t@aAt@{@PQJKJGFG@Cd@]XSxDmCf@a@n@o@TY\\g@LQHMJSLUP[f@iAPg@b@yAFODMNi@FS|@qCVaAHUHUn@wBHYh@eBpAkEjBiGfAeDj@yADMFQd@sAf@kAJUh@qAf@eAt@sAn@iALSN[p@kAVc@JOLSj@w@z@}@x@q@pAu@p@_@j@Sl@MLCRCb@E`@?^?L@`ABz@?N@~AFdADJ@rAH`DVpCVrAJd@BfHp@zGl@pAJ|ALnGp@jEh@fBJpAFF?P@N@ZCtC]r@GJCFCLCD?TEVEXGhAYzAg@NGv@]`@QJEvB_AXMVK\\Qb@Qn@QJCNAZC^ENA`@FnBb@xEtA^H^JnCl@z@r@`@Pr@TtBlA~C`Bn@\\xAl@PF`@LrAVlCh@bBLl@BlBJdG\\RDjCHn@?pB?xB?R@`@@GxAC^?ZInBIfCAvC?r@@dD@n@@b@@^D`C?TDxAFbBHdB@VHp@RjAJb@NNH`@VlBFf@PzARhBFd@@LRbBFh@\\nC@FNhAb@lEj@tDPpABTBRZlBTdBXjBn@xEBLDTRpAR~@l@jDj@Qv@IrEP",
"encodedPolylinePrecision": 5
}
],
"sections": [
{
"startPointIndex": 83,
"endPointIndex": 147,
"sectionType": "TRAFFIC",
"simpleCategory": "JAM",
"effectiveSpeedInKmh": 35,
"delayInSeconds": 76,
"magnitudeOfDelay": 1,
"tec": {
"causes": [
{
"mainCauseCode": 1
}
],
"effectCode": 4
},
"eventId": "TTL41048054144049000"
},
{
"lanes": [
{
@@ -349,9 +331,8 @@
"travelTimeInSeconds": 0,
"point": {
"latitude": 48.18554,
"longitude": 11.57927
"longitude": 11.57937
},
"exitNumber": "",
"pointIndex": 0,
"instructionType": "LOCATION_DEPARTURE",
"street": "Vogelhartstraße",
@@ -362,8 +343,8 @@
"message": "Leave from Vogelhartstraße"
},
{
"routeOffsetInMeters": 64,
"travelTimeInSeconds": 14,
"routeOffsetInMeters": 72,
"travelTimeInSeconds": 16,
"point": {
"latitude": 48.18557,
"longitude": 11.57841
@@ -380,8 +361,8 @@
"message": "Turn right onto Silcherstraße"
},
{
"routeOffsetInMeters": 218,
"travelTimeInSeconds": 57,
"routeOffsetInMeters": 226,
"travelTimeInSeconds": 60,
"point": {
"latitude": 48.18696,
"longitude": 11.57857
@@ -398,8 +379,8 @@
"message": "Turn right onto Schmalkaldener Straße"
},
{
"routeOffsetInMeters": 650,
"travelTimeInSeconds": 131,
"routeOffsetInMeters": 658,
"travelTimeInSeconds": 134,
"point": {
"latitude": 48.18686,
"longitude": 11.58437
@@ -419,8 +400,8 @@
"message": "Turn right onto Ingolstädter Straße/B13"
},
{
"routeOffsetInMeters": 1713,
"travelTimeInSeconds": 266,
"routeOffsetInMeters": 1720,
"travelTimeInSeconds": 267,
"point": {
"latitude": 48.17733,
"longitude": 11.58503
@@ -441,8 +422,8 @@
"combinedMessage": "Turn left onto Schenkendorfstraße/B2R then keep left at Schenkendorfstraße/B2R toward Messe / ICM"
},
{
"routeOffsetInMeters": 2067,
"travelTimeInSeconds": 309,
"routeOffsetInMeters": 2075,
"travelTimeInSeconds": 307,
"point": {
"latitude": 48.17678,
"longitude": 11.58957
@@ -464,8 +445,8 @@
"combinedMessage": "Keep left at Schenkendorfstraße/B2R toward Messe / ICM then keep left at Schenkendorfstraße/B2R toward Passau"
},
{
"routeOffsetInMeters": 2419,
"travelTimeInSeconds": 332,
"routeOffsetInMeters": 2426,
"travelTimeInSeconds": 329,
"point": {
"latitude": 48.17518,
"longitude": 11.59363
@@ -486,8 +467,8 @@
"message": "Keep left at Schenkendorfstraße/B2R toward Passau"
},
{
"routeOffsetInMeters": 2774,
"travelTimeInSeconds": 357,
"routeOffsetInMeters": 2781,
"travelTimeInSeconds": 353,
"point": {
"latitude": 48.17329,
"longitude": 11.59747
@@ -506,8 +487,8 @@
"message": "Follow Isarring/B2R toward München-Ost"
},
{
"routeOffsetInMeters": 8425,
"travelTimeInSeconds": 806,
"routeOffsetInMeters": 8433,
"travelTimeInSeconds": 734,
"point": {
"latitude": 48.13017,
"longitude": 11.61541
@@ -524,8 +505,8 @@
"message": "Bear right at Ampfingstraße"
},
{
"routeOffsetInMeters": 9487,
"travelTimeInSeconds": 953,
"routeOffsetInMeters": 9495,
"travelTimeInSeconds": 884,
"point": {
"latitude": 48.12089,
"longitude": 11.61285
@@ -543,8 +524,8 @@
"combinedMessage": "Turn right onto Anzinger Straße then keep straight on at Sankt-Martin-Straße"
},
{
"routeOffsetInMeters": 9983,
"travelTimeInSeconds": 1044,
"routeOffsetInMeters": 9991,
"travelTimeInSeconds": 974,
"point": {
"latitude": 48.12087,
"longitude": 11.60621
@@ -561,20 +542,39 @@
"message": "Keep straight on at Sankt-Martin-Straße"
},
{
"routeOffsetInMeters": 10879,
"travelTimeInSeconds": 1170,
"routeOffsetInMeters": 10941,
"travelTimeInSeconds": 1103,
"point": {
"latitude": 48.1183,
"longitude": 11.59485
"latitude": 48.11811,
"longitude": 11.59417
},
"pointIndex": 335,
"instructionType": "TURN",
"street": "Hohenwaldeckstraße",
"countryCode": "DEU",
"junctionType": "REGULAR",
"turnAngleInDecimalDegrees": -90,
"possibleCombineWithNext": true,
"drivingSide": "RIGHT",
"maneuver": "TURN_LEFT",
"message": "Turn left onto Hohenwaldeckstraße",
"combinedMessage": "Turn left onto Hohenwaldeckstraße then you have arrived at Hohenwaldeckstraße. Your destination is on the left"
},
{
"routeOffsetInMeters": 11116,
"travelTimeInSeconds": 1148,
"point": {
"latitude": 48.11655,
"longitude": 11.59422
},
"pointIndex": 338,
"instructionType": "LOCATION_ARRIVAL",
"street": "Sankt-Martin-Straße",
"street": "Hohenwaldeckstraße",
"countryCode": "DEU",
"possibleCombineWithNext": false,
"drivingSide": "RIGHT",
"maneuver": "ARRIVE",
"message": "You have arrived at Sankt-Martin-Straße"
"maneuver": "ARRIVE_LEFT",
"message": "You have arrived at Hohenwaldeckstraße. Your destination is on the left"
}
],
"instructionGroups": [
@@ -582,19 +582,25 @@
"firstInstructionIndex": 0,
"lastInstructionIndex": 3,
"groupMessage": "Leave from Vogelhartstraße. Take the Ingolstädter Straße/B13",
"groupLengthInMeters": 1713
"groupLengthInMeters": 1720
},
{
"firstInstructionIndex": 4,
"lastInstructionIndex": 7,
"groupMessage": "Take the Schenkendorfstraße, Isarring/B2R toward Messe / ICM, Passau, München-Ost",
"groupLengthInMeters": 6712
"groupLengthInMeters": 6713
},
{
"firstInstructionIndex": 8,
"lastInstructionIndex": 11,
"groupMessage": "Take the Ampfingstraße, Anzinger Straße. Continue to your destination at Sankt-Martin-Straße",
"groupLengthInMeters": 2454
"lastInstructionIndex": 10,
"groupMessage": "Take the Ampfingstraße, Anzinger Straße, Sankt-Martin-Straße",
"groupLengthInMeters": 2508
},
{
"firstInstructionIndex": 11,
"lastInstructionIndex": 12,
"groupMessage": "Get to your destination at Hohenwaldeckstraße",
"groupLengthInMeters": 175
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -27,7 +27,7 @@
<string name="reject_action_title" msgid="6730366705938402668">"Ablehnen"</string>
<string name="ok_action_title" msgid="7128494973966098611">"OK"</string>
<string name="favorites" msgid="522064494016370117">"Favoriten"</string>
<string name="display_settings" msgid="5726880972110281095">"Einstellungen für die Anzeige"</string>
<string name="display_settings" msgid="5726880972110281095">"Anzeige"</string>
<string name="arrived_exclamation_msg" msgid="9132265698563096988">"Angekommen!"</string>
<string name="category_title" msgid="4783851267093259949">"Kategorien"</string>
<string name="no_places" msgid="7246005909846715898">"Keine Orte"</string>
@@ -42,12 +42,13 @@
<string name="contacts">Kontakte</string>
<string name="route_preview">Route Vorschau</string>
<string name="display">Anzeige</string>
<string name="navigation_settings">Navigations Einstellungen</string>
<string name="navigation_settings">Navigation</string>
<string name="fuel_station">Tankstelle</string>
<string name="pharmacy">Apotheke</string>
<string name="charging_station">Ladestation</string>
<string name="speed_camera">Speed camera</string>
<string name="use_car_location">Auto GPS verwenden</string>
<string name="tomtom">TomTom\t</string>
<string name="options">Optionen</string>
</resources>

View File

@@ -10,7 +10,7 @@
<string name="off_action_title">Off</string>
<string name="use_telephon_settings">Use telephon settings</string>
<string name="dark_mode">Dark mode</string>
<string name="display_settings">Display settings</string>
<string name="display_settings">Display</string>
<string name="threed_building">3D building</string>
<string name="arrived_exclamation_msg">Arrived!</string>
<string name="drive_now">Drive now</string>
@@ -24,7 +24,7 @@
<string name="recent_Item_deleted">Recent item deleted</string>
<string name="route_preview">Route preview</string>
<string name="display">Display</string>
<string name="navigation_settings">Navigation settings</string>
<string name="navigation_settings">Navigation</string>
<string name="settings_action_title">Settings</string>
<string name="accept_action_title">Accept</string>
<string name="reject_action_title">Reject</string>
@@ -35,4 +35,5 @@
<string name="routing_engine" translatable="false">Routing engine</string>
<string name="use_car_location">Use car location</string>
<string name="tomtom">TomTom\t</string>
<string name="options">Options</string>
</resources>