TomTom Routing
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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>(),
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.kouros.navigation.data.tomtom
|
||||
|
||||
data class Cause(
|
||||
val mainCauseCode: Int
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.kouros.navigation.data.tomtom
|
||||
|
||||
data class Guidance(
|
||||
val instructionGroups: List<InstructionGroup>,
|
||||
val instructions: List<Instruction>
|
||||
)
|
||||
@@ -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",
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.kouros.navigation.data.tomtom
|
||||
|
||||
data class Lane(
|
||||
val directions: List<String>,
|
||||
val follow: String
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.kouros.navigation.data.tomtom
|
||||
|
||||
data class Leg(
|
||||
val encodedPolyline: String,
|
||||
val encodedPolylinePrecision: Int,
|
||||
val summary: SummaryX
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.kouros.navigation.data.tomtom
|
||||
|
||||
data class Point(
|
||||
val latitude: Double,
|
||||
val longitude: Double
|
||||
)
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.kouros.navigation.data.tomtom
|
||||
|
||||
data class Report(
|
||||
val effectiveSettings: List<EffectiveSetting>
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
@@ -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§ionType=traffic&report=effectiveSettings&routeType=eco" +
|
||||
"&traffic=true&avoid=unpavedRoads&travelMode=car" +
|
||||
"&vehicleMaxSpeed=120&vehicleCommercial=false" +
|
||||
"&instructionsType=text&language=en-GB§ionType=lanes" +
|
||||
"&routeRepresentation=encodedPolyline" +
|
||||
"&vehicleEngineType=combustion&key=$tomtomApiKey"
|
||||
return fetchUrl(
|
||||
url,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.kouros.navigation.data.tomtom
|
||||
|
||||
data class TomTomResponse(
|
||||
val formatVersion: String,
|
||||
val routes: List<Route>
|
||||
)
|
||||
@@ -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§ionType=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§ionType=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()
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
11
common/data/src/main/res/drawable/arrow_back_24px.xml
Normal file
11
common/data/src/main/res/drawable/arrow_back_24px.xml
Normal 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>
|
||||
10
common/data/src/main/res/drawable/menu_24px.xml
Normal file
10
common/data/src/main/res/drawable/menu_24px.xml
Normal 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>
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user