TomTom Routing
This commit is contained in:
@@ -88,7 +88,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val routeModel = RouteModel()
|
val routeModel = RouteModel()
|
||||||
var tilt = 50.0
|
var tilt = 50.0
|
||||||
val useMock = true
|
val useMock = true
|
||||||
val type = 3 // simulate 2 test 3 gpx
|
val type = 1 // simulate 2 test 3 gpx
|
||||||
|
|
||||||
var currentIndex = 0
|
var currentIndex = 0
|
||||||
val stepData: MutableLiveData<StepData> by lazy {
|
val stepData: MutableLiveData<StepData> by lazy {
|
||||||
@@ -309,20 +309,16 @@ class MainActivity : ComponentActivity() {
|
|||||||
&& lastLocation.longitude != location.position.longitude
|
&& lastLocation.longitude != location.position.longitude
|
||||||
) {
|
) {
|
||||||
val currentLocation = location(location.position.longitude, location.position.latitude)
|
val currentLocation = location(location.position.longitude, location.position.latitude)
|
||||||
// if (currentIndex == 0)
|
|
||||||
// navigationViewModel.loadTraffic(applicationContext, currentLocation, 0f)
|
|
||||||
// currentIndex = 1
|
|
||||||
val bearing = bearing(lastLocation, currentLocation, cameraPosition.value!!.bearing)
|
val bearing = bearing(lastLocation, currentLocation, cameraPosition.value!!.bearing)
|
||||||
with(routeModel) {
|
with(routeModel) {
|
||||||
if (isNavigating()) {
|
if (isNavigating()) {
|
||||||
updateLocation(currentLocation, navigationViewModel)
|
updateLocation(currentLocation, navigationViewModel)
|
||||||
stepData.value = currentStep()
|
stepData.value = currentStep()
|
||||||
println("Step: ${stepData.value!!.instruction} ${stepData.value!!.leftStepDistance}")
|
|
||||||
if (route.currentStepIndex + 1 <= curLeg.steps.size) {
|
if (route.currentStepIndex + 1 <= curLeg.steps.size) {
|
||||||
nextStepData.value = nextStep()
|
nextStepData.value = nextStep()
|
||||||
}
|
}
|
||||||
if (maneuverType in 39..42
|
if (maneuverType in 39..42
|
||||||
&& leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
&& routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
||||||
) {
|
) {
|
||||||
// stopNavigation()
|
// stopNavigation()
|
||||||
arrived = true
|
arrived = true
|
||||||
@@ -410,9 +406,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
)
|
)
|
||||||
val step = routeModel.currentStep()
|
val step = routeModel.currentStep()
|
||||||
println("Step: ${step.instruction} ${step.leftStepDistance}")
|
println("Step: ${step.instruction} ${step.leftStepDistance}")
|
||||||
if (step.leftStepDistance == 70.0) {
|
|
||||||
println("")
|
|
||||||
}
|
|
||||||
if (index + 1 <= routeModel.curLeg.steps.size) {
|
if (index + 1 <= routeModel.curLeg.steps.size) {
|
||||||
//nextStepData.value = routeModel.nextStep()
|
//nextStepData.value = routeModel.nextStep()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ fun NavigationSheet(
|
|||||||
val distance = (step.leftDistance / 1000).round(1)
|
val distance = (step.leftDistance / 1000).round(1)
|
||||||
|
|
||||||
if (step.lane.isNotEmpty()) {
|
if (step.lane.isNotEmpty()) {
|
||||||
routeModel.addLanes( step)
|
routeModel.iconMapper.addLanes( step)
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ class SurfaceRenderer(
|
|||||||
val trafficData = MutableLiveData(emptyMap<String, String>())
|
val trafficData = MutableLiveData(emptyMap<String, String>())
|
||||||
val speedCamerasData = MutableLiveData("")
|
val speedCamerasData = MutableLiveData("")
|
||||||
val speed = MutableLiveData(0F)
|
val speed = MutableLiveData(0F)
|
||||||
|
val maxSpeed = MutableLiveData(0)
|
||||||
var viewStyle = ViewStyle.VIEW
|
var viewStyle = ViewStyle.VIEW
|
||||||
lateinit var centerLocation: Location
|
lateinit var centerLocation: Location
|
||||||
var previewDistance = 0.0
|
var previewDistance = 0.0
|
||||||
@@ -209,11 +210,12 @@ class SurfaceRenderer(
|
|||||||
val cameraDuration =
|
val cameraDuration =
|
||||||
duration(viewStyle == ViewStyle.PREVIEW, position!!.bearing, lastBearing)
|
duration(viewStyle == ViewStyle.PREVIEW, position!!.bearing, lastBearing)
|
||||||
val currentSpeed: Float? by speed.observeAsState()
|
val currentSpeed: Float? by speed.observeAsState()
|
||||||
|
val maxSpeed: Int? by maxSpeed.observeAsState()
|
||||||
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
||||||
DrawNavigationImages(
|
DrawNavigationImages(
|
||||||
paddingValues,
|
paddingValues,
|
||||||
currentSpeed,
|
currentSpeed,
|
||||||
routeModel,
|
maxSpeed!!,
|
||||||
width,
|
width,
|
||||||
height
|
height
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -294,16 +294,16 @@ fun BuildingLayer(tiles: Source) {
|
|||||||
fun DrawNavigationImages(
|
fun DrawNavigationImages(
|
||||||
padding: PaddingValues,
|
padding: PaddingValues,
|
||||||
speed: Float?,
|
speed: Float?,
|
||||||
routeModel: RouteModel,
|
maxSpeed: Int,
|
||||||
width: Int,
|
width: Int,
|
||||||
height: Int
|
height: Int
|
||||||
) {
|
) {
|
||||||
NavigationImage(padding, width, height)
|
NavigationImage(padding, width, height)
|
||||||
if (speed != null) {
|
if (speed != null) {
|
||||||
CurrentSpeed(width, height, speed, routeModel.maxSpeed)
|
CurrentSpeed(width, height, speed, maxSpeed)
|
||||||
}
|
}
|
||||||
if (speed != null && routeModel.maxSpeed > 0 && (speed * 3.6) > routeModel.maxSpeed) {
|
if (speed != null && maxSpeed > 0 && (speed * 3.6) > maxSpeed) {
|
||||||
MaxSpeed(width, height, routeModel.maxSpeed)
|
MaxSpeed(width, height, maxSpeed)
|
||||||
}
|
}
|
||||||
//DebugInfo(width, height, routeModel)
|
//DebugInfo(width, height, routeModel)
|
||||||
}
|
}
|
||||||
@@ -470,7 +470,7 @@ fun DebugInfo(
|
|||||||
contentAlignment = Alignment.CenterStart
|
contentAlignment = Alignment.CenterStart
|
||||||
) {
|
) {
|
||||||
val textMeasurerLocation = rememberTextMeasurer()
|
val textMeasurerLocation = rememberTextMeasurer()
|
||||||
val location = routeModel.location.latitude.toString()
|
val location = routeModel.currentLocation.latitude.toString()
|
||||||
val styleSpeed = TextStyle(
|
val styleSpeed = TextStyle(
|
||||||
fontSize = 26.sp,
|
fontSize = 26.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kouros.navigation.car.navigation
|
package com.kouros.navigation.car.navigation
|
||||||
|
|
||||||
|
import android.location.Location
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
@@ -98,17 +99,17 @@ class RouteCarModel() : RouteModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun travelEstimate(carContext: CarContext): TravelEstimate {
|
fun travelEstimate(carContext: CarContext): TravelEstimate {
|
||||||
val timeLeft = travelLeftTime()
|
val timeLeft = routeCalculator.travelLeftTime()
|
||||||
val timeToDestinationMillis =
|
val timeToDestinationMillis =
|
||||||
TimeUnit.SECONDS.toMillis(timeLeft.toLong())
|
TimeUnit.SECONDS.toMillis(timeLeft.toLong())
|
||||||
val leftDistance = travelLeftDistance() / 1000
|
val leftDistance = routeCalculator.travelLeftDistance() / 1000
|
||||||
val displayUnit = if (leftDistance > 1.0) {
|
val displayUnit = if (leftDistance > 1.0) {
|
||||||
Distance.UNIT_KILOMETERS
|
Distance.UNIT_KILOMETERS
|
||||||
} else {
|
} else {
|
||||||
Distance.UNIT_METERS
|
Distance.UNIT_METERS
|
||||||
}
|
}
|
||||||
val arrivalTime = DateTimeWithZone.create(
|
val arrivalTime = DateTimeWithZone.create(
|
||||||
arrivalTime(),
|
routeCalculator.arrivalTime(),
|
||||||
TimeZone.getTimeZone("Europe/Berlin")
|
TimeZone.getTimeZone("Europe/Berlin")
|
||||||
)
|
)
|
||||||
val travelBuilder = TravelEstimate.Builder( // The estimated distance to the destination.
|
val travelBuilder = TravelEstimate.Builder( // The estimated distance to the destination.
|
||||||
@@ -133,99 +134,99 @@ class RouteCarModel() : RouteModel() {
|
|||||||
return travelBuilder.build()
|
return travelBuilder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addLanes(carContext: CarContext, step: Step.Builder, stepData: StepData) {
|
fun addLanes(carContext: CarContext, step: Step.Builder, stepData: StepData) {
|
||||||
var laneImageAdded = false
|
var laneImageAdded = false
|
||||||
stepData.lane.forEach {
|
stepData.lane.forEach {
|
||||||
if (it.indications.isNotEmpty() && it.valid) {
|
if (it.indications.isNotEmpty() && it.valid) {
|
||||||
Collections.sort<String>(it.indications)
|
Collections.sort<String>(it.indications)
|
||||||
var direction = ""
|
var direction = ""
|
||||||
it.indications.forEach { it2 ->
|
it.indications.forEach { it2 ->
|
||||||
direction = if (direction.isEmpty()) {
|
direction = if (direction.isEmpty()) {
|
||||||
it2.trim()
|
it2.trim()
|
||||||
} else {
|
} else {
|
||||||
"${direction}_${it2.trim()}"
|
"${direction}_${it2.trim()}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
val laneDirection = iconMapper.addLanes(direction, stepData)
|
||||||
val laneDirection = addLanes(direction, stepData)
|
if (laneDirection != LaneDirection.SHAPE_UNKNOWN) {
|
||||||
if (laneDirection != LaneDirection.SHAPE_UNKNOWN) {
|
if (!laneImageAdded) {
|
||||||
if (!laneImageAdded) {
|
step.setLanesImage(createCarIcon(iconMapper.createLaneIcon(carContext, stepData)))
|
||||||
step.setLanesImage(createCarIcon(createLaneIcon(carContext, stepData)))
|
laneImageAdded = true
|
||||||
laneImageAdded = true
|
}
|
||||||
|
val laneType =
|
||||||
|
Lane.Builder()
|
||||||
|
.addDirection(LaneDirection.create(laneDirection, false))
|
||||||
|
.build()
|
||||||
|
step.addLane(laneType)
|
||||||
}
|
}
|
||||||
val laneType =
|
|
||||||
Lane.Builder()
|
|
||||||
.addDirection(LaneDirection.create(laneDirection, false))
|
|
||||||
.build()
|
|
||||||
step.addLane(laneType)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun createString(
|
fun createString(
|
||||||
text: String
|
text: String
|
||||||
): SpannableString {
|
): SpannableString {
|
||||||
val spannableString = SpannableString(text)
|
val spannableString = SpannableString(text)
|
||||||
return spannableString
|
return spannableString
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createCarText(carContext: CarContext, @StringRes stringRes: Int): CarText {
|
fun createCarText(carContext: CarContext, @StringRes stringRes: Int): CarText {
|
||||||
return CarText.create(carContext.getString(stringRes))
|
return CarText.create(carContext.getString(stringRes))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createCarIcon(carContext: CarContext, @DrawableRes iconRes: Int): CarIcon {
|
fun createCarIcon(carContext: CarContext, @DrawableRes iconRes: Int): CarIcon {
|
||||||
return CarIcon.Builder(IconCompat.createWithResource(carContext, iconRes)).build()
|
return CarIcon.Builder(IconCompat.createWithResource(carContext, iconRes)).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
// fun createCarIcon(iconCompat: IconCompat): CarIcon {
|
fun createCarIcon(iconCompat: IconCompat): CarIcon {
|
||||||
// return CarIcon.Builder(iconCompat).build()
|
return CarIcon.Builder(iconCompat).build()
|
||||||
// }
|
}
|
||||||
|
|
||||||
fun showSpeedCamera(carContext: CarContext, distance: Double, maxSpeed: String) {
|
fun showSpeedCamera(carContext: CarContext, distance: Double, maxSpeed: String) {
|
||||||
carContext.getCarService<AppManager?>(AppManager::class.java)
|
carContext.getCarService<AppManager?>(AppManager::class.java)
|
||||||
.showAlert(
|
.showAlert(
|
||||||
createAlert(
|
createAlert(
|
||||||
carContext,
|
carContext,
|
||||||
maxSpeed,
|
maxSpeed,
|
||||||
createCarIcon(carContext, R.drawable.speed_camera_24px)
|
createCarIcon(carContext, R.drawable.speed_camera_24px)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createAlert(
|
||||||
|
carContext: CarContext,
|
||||||
|
maxSpeed: String?,
|
||||||
|
icon: CarIcon
|
||||||
|
): Alert {
|
||||||
|
val title = createCarText(carContext, R.string.speed_camera)
|
||||||
|
val subtitle = CarText.create(maxSpeed!!)
|
||||||
|
|
||||||
|
val dismissAction: Action = createToastAction(
|
||||||
|
carContext,
|
||||||
|
R.string.exit_action_title, R.string.exit_action_title,
|
||||||
|
FLAG_DEFAULT
|
||||||
)
|
)
|
||||||
}
|
return Alert.Builder( /* alertId: */0, title, /* durationMillis: */5000)
|
||||||
|
.setSubtitle(subtitle)
|
||||||
|
.setIcon(icon)
|
||||||
|
.addAction(dismissAction).setCallback(object : AlertCallback {
|
||||||
|
override fun onCancel(reason: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
fun createAlert(
|
override fun onDismiss() {
|
||||||
carContext: CarContext,
|
}
|
||||||
maxSpeed: String?,
|
}).build()
|
||||||
icon: CarIcon
|
}
|
||||||
): Alert {
|
|
||||||
val title = createCarText(carContext, R.string.speed_camera)
|
|
||||||
val subtitle = CarText.create(maxSpeed!!)
|
|
||||||
|
|
||||||
val dismissAction: Action = createToastAction(
|
private fun createToastAction(
|
||||||
carContext,
|
carContext: CarContext,
|
||||||
R.string.exit_action_title, R.string.exit_action_title,
|
@StringRes titleRes: Int, @StringRes toastStringRes: Int,
|
||||||
FLAG_DEFAULT
|
flags: Int
|
||||||
)
|
): Action {
|
||||||
return Alert.Builder( /* alertId: */0, title, /* durationMillis: */5000)
|
return Action.Builder()
|
||||||
.setSubtitle(subtitle)
|
.setOnClickListener { }
|
||||||
.setIcon(icon)
|
.setTitle(createCarText(carContext, titleRes))
|
||||||
.addAction(dismissAction).setCallback(object : AlertCallback {
|
.setFlags(flags)
|
||||||
override fun onCancel(reason: Int) {
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDismiss() {
|
|
||||||
}
|
|
||||||
}).build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createToastAction(
|
|
||||||
carContext: CarContext,
|
|
||||||
@StringRes titleRes: Int, @StringRes toastStringRes: Int,
|
|
||||||
flags: Int
|
|
||||||
): Action {
|
|
||||||
return Action.Builder()
|
|
||||||
.setOnClickListener { }
|
|
||||||
.setTitle(createCarText(carContext, titleRes))
|
|
||||||
.setFlags(flags)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ class NavigationScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getRoutingInfo(): RoutingInfo {
|
fun getRoutingInfo(): RoutingInfo {
|
||||||
var currentDistance = routeModel.leftStepDistance()
|
var currentDistance = routeModel.routeCalculator.leftStepDistance()
|
||||||
val displayUnit = if (currentDistance > 1000.0) {
|
val displayUnit = if (currentDistance > 1000.0) {
|
||||||
currentDistance /= 1000.0
|
currentDistance /= 1000.0
|
||||||
Distance.UNIT_KILOMETERS
|
Distance.UNIT_KILOMETERS
|
||||||
@@ -496,7 +496,7 @@ class NavigationScreen(
|
|||||||
|| maneuverType == Maneuver.TYPE_DESTINATION_LEFT
|
|| maneuverType == Maneuver.TYPE_DESTINATION_LEFT
|
||||||
|| maneuverType == Maneuver.TYPE_DESTINATION_RIGHT
|
|| maneuverType == Maneuver.TYPE_DESTINATION_RIGHT
|
||||||
|| maneuverType == Maneuver.TYPE_DESTINATION_STRAIGHT)
|
|| maneuverType == Maneuver.TYPE_DESTINATION_STRAIGHT)
|
||||||
&& leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
&& routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
||||||
) {
|
) {
|
||||||
stopNavigation()
|
stopNavigation()
|
||||||
arrived = true
|
arrived = true
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Overpass {
|
|||||||
val overpassUrl = "https://kouros-online.de/overpass/interpreter"
|
val overpassUrl = "https://kouros-online.de/overpass/interpreter"
|
||||||
|
|
||||||
|
|
||||||
fun getAround(radius: Int, linestring: String) : List<Elements> {
|
fun getAround(radius: Int, linestring: String): List<Elements> {
|
||||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||||
httpURLConnection.requestMethod = "POST"
|
httpURLConnection.requestMethod = "POST"
|
||||||
httpURLConnection.setRequestProperty(
|
httpURLConnection.setRequestProperty(
|
||||||
@@ -57,12 +57,13 @@ class Overpass {
|
|||||||
| node[$type=$category]
|
| node[$type=$category]
|
||||||
| ($boundingBox);
|
| ($boundingBox);
|
||||||
|);
|
|);
|
||||||
|
|(._;>;);
|
||||||
|out body;
|
|out body;
|
||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
return overpassApi(httpURLConnection, searchQuery)
|
return overpassApi(httpURLConnection, searchQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun overpassApi(httpURLConnection: HttpURLConnection, searchQuery: String) : List<Elements> {
|
fun overpassApi(httpURLConnection: HttpURLConnection, searchQuery: String): List<Elements> {
|
||||||
try {
|
try {
|
||||||
val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream)
|
val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream)
|
||||||
outputStreamWriter.write(searchQuery)
|
outputStreamWriter.write(searchQuery)
|
||||||
@@ -75,8 +76,10 @@ class Overpass {
|
|||||||
val gson = GsonBuilder().serializeNulls().create()
|
val gson = GsonBuilder().serializeNulls().create()
|
||||||
val overpass = gson.fromJson(response, Amenity::class.java)
|
val overpass = gson.fromJson(response, Amenity::class.java)
|
||||||
return overpass.elements
|
return overpass.elements
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
||||||
}
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,16 +35,16 @@ class TomTomRoute {
|
|||||||
var stepDuration = 0.0
|
var stepDuration = 0.0
|
||||||
val allIntersections = mutableListOf<Intersection>()
|
val allIntersections = mutableListOf<Intersection>()
|
||||||
val steps = mutableListOf<Step>()
|
val steps = mutableListOf<Step>()
|
||||||
for (index in 0..< route.guidance.instructions.size) {
|
var lastPointIndex = 0
|
||||||
|
for (index in 1..< route.guidance.instructions.size) {
|
||||||
val instruction = route.guidance.instructions[index]
|
val instruction = route.guidance.instructions[index]
|
||||||
val nextPointIndex = nextPointIndex(index, route)
|
|
||||||
val maneuver = RouteManeuver(
|
val maneuver = RouteManeuver(
|
||||||
bearingBefore = 0,
|
bearingBefore = 0,
|
||||||
bearingAfter = 0,
|
bearingAfter = 0,
|
||||||
type = convertType(instruction.maneuver),
|
type = convertType(instruction.maneuver),
|
||||||
waypoints = points.subList(
|
waypoints = points.subList(
|
||||||
|
lastPointIndex,
|
||||||
instruction.pointIndex,
|
instruction.pointIndex,
|
||||||
route.guidance.instructions[nextPointIndex].pointIndex
|
|
||||||
),
|
),
|
||||||
exit = exitNumber(instruction),
|
exit = exitNumber(instruction),
|
||||||
location = location(
|
location = location(
|
||||||
@@ -52,6 +52,7 @@ class TomTomRoute {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
lastPointIndex = instruction.pointIndex
|
||||||
val intersections = mutableListOf<Intersection>()
|
val intersections = mutableListOf<Intersection>()
|
||||||
route.sections.forEach { section ->
|
route.sections.forEach { section ->
|
||||||
val lanes = mutableListOf<Lane>()
|
val lanes = mutableListOf<Lane>()
|
||||||
@@ -75,8 +76,8 @@ class TomTomRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
allIntersections.addAll(intersections)
|
allIntersections.addAll(intersections)
|
||||||
stepDistance = route.guidance.instructions[nextPointIndex].routeOffsetInMeters - stepDistance
|
stepDistance = route.guidance.instructions[index].routeOffsetInMeters - stepDistance
|
||||||
stepDuration = route.guidance.instructions[nextPointIndex].travelTimeInSeconds - stepDuration
|
stepDuration = route.guidance.instructions[index].travelTimeInSeconds - stepDuration
|
||||||
val name = instruction.street
|
val name = instruction.street
|
||||||
val step = Step(
|
val step = Step(
|
||||||
index = stepIndex,
|
index = stepIndex,
|
||||||
@@ -86,8 +87,8 @@ class TomTomRoute {
|
|||||||
maneuver = maneuver,
|
maneuver = maneuver,
|
||||||
intersection = intersections
|
intersection = intersections
|
||||||
)
|
)
|
||||||
stepDistance = route.guidance.instructions[nextPointIndex].routeOffsetInMeters.toDouble()
|
stepDistance = route.guidance.instructions[index].routeOffsetInMeters.toDouble()
|
||||||
stepDuration = route.guidance.instructions[nextPointIndex].travelTimeInSeconds.toDouble()
|
stepDuration = route.guidance.instructions[index].travelTimeInSeconds.toDouble()
|
||||||
steps.add(step)
|
steps.add(step)
|
||||||
stepIndex += 1
|
stepIndex += 1
|
||||||
}
|
}
|
||||||
@@ -108,15 +109,6 @@ class TomTomRoute {
|
|||||||
.routes(routes)
|
.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 {
|
fun convertType(type: String): Int {
|
||||||
var newType = 0
|
var newType = 0
|
||||||
when (type) {
|
when (type) {
|
||||||
|
|||||||
@@ -0,0 +1,276 @@
|
|||||||
|
package com.kouros.navigation.model
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Matrix
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.car.app.model.CarIcon
|
||||||
|
import androidx.car.app.navigation.model.LaneDirection
|
||||||
|
import androidx.car.app.navigation.model.Maneuver
|
||||||
|
import androidx.core.graphics.createBitmap
|
||||||
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
|
import com.kouros.data.R
|
||||||
|
import com.kouros.navigation.data.StepData
|
||||||
|
import java.util.Collections
|
||||||
|
import java.util.Locale
|
||||||
|
import kotlin.collections.forEach
|
||||||
|
|
||||||
|
class IconMapper(var routeModel: RouteModel) {
|
||||||
|
|
||||||
|
|
||||||
|
fun maneuverIcon(routeManeuverType: Int): Int {
|
||||||
|
var currentTurnIcon = R.drawable.ic_turn_name_change
|
||||||
|
when (routeManeuverType) {
|
||||||
|
Maneuver.TYPE_STRAIGHT -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_name_change
|
||||||
|
}
|
||||||
|
|
||||||
|
Maneuver.TYPE_DESTINATION,
|
||||||
|
Maneuver.TYPE_DESTINATION_RIGHT,
|
||||||
|
Maneuver.TYPE_DESTINATION_LEFT,
|
||||||
|
Maneuver.TYPE_DESTINATION_STRAIGHT
|
||||||
|
-> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_destination
|
||||||
|
}
|
||||||
|
|
||||||
|
Maneuver.TYPE_TURN_NORMAL_RIGHT -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_normal_right
|
||||||
|
}
|
||||||
|
|
||||||
|
Maneuver.TYPE_TURN_NORMAL_LEFT -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_normal_left
|
||||||
|
}
|
||||||
|
|
||||||
|
Maneuver.TYPE_OFF_RAMP_SLIGHT_RIGHT -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_slight_right
|
||||||
|
}
|
||||||
|
|
||||||
|
Maneuver.TYPE_TURN_SLIGHT_RIGHT -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_slight_right
|
||||||
|
}
|
||||||
|
|
||||||
|
Maneuver.TYPE_KEEP_RIGHT -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_name_change
|
||||||
|
}
|
||||||
|
|
||||||
|
Maneuver.TYPE_KEEP_LEFT -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_name_change
|
||||||
|
}
|
||||||
|
|
||||||
|
Maneuver.TYPE_ROUNDABOUT_ENTER_CCW -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
||||||
|
}
|
||||||
|
|
||||||
|
Maneuver.TYPE_ROUNDABOUT_EXIT_CCW -> {
|
||||||
|
|
||||||
|
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentTurnIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun addLanes(stepData: StepData) : Int {
|
||||||
|
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)
|
||||||
|
return laneDirection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
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>()
|
||||||
|
stepData.lane.forEach {
|
||||||
|
if (it.indications.isNotEmpty()) {
|
||||||
|
Collections.sort<String>(it.indications)
|
||||||
|
val resource = laneToResource(it.indications, stepData)
|
||||||
|
if (resource.isNotEmpty()) {
|
||||||
|
val id = resourceId(resource);
|
||||||
|
val bitMap = BitmapFactory.decodeResource(context.resources, id)
|
||||||
|
bitmaps.add(bitMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return if (bitmaps.isEmpty()) {
|
||||||
|
IconCompat.createWithResource(context, R.drawable.ic_close_white_24dp)
|
||||||
|
} else {
|
||||||
|
IconCompat.createWithBitmap(overlay(bitmaps = bitmaps))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun overlay(bitmaps: List<Bitmap>): Bitmap {
|
||||||
|
val matrix = Matrix()
|
||||||
|
if (bitmaps.size == 1) {
|
||||||
|
return bitmaps.first()
|
||||||
|
}
|
||||||
|
val bmOverlay = createBitmap(
|
||||||
|
bitmaps.first().getWidth() * (bitmaps.size * 1.5).toInt(),
|
||||||
|
bitmaps.first().getHeight(),
|
||||||
|
bitmaps.first().getConfig()!!
|
||||||
|
)
|
||||||
|
val canvas = Canvas(bmOverlay)
|
||||||
|
canvas.drawBitmap(bitmaps.first(), matrix, null)
|
||||||
|
var i = 0
|
||||||
|
bitmaps.forEach {
|
||||||
|
if (i > 0) {
|
||||||
|
matrix.setTranslate(i * 45F, 0F)
|
||||||
|
canvas.drawBitmap(it, matrix, null)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return bmOverlay
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun laneToResource(directions: List<String>, stepData: StepData): String {
|
||||||
|
var direction = ""
|
||||||
|
directions.forEach {
|
||||||
|
direction = if (direction.isEmpty()) {
|
||||||
|
it.trim()
|
||||||
|
} else {
|
||||||
|
"${direction}_${it.trim()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
direction = direction.lowercase()
|
||||||
|
return when (direction) {
|
||||||
|
"left_straight" -> {
|
||||||
|
when (stepData.currentManeuverType) {
|
||||||
|
Maneuver.TYPE_TURN_NORMAL_LEFT -> "left_o_straight_x"
|
||||||
|
Maneuver.TYPE_STRAIGHT -> "left_x_straight_o"
|
||||||
|
else
|
||||||
|
-> "left_x_straight_x"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"right_straight" -> {
|
||||||
|
when (stepData.currentManeuverType) {
|
||||||
|
Maneuver.TYPE_TURN_NORMAL_RIGHT -> "right_x_straight_x"
|
||||||
|
Maneuver.TYPE_STRAIGHT -> "right_x_straight_o"
|
||||||
|
Maneuver.TYPE_TURN_SLIGHT_RIGHT -> "right_o_straight_o"
|
||||||
|
else
|
||||||
|
-> "right_x_straight_x"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"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", "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 -> {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resourceId(
|
||||||
|
variableName: String,
|
||||||
|
): Int {
|
||||||
|
return when (variableName) {
|
||||||
|
"left_x" -> R.drawable.left_x
|
||||||
|
"left_o" -> R.drawable.left_o
|
||||||
|
"left_o_right_x" -> R.drawable.left_o_right_x
|
||||||
|
"right_x" -> R.drawable.right_x
|
||||||
|
"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
|
||||||
|
"right_x_straight_o" -> R.drawable.right_x_straight_x
|
||||||
|
"straight_o" -> R.drawable.straight_o
|
||||||
|
"left_o_straight_x" -> R.drawable.left_o_straight_x
|
||||||
|
"left_x_straight_o" -> R.drawable.left_x_straight_o
|
||||||
|
else -> {
|
||||||
|
R.drawable.left_x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package com.kouros.navigation.model
|
||||||
|
|
||||||
|
import android.location.Location
|
||||||
|
import androidx.car.app.navigation.model.Step
|
||||||
|
import com.kouros.navigation.utils.location
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
class RouteCalculator(var routeModel: RouteModel) {
|
||||||
|
|
||||||
|
var lastSpeedLocation: Location = location(0.0, 0.0)
|
||||||
|
|
||||||
|
var lastSpeedIndex: Int = 0
|
||||||
|
|
||||||
|
|
||||||
|
fun findStep(location: Location) {
|
||||||
|
var nearestDistance = 100000f
|
||||||
|
for ((index, step) in routeModel.curLeg.steps.withIndex()) {
|
||||||
|
if (index >= routeModel.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
|
||||||
|
routeModel.route.currentStepIndex = step.index
|
||||||
|
step.waypointIndex = wayIndex
|
||||||
|
step.wayPointLocation = location(waypoint[0], waypoint[1])
|
||||||
|
routeModel.routeBearing = routeModel.lastLocation.bearingTo(location)
|
||||||
|
} else {
|
||||||
|
if (nearestDistance != 100000f) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nearestDistance == 0F) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nearestDistance == 0F) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun travelLeftTime(): Double {
|
||||||
|
var timeLeft = 0.0
|
||||||
|
// time for next step until end step
|
||||||
|
for (i in routeModel.route.currentStepIndex + 1..<routeModel.curLeg.steps.size) {
|
||||||
|
val step = routeModel.curLeg.steps[i]
|
||||||
|
timeLeft += step.duration
|
||||||
|
}
|
||||||
|
// time for current step
|
||||||
|
val step = routeModel.route.currentStep()
|
||||||
|
val curTime = step.duration
|
||||||
|
val percent =
|
||||||
|
100 * (step.maneuver.waypoints.size - step.waypointIndex) / (step.maneuver.waypoints.size)
|
||||||
|
val time = curTime * percent / 100
|
||||||
|
timeLeft += time
|
||||||
|
return timeLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the current [Step] left distance in m. */
|
||||||
|
fun leftStepDistance(): Double {
|
||||||
|
val step = routeModel.route.currentStep()
|
||||||
|
var leftDistance = 0F
|
||||||
|
for (i in step.waypointIndex..<step.maneuver.waypoints.size - 1) {
|
||||||
|
val loc1 = location(step.maneuver.waypoints[i][0], step.maneuver.waypoints[i][1])
|
||||||
|
val loc2 =
|
||||||
|
location(step.maneuver.waypoints[i + 1][0], step.maneuver.waypoints[i + 1][1])
|
||||||
|
val distance = loc1.distanceTo(loc2)
|
||||||
|
leftDistance += distance
|
||||||
|
}
|
||||||
|
return (leftDistance / 10.0).roundToInt() * 10.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the left distance in m. */
|
||||||
|
fun travelLeftDistance(): Double {
|
||||||
|
var leftDistance = 0.0
|
||||||
|
for (i in routeModel.route.currentStepIndex + 1..<routeModel.curLeg.steps.size) {
|
||||||
|
val step = routeModel.route.legs()[0].steps[i]
|
||||||
|
leftDistance += step.distance
|
||||||
|
}
|
||||||
|
leftDistance += leftStepDistance()
|
||||||
|
return leftDistance
|
||||||
|
}
|
||||||
|
|
||||||
|
fun arrivalTime(): Long {
|
||||||
|
val timeLeft = travelLeftTime()
|
||||||
|
// Calculate the time to destination from the current time.
|
||||||
|
val nowUtcMillis = System.currentTimeMillis()
|
||||||
|
val timeToDestinationMillis =
|
||||||
|
TimeUnit.SECONDS.toMillis(timeLeft.toLong())
|
||||||
|
return nowUtcMillis + timeToDestinationMillis
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSpeedLimit(location: Location, viewModel: ViewModel) {
|
||||||
|
if (routeModel.isNavigating()) {
|
||||||
|
// speed limit
|
||||||
|
val distance = lastSpeedLocation.distanceTo(location)
|
||||||
|
if (distance > 500 || lastSpeedIndex < routeModel.route.currentStepIndex) {
|
||||||
|
lastSpeedIndex = routeModel.route.currentStepIndex
|
||||||
|
lastSpeedLocation = location
|
||||||
|
viewModel.getMaxSpeed(location, routeModel.previousStreet())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,58 +1,36 @@
|
|||||||
package com.kouros.navigation.model
|
package com.kouros.navigation.model
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.graphics.Matrix
|
|
||||||
import android.location.Location
|
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.Maneuver
|
||||||
import androidx.car.app.navigation.model.Step
|
|
||||||
import androidx.core.graphics.createBitmap
|
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
|
||||||
import com.kouros.data.R
|
|
||||||
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
||||||
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
|
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
import com.kouros.navigation.data.Route
|
import com.kouros.navigation.data.Route
|
||||||
import com.kouros.navigation.data.RouteEngine
|
|
||||||
import com.kouros.navigation.data.StepData
|
import com.kouros.navigation.data.StepData
|
||||||
import com.kouros.navigation.data.route.Intersection
|
|
||||||
import com.kouros.navigation.data.route.Lane
|
import com.kouros.navigation.data.route.Lane
|
||||||
import com.kouros.navigation.data.route.Leg
|
import com.kouros.navigation.data.route.Leg
|
||||||
import com.kouros.navigation.data.route.Routes
|
import com.kouros.navigation.data.route.Routes
|
||||||
import com.kouros.navigation.data.valhalla.ManeuverType
|
|
||||||
import com.kouros.navigation.utils.Levenshtein
|
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
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.absoluteValue
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
open class RouteModel() {
|
open class RouteModel() {
|
||||||
|
|
||||||
var route = Route.Builder().buildEmpty()
|
var route = Route.Builder().buildEmpty()
|
||||||
|
|
||||||
|
val routeCalculator = RouteCalculator(this)
|
||||||
|
|
||||||
|
var iconMapper = IconMapper(this)
|
||||||
var navigating: Boolean = false
|
var navigating: Boolean = false
|
||||||
var destination: Place = Place()
|
var destination: Place = Place()
|
||||||
var arrived: Boolean = false
|
var arrived: Boolean = false
|
||||||
var maneuverType: Int = 0
|
var maneuverType: Int = 0
|
||||||
var travelMessage: String = ""
|
var travelMessage: String = ""
|
||||||
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 currentLocation: Location = location(0.0, 0.0)
|
||||||
|
|
||||||
var lastLocation: Location = location(0.0, 0.0)
|
var lastLocation: Location = location(0.0, 0.0)
|
||||||
var routeBearing: Float = 0F
|
var routeBearing: Float = 0F
|
||||||
|
|
||||||
@@ -63,6 +41,7 @@ open class RouteModel() {
|
|||||||
val curLeg: Leg
|
val curLeg: Leg
|
||||||
get() = route.routes[currentRouteIndex].legs.first()
|
get() = route.routes[currentRouteIndex].legs.first()
|
||||||
|
|
||||||
|
|
||||||
fun startNavigation(routeString: String, context: Context) {
|
fun startNavigation(routeString: String, context: Context) {
|
||||||
val routeEngine = getIntKeyValue(context = context, ROUTING_ENGINE)
|
val routeEngine = getIntKeyValue(context = context, ROUTING_ENGINE)
|
||||||
route = Route.Builder()
|
route = Route.Builder()
|
||||||
@@ -88,36 +67,10 @@ open class RouteModel() {
|
|||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
fun updateLocation(curLocation: Location, viewModel: ViewModel) {
|
fun updateLocation(curLocation: Location, viewModel: ViewModel) {
|
||||||
location = curLocation
|
currentLocation = curLocation
|
||||||
findStep(curLocation)
|
routeCalculator.findStep(curLocation)
|
||||||
updateSpeedLimit(curLocation, viewModel)
|
routeCalculator.updateSpeedLimit(curLocation, viewModel)
|
||||||
lastLocation = location
|
lastLocation = currentLocation
|
||||||
}
|
|
||||||
|
|
||||||
private fun findStep(location: Location) {
|
|
||||||
var nearestDistance = 100000f
|
|
||||||
for ((index, step) in curLeg.steps.withIndex()) {
|
|
||||||
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.currentStepIndex = step.index
|
|
||||||
step.waypointIndex = wayIndex
|
|
||||||
step.wayPointLocation = location(waypoint[0], waypoint[1])
|
|
||||||
routeBearing = lastLocation.bearingTo(location)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nearestDistance == 0F) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nearestDistance == 0F) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun currentLanes(location: Location): List<Lane> {
|
private fun currentLanes(location: Location): List<Lane> {
|
||||||
@@ -135,68 +88,21 @@ open class RouteModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lanes
|
return lanes
|
||||||
|
|
||||||
// var inter = Intersection()
|
|
||||||
// var nearestDistance = 100000.0f
|
|
||||||
// route.currentStep().intersection.forEach {
|
|
||||||
// if (it.lane.isNotEmpty()) {
|
|
||||||
// val distance = location.distanceTo(location(it.location[0], it.location[1]))
|
|
||||||
// val interBearing = location.bearingTo(location(it.location[0], it.location[1]))
|
|
||||||
// if (distance < nearestDistance) {
|
|
||||||
// nearestDistance = distance
|
|
||||||
// if (distance <= NEXT_STEP_THRESHOLD * 3) {
|
|
||||||
// if (route.routeEngine == RouteEngine.TOMTOM.ordinal
|
|
||||||
// || (interBearing.absoluteValue - route.currentStep().maneuver.bearingAfter.absoluteValue).absoluteValue < 20
|
|
||||||
// ) {
|
|
||||||
// inter = it
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return inter.lane
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSpeedLimit(location: Location, viewModel: ViewModel) = runBlocking {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun currentStep(): StepData {
|
fun currentStep(): StepData {
|
||||||
val distanceToNextStep = leftStepDistance()
|
val distanceToNextStep = routeCalculator.leftStepDistance()
|
||||||
// Determine the maneuver type and corresponding icon
|
// Determine the maneuver type and corresponding icon
|
||||||
val currentStep = route.nextStep(1)
|
val currentStep = route.nextStep(0)
|
||||||
// Safely get the street name from the maneuver
|
// Safely get the street name from the maneuver
|
||||||
val streetName = currentStep.name
|
val streetName = currentStep.name
|
||||||
val curManeuverType = currentStep.maneuver.type
|
val curManeuverType = currentStep.maneuver.type
|
||||||
val exitNumber = currentStep.maneuver.exit
|
val exitNumber = currentStep.maneuver.exit
|
||||||
val maneuverIcon = maneuverIcon(curManeuverType)
|
val maneuverIcon = iconMapper.maneuverIcon(curManeuverType)
|
||||||
maneuverType = curManeuverType
|
maneuverType = curManeuverType
|
||||||
|
|
||||||
val lanes = currentLanes(location)
|
val lanes = currentLanes(currentLocation)
|
||||||
|
|
||||||
// Construct and return the final StepData object
|
// Construct and return the final StepData object
|
||||||
return StepData(
|
return StepData(
|
||||||
@@ -204,17 +110,17 @@ open class RouteModel() {
|
|||||||
distanceToNextStep,
|
distanceToNextStep,
|
||||||
maneuverType,
|
maneuverType,
|
||||||
maneuverIcon,
|
maneuverIcon,
|
||||||
arrivalTime(),
|
routeCalculator.arrivalTime(),
|
||||||
travelLeftDistance(),
|
routeCalculator.travelLeftDistance(),
|
||||||
lanes,
|
lanes,
|
||||||
exitNumber
|
exitNumber
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nextStep(): StepData {
|
fun nextStep(): StepData {
|
||||||
val step = route.nextStep(2)
|
val step = route.nextStep(1)
|
||||||
val maneuverType = step.maneuver.type
|
val maneuverType = step.maneuver.type
|
||||||
val distanceLeft = leftStepDistance()
|
val distanceLeft = routeCalculator.leftStepDistance()
|
||||||
var text = ""
|
var text = ""
|
||||||
when (distanceLeft) {
|
when (distanceLeft) {
|
||||||
in 0.0..NEXT_STEP_THRESHOLD -> {
|
in 0.0..NEXT_STEP_THRESHOLD -> {
|
||||||
@@ -227,336 +133,28 @@ open class RouteModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val maneuverIcon = maneuverIcon(maneuverType)
|
val maneuverIcon = iconMapper.maneuverIcon(maneuverType)
|
||||||
// Construct and return the final StepData object
|
// Construct and return the final StepData object
|
||||||
return StepData(
|
return StepData(
|
||||||
text,
|
text,
|
||||||
distanceLeft,
|
distanceLeft,
|
||||||
maneuverType,
|
maneuverType,
|
||||||
maneuverIcon,
|
maneuverIcon,
|
||||||
arrivalTime(),
|
routeCalculator.arrivalTime(),
|
||||||
travelLeftDistance(),
|
routeCalculator.travelLeftDistance(),
|
||||||
listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList())),
|
listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList())),
|
||||||
step.maneuver.exit
|
step.maneuver.exit
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun travelLeftTime(): Double {
|
fun previousStreet(): String {
|
||||||
var timeLeft = 0.0
|
if (route.currentStepIndex > 0) {
|
||||||
// time for next step until end step
|
return route.legs().first().steps[route.currentStepIndex - 1].name
|
||||||
for (i in route.currentStepIndex + 1..<curLeg.steps.size) {
|
|
||||||
val step = curLeg.steps[i]
|
|
||||||
timeLeft += step.duration
|
|
||||||
}
|
}
|
||||||
// time for current step
|
return ""
|
||||||
val step = route.currentStep()
|
|
||||||
val curTime = step.duration
|
|
||||||
val percent =
|
|
||||||
100 * (step.maneuver.waypoints.size - step.waypointIndex) / (step.maneuver.waypoints.size)
|
|
||||||
val time = curTime * percent / 100
|
|
||||||
timeLeft += time
|
|
||||||
return timeLeft
|
|
||||||
}
|
|
||||||
|
|
||||||
fun arrivalTime(): Long {
|
|
||||||
val timeLeft = travelLeftTime()
|
|
||||||
// Calculate the time to destination from the current time.
|
|
||||||
val nowUtcMillis = System.currentTimeMillis()
|
|
||||||
val timeToDestinationMillis =
|
|
||||||
TimeUnit.SECONDS.toMillis(timeLeft.toLong())
|
|
||||||
return nowUtcMillis + timeToDestinationMillis
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the current [Step] left distance in m. */
|
|
||||||
fun leftStepDistance(): Double {
|
|
||||||
val step = route.currentStep()
|
|
||||||
println(step.index)
|
|
||||||
var leftDistance = 0F
|
|
||||||
for (i in step.waypointIndex..<step.maneuver.waypoints.size - 1) {
|
|
||||||
val loc1 = location(step.maneuver.waypoints[i][0], step.maneuver.waypoints[i][1])
|
|
||||||
val loc2 =
|
|
||||||
location(step.maneuver.waypoints[i + 1][0], step.maneuver.waypoints[i + 1][1])
|
|
||||||
val distance = loc1.distanceTo(loc2)
|
|
||||||
leftDistance += distance
|
|
||||||
}
|
|
||||||
return (leftDistance / 10.0).roundToInt() * 10.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the left distance in m. */
|
|
||||||
fun travelLeftDistance(): Double {
|
|
||||||
var leftDistance = 0.0
|
|
||||||
for (i in route.currentStepIndex + 1..<curLeg.steps.size) {
|
|
||||||
val step = route.legs()[0].steps[i]
|
|
||||||
leftDistance += step.distance
|
|
||||||
}
|
|
||||||
leftDistance += leftStepDistance()
|
|
||||||
return leftDistance
|
|
||||||
}
|
|
||||||
|
|
||||||
fun maneuverIcon(routeManeuverType: Int): Int {
|
|
||||||
var currentTurnIcon = R.drawable.ic_turn_name_change
|
|
||||||
when (routeManeuverType) {
|
|
||||||
Maneuver.TYPE_STRAIGHT -> {
|
|
||||||
currentTurnIcon = R.drawable.ic_turn_name_change
|
|
||||||
}
|
|
||||||
|
|
||||||
Maneuver.TYPE_DESTINATION,
|
|
||||||
Maneuver.TYPE_DESTINATION_RIGHT,
|
|
||||||
Maneuver.TYPE_DESTINATION_LEFT,
|
|
||||||
Maneuver.TYPE_DESTINATION_STRAIGHT
|
|
||||||
-> {
|
|
||||||
currentTurnIcon = R.drawable.ic_turn_destination
|
|
||||||
}
|
|
||||||
|
|
||||||
Maneuver.TYPE_TURN_NORMAL_RIGHT -> {
|
|
||||||
currentTurnIcon = R.drawable.ic_turn_normal_right
|
|
||||||
}
|
|
||||||
|
|
||||||
Maneuver.TYPE_TURN_NORMAL_LEFT -> {
|
|
||||||
currentTurnIcon = R.drawable.ic_turn_normal_left
|
|
||||||
}
|
|
||||||
|
|
||||||
Maneuver.TYPE_OFF_RAMP_SLIGHT_RIGHT -> {
|
|
||||||
currentTurnIcon = R.drawable.ic_turn_slight_right
|
|
||||||
}
|
|
||||||
|
|
||||||
Maneuver.TYPE_TURN_SLIGHT_RIGHT -> {
|
|
||||||
currentTurnIcon = R.drawable.ic_turn_slight_right
|
|
||||||
}
|
|
||||||
|
|
||||||
Maneuver.TYPE_KEEP_RIGHT -> {
|
|
||||||
currentTurnIcon = R.drawable.ic_turn_name_change
|
|
||||||
}
|
|
||||||
|
|
||||||
Maneuver.TYPE_KEEP_LEFT -> {
|
|
||||||
currentTurnIcon = R.drawable.ic_turn_name_change
|
|
||||||
}
|
|
||||||
|
|
||||||
Maneuver.TYPE_ROUNDABOUT_ENTER_CCW -> {
|
|
||||||
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
|
||||||
}
|
|
||||||
|
|
||||||
Maneuver.TYPE_ROUNDABOUT_EXIT_CCW -> {
|
|
||||||
|
|
||||||
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return currentTurnIcon
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isNavigating(): Boolean {
|
fun isNavigating(): Boolean {
|
||||||
return navigating
|
return navigating
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun hasArrived(type: Int): Boolean {
|
|
||||||
return type == ManeuverType.DestinationRight.value
|
|
||||||
|| type == ManeuverType.Destination.value
|
|
||||||
|| 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>()
|
|
||||||
stepData.lane.forEach {
|
|
||||||
if (it.indications.isNotEmpty()) {
|
|
||||||
Collections.sort<String>(it.indications)
|
|
||||||
val resource = laneToResource(it.indications, stepData)
|
|
||||||
if (resource.isNotEmpty()) {
|
|
||||||
val id = resourceId(resource);
|
|
||||||
val bitMap = BitmapFactory.decodeResource(context.resources, id)
|
|
||||||
bitmaps.add(bitMap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return if (bitmaps.isEmpty()) {
|
|
||||||
IconCompat.createWithResource(context, R.drawable.ic_close_white_24dp)
|
|
||||||
} else {
|
|
||||||
IconCompat.createWithBitmap(overlay(bitmaps = bitmaps))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun overlay(bitmaps: List<Bitmap>): Bitmap {
|
|
||||||
val matrix = Matrix()
|
|
||||||
if (bitmaps.size == 1) {
|
|
||||||
return bitmaps.first()
|
|
||||||
}
|
|
||||||
val bmOverlay = createBitmap(
|
|
||||||
bitmaps.first().getWidth() * (bitmaps.size * 1.5).toInt(),
|
|
||||||
bitmaps.first().getHeight(),
|
|
||||||
bitmaps.first().getConfig()!!
|
|
||||||
)
|
|
||||||
val canvas = Canvas(bmOverlay)
|
|
||||||
canvas.drawBitmap(bitmaps.first(), matrix, null)
|
|
||||||
var i = 0
|
|
||||||
bitmaps.forEach {
|
|
||||||
if (i > 0) {
|
|
||||||
matrix.setTranslate(i * 45F, 0F)
|
|
||||||
canvas.drawBitmap(it, matrix, null)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return bmOverlay
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun laneToResource(directions: List<String>, stepData: StepData): String {
|
|
||||||
var direction = ""
|
|
||||||
directions.forEach {
|
|
||||||
direction = if (direction.isEmpty()) {
|
|
||||||
it.trim()
|
|
||||||
} else {
|
|
||||||
"${direction}_${it.trim()}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
direction = direction.lowercase()
|
|
||||||
return when (direction) {
|
|
||||||
"left_straight" -> {
|
|
||||||
when (stepData.currentManeuverType) {
|
|
||||||
Maneuver.TYPE_TURN_NORMAL_LEFT -> "left_o_straight_x"
|
|
||||||
Maneuver.TYPE_STRAIGHT -> "left_x_straight_o"
|
|
||||||
else
|
|
||||||
-> "left_x_straight_x"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"right_straight" -> {
|
|
||||||
when (stepData.currentManeuverType) {
|
|
||||||
Maneuver.TYPE_TURN_NORMAL_RIGHT -> "right_x_straight_x"
|
|
||||||
Maneuver.TYPE_STRAIGHT -> "right_x_straight_o"
|
|
||||||
Maneuver.TYPE_TURN_SLIGHT_RIGHT -> "right_o_straight_o"
|
|
||||||
else
|
|
||||||
-> "right_x_straight_x"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"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", "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 -> {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resourceId(
|
|
||||||
variableName: String,
|
|
||||||
): Int {
|
|
||||||
return when (variableName) {
|
|
||||||
"left_x" -> R.drawable.left_x
|
|
||||||
"left_o" -> R.drawable.left_o
|
|
||||||
"left_o_right_x" -> R.drawable.left_o_right_x
|
|
||||||
"right_x" -> R.drawable.right_x
|
|
||||||
"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
|
|
||||||
"right_x_straight_o" -> R.drawable.right_x_straight_x
|
|
||||||
"straight_o" -> R.drawable.straight_o
|
|
||||||
"left_o_straight_x" -> R.drawable.left_o_straight_x
|
|
||||||
"left_x_straight_o" -> R.drawable.left_x_straight_o
|
|
||||||
else -> {
|
|
||||||
R.drawable.left_x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@ import com.kouros.navigation.data.tomtom.Features
|
|||||||
import com.kouros.navigation.data.tomtom.Traffic
|
import com.kouros.navigation.data.tomtom.Traffic
|
||||||
import com.kouros.navigation.data.tomtom.TrafficData
|
import com.kouros.navigation.data.tomtom.TrafficData
|
||||||
import com.kouros.navigation.utils.GeoUtils.createPointCollection
|
import com.kouros.navigation.utils.GeoUtils.createPointCollection
|
||||||
|
import com.kouros.navigation.utils.Levenshtein
|
||||||
import com.kouros.navigation.utils.NavigationUtils
|
import com.kouros.navigation.utils.NavigationUtils
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
import io.objectbox.kotlin.boxFor
|
import io.objectbox.kotlin.boxFor
|
||||||
@@ -37,7 +38,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
MutableLiveData()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
val traffic: MutableLiveData<Map<String, String> > by lazy {
|
val traffic: MutableLiveData<Map<String, String>> by lazy {
|
||||||
MutableLiveData()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +78,9 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
MutableLiveData()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val maxSpeed: MutableLiveData<Int> by lazy {
|
||||||
|
MutableLiveData()
|
||||||
|
}
|
||||||
val routingEngine: MutableLiveData<Int> by lazy {
|
val routingEngine: MutableLiveData<Int> by lazy {
|
||||||
MutableLiveData()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
@@ -93,11 +97,17 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
query.close()
|
query.close()
|
||||||
for (place in results) {
|
for (place in results) {
|
||||||
val plLocation = location(place.longitude, place.latitude)
|
val plLocation = location(place.longitude, place.latitude)
|
||||||
val distance = repository.getRouteDistance(location, plLocation, carOrientation, SearchFilter(), context)
|
val distance = repository.getRouteDistance(
|
||||||
|
location,
|
||||||
|
plLocation,
|
||||||
|
carOrientation,
|
||||||
|
SearchFilter(),
|
||||||
|
context
|
||||||
|
)
|
||||||
place.distance = distance.toFloat()
|
place.distance = distance.toFloat()
|
||||||
if (place.distance > 1F) {
|
if (place.distance > 1F) {
|
||||||
recentPlace.postValue(place)
|
recentPlace.postValue(place)
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -106,7 +116,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadRecentPlaces(context: Context, location: Location, carOrientation : Float) {
|
fun loadRecentPlaces(context: Context, location: Location, carOrientation: Float) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val placeBox = boxStore.boxFor(Place::class)
|
val placeBox = boxStore.boxFor(Place::class)
|
||||||
@@ -118,16 +128,16 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
query.close()
|
query.close()
|
||||||
for (place in results) {
|
for (place in results) {
|
||||||
val plLocation = location(place.longitude, place.latitude)
|
val plLocation = location(place.longitude, place.latitude)
|
||||||
if (place.latitude != 0.0) {
|
if (place.latitude != 0.0) {
|
||||||
val distance =
|
val distance =
|
||||||
repository.getRouteDistance(
|
repository.getRouteDistance(
|
||||||
location,
|
location,
|
||||||
plLocation,
|
plLocation,
|
||||||
carOrientation,
|
carOrientation,
|
||||||
getSearchFilter(context), context
|
getSearchFilter(context), context
|
||||||
)
|
)
|
||||||
place.distance = distance.toFloat()
|
place.distance = distance.toFloat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
places.postValue(results)
|
places.postValue(results)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -149,7 +159,13 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
for (place in results) {
|
for (place in results) {
|
||||||
val plLocation = location(place.longitude, place.latitude)
|
val plLocation = location(place.longitude, place.latitude)
|
||||||
val distance =
|
val distance =
|
||||||
repository.getRouteDistance(location, plLocation, carOrientation, getSearchFilter(context), context)
|
repository.getRouteDistance(
|
||||||
|
location,
|
||||||
|
plLocation,
|
||||||
|
carOrientation,
|
||||||
|
getSearchFilter(context),
|
||||||
|
context
|
||||||
|
)
|
||||||
place.distance = distance.toFloat()
|
place.distance = distance.toFloat()
|
||||||
}
|
}
|
||||||
favorites.postValue(results)
|
favorites.postValue(results)
|
||||||
@@ -159,7 +175,12 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadRoute(context: Context, currentLocation: Location, location: Location, carOrientation : Float) {
|
fun loadRoute(
|
||||||
|
context: Context,
|
||||||
|
currentLocation: Location,
|
||||||
|
location: Location,
|
||||||
|
carOrientation: Float
|
||||||
|
) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
route.postValue(
|
route.postValue(
|
||||||
@@ -177,7 +198,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadTraffic(context: Context, currentLocation: Location, carOrientation : Float) {
|
fun loadTraffic(context: Context, currentLocation: Location, carOrientation: Float) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val data = repository.getTraffic(
|
val data = repository.getTraffic(
|
||||||
@@ -195,23 +216,34 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun rebuildTraffic(data: String) : Map<String, String> {
|
private fun rebuildTraffic(data: String): Map<String, String> {
|
||||||
val featureCollection = FeatureCollection.fromJson(data)
|
val featureCollection = FeatureCollection.fromJson(data)
|
||||||
val incidents = mutableMapOf<String, String>()
|
val incidents = mutableMapOf<String, String>()
|
||||||
val queuing = featureCollection.features()!!.filter { it.properties()!!.get("events").toString().contains("Queuing traffic")}
|
val queuing = featureCollection.features()!!
|
||||||
|
.filter { it.properties()!!.get("events").toString().contains("Queuing traffic") }
|
||||||
incidents["queuing"] = FeatureCollection.fromFeatures(queuing).toJson()
|
incidents["queuing"] = FeatureCollection.fromFeatures(queuing).toJson()
|
||||||
val stationary = featureCollection.features()!!.filter { it.properties()!!.get("events").toString().contains("Stationary traffic")}
|
val stationary = featureCollection.features()!!
|
||||||
|
.filter { it.properties()!!.get("events").toString().contains("Stationary traffic") }
|
||||||
incidents["stationary"] = FeatureCollection.fromFeatures(stationary).toJson()
|
incidents["stationary"] = FeatureCollection.fromFeatures(stationary).toJson()
|
||||||
val slow = featureCollection.features()!!.filter { it.properties()!!.get("events").toString().contains("Slow traffic")}
|
val slow = featureCollection.features()!!
|
||||||
|
.filter { it.properties()!!.get("events").toString().contains("Slow traffic") }
|
||||||
incidents["slow"] = FeatureCollection.fromFeatures(slow).toJson()
|
incidents["slow"] = FeatureCollection.fromFeatures(slow).toJson()
|
||||||
val heavy = featureCollection.features()!!.filter { it.properties()!!.get("events").toString().contains("Heavy traffic")}
|
val heavy = featureCollection.features()!!
|
||||||
|
.filter { it.properties()!!.get("events").toString().contains("Heavy traffic") }
|
||||||
incidents["heavy"] = FeatureCollection.fromFeatures(heavy).toJson()
|
incidents["heavy"] = FeatureCollection.fromFeatures(heavy).toJson()
|
||||||
val roadworks = featureCollection.features()!!.filter { it.properties()!!.get("events").toString().contains("Roadworks")}
|
val roadworks = featureCollection.features()!!
|
||||||
|
.filter { it.properties()!!.get("events").toString().contains("Roadworks") }
|
||||||
incidents["roadworks"] = FeatureCollection.fromFeatures(roadworks).toJson()
|
incidents["roadworks"] = FeatureCollection.fromFeatures(roadworks).toJson()
|
||||||
|
|
||||||
return incidents
|
return incidents
|
||||||
}
|
}
|
||||||
fun loadPreviewRoute(context: Context, currentLocation: Location, location: Location, carOrientation: Float) {
|
|
||||||
|
fun loadPreviewRoute(
|
||||||
|
context: Context,
|
||||||
|
currentLocation: Location,
|
||||||
|
location: Location,
|
||||||
|
carOrientation: Float
|
||||||
|
) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
previewRoute.postValue(
|
previewRoute.postValue(
|
||||||
@@ -277,6 +309,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchPlaces(search: String, location: Location) {
|
fun searchPlaces(search: String, location: Location) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val placesJson = repository.searchPlaces(search, location)
|
val placesJson = repository.searchPlaces(search, location)
|
||||||
@@ -320,13 +353,13 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSpeedCameras(location: Location, radius : Double) {
|
fun getSpeedCameras(location: Location, radius: Double) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val amenities = Overpass().getAmenities("highway", "speed_camera", location, radius)
|
val amenities = Overpass().getAmenities("highway", "speed_camera", location, radius)
|
||||||
val distAmenities = mutableListOf<Elements>()
|
val distAmenities = mutableListOf<Elements>()
|
||||||
amenities.forEach {
|
amenities.forEach {
|
||||||
val plLocation =
|
val plLocation =
|
||||||
location(longitude = it.lon!!, latitude = it.lat!!)
|
location(longitude = it.lon, latitude = it.lat)
|
||||||
val distance = plLocation.distanceTo(location)
|
val distance = plLocation.distanceTo(location)
|
||||||
it.distance = distance.toDouble()
|
it.distance = distance.toDouble()
|
||||||
distAmenities.add(it)
|
distAmenities.add(it)
|
||||||
@@ -336,10 +369,22 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMaxSpeed(location: Location) : List<Elements> {
|
fun getMaxSpeed(location: Location, street: String) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val levenshtein = Levenshtein()
|
||||||
val lineString = "${location.latitude},${location.longitude}"
|
val lineString = "${location.latitude},${location.longitude}"
|
||||||
val amenities = Overpass().getAround(10, lineString)
|
val amenities = Overpass().getAround(10, lineString)
|
||||||
return amenities
|
amenities.forEach {
|
||||||
|
if (it.tags.name != null) {
|
||||||
|
val distance =
|
||||||
|
levenshtein.distance(it.tags.name!!, street)
|
||||||
|
if (distance < 5) {
|
||||||
|
val speed = it.tags.maxspeed.toInt()
|
||||||
|
maxSpeed.postValue(speed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveFavorite(place: Place) {
|
fun saveFavorite(place: Place) {
|
||||||
@@ -350,7 +395,8 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
fun saveRecent(place: Place) {
|
fun saveRecent(place: Place) {
|
||||||
if (place.category == Constants.FUEL_STATION
|
if (place.category == Constants.FUEL_STATION
|
||||||
|| place.category == Constants.CHARGING_STATION
|
|| place.category == Constants.CHARGING_STATION
|
||||||
|| place.category == Constants.PHARMACY) {
|
|| place.category == Constants.PHARMACY
|
||||||
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
place.category = Constants.RECENT
|
place.category = Constants.RECENT
|
||||||
@@ -423,7 +469,11 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun loadPlaces2(context: Context, location: Location, carOrientation: Float): SnapshotStateList<Place?> {
|
fun loadPlaces2(
|
||||||
|
context: Context,
|
||||||
|
location: Location,
|
||||||
|
carOrientation: Float
|
||||||
|
): SnapshotStateList<Place?> {
|
||||||
val results = listOf<Place>()
|
val results = listOf<Place>()
|
||||||
try {
|
try {
|
||||||
val placeBox = boxStore.boxFor(Place::class)
|
val placeBox = boxStore.boxFor(Place::class)
|
||||||
@@ -436,7 +486,13 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
for (place in results) {
|
for (place in results) {
|
||||||
val plLocation = location(place.longitude, place.latitude)
|
val plLocation = location(place.longitude, place.latitude)
|
||||||
val distance =
|
val distance =
|
||||||
repository.getRouteDistance(location, plLocation, carOrientation, getSearchFilter(context), context)
|
repository.getRouteDistance(
|
||||||
|
location,
|
||||||
|
plLocation,
|
||||||
|
carOrientation,
|
||||||
|
getSearchFilter(context),
|
||||||
|
context
|
||||||
|
)
|
||||||
place.distance = distance.toFloat()
|
place.distance = distance.toFloat()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
Reference in New Issue
Block a user