TomTom Routing
This commit is contained in:
@@ -88,7 +88,7 @@ class MainActivity : ComponentActivity() {
|
||||
val routeModel = RouteModel()
|
||||
var tilt = 50.0
|
||||
val useMock = true
|
||||
val type = 3 // simulate 2 test 3 gpx
|
||||
val type = 1 // simulate 2 test 3 gpx
|
||||
|
||||
var currentIndex = 0
|
||||
val stepData: MutableLiveData<StepData> by lazy {
|
||||
@@ -309,20 +309,16 @@ class MainActivity : ComponentActivity() {
|
||||
&& lastLocation.longitude != location.position.longitude
|
||||
) {
|
||||
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)
|
||||
with(routeModel) {
|
||||
if (isNavigating()) {
|
||||
updateLocation(currentLocation, navigationViewModel)
|
||||
stepData.value = currentStep()
|
||||
println("Step: ${stepData.value!!.instruction} ${stepData.value!!.leftStepDistance}")
|
||||
if (route.currentStepIndex + 1 <= curLeg.steps.size) {
|
||||
nextStepData.value = nextStep()
|
||||
}
|
||||
if (maneuverType in 39..42
|
||||
&& leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
||||
&& routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
||||
) {
|
||||
// stopNavigation()
|
||||
arrived = true
|
||||
@@ -410,9 +406,6 @@ class MainActivity : ComponentActivity() {
|
||||
)
|
||||
val step = routeModel.currentStep()
|
||||
println("Step: ${step.instruction} ${step.leftStepDistance}")
|
||||
if (step.leftStepDistance == 70.0) {
|
||||
println("")
|
||||
}
|
||||
if (index + 1 <= routeModel.curLeg.steps.size) {
|
||||
//nextStepData.value = routeModel.nextStep()
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ fun NavigationSheet(
|
||||
val distance = (step.leftDistance / 1000).round(1)
|
||||
|
||||
if (step.lane.isNotEmpty()) {
|
||||
routeModel.addLanes( step)
|
||||
routeModel.iconMapper.addLanes( step)
|
||||
}
|
||||
|
||||
Column {
|
||||
|
||||
@@ -76,6 +76,7 @@ class SurfaceRenderer(
|
||||
val trafficData = MutableLiveData(emptyMap<String, String>())
|
||||
val speedCamerasData = MutableLiveData("")
|
||||
val speed = MutableLiveData(0F)
|
||||
val maxSpeed = MutableLiveData(0)
|
||||
var viewStyle = ViewStyle.VIEW
|
||||
lateinit var centerLocation: Location
|
||||
var previewDistance = 0.0
|
||||
@@ -209,11 +210,12 @@ class SurfaceRenderer(
|
||||
val cameraDuration =
|
||||
duration(viewStyle == ViewStyle.PREVIEW, position!!.bearing, lastBearing)
|
||||
val currentSpeed: Float? by speed.observeAsState()
|
||||
val maxSpeed: Int? by maxSpeed.observeAsState()
|
||||
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
||||
DrawNavigationImages(
|
||||
paddingValues,
|
||||
currentSpeed,
|
||||
routeModel,
|
||||
maxSpeed!!,
|
||||
width,
|
||||
height
|
||||
)
|
||||
|
||||
@@ -294,16 +294,16 @@ fun BuildingLayer(tiles: Source) {
|
||||
fun DrawNavigationImages(
|
||||
padding: PaddingValues,
|
||||
speed: Float?,
|
||||
routeModel: RouteModel,
|
||||
maxSpeed: Int,
|
||||
width: Int,
|
||||
height: Int
|
||||
) {
|
||||
NavigationImage(padding, width, height)
|
||||
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) {
|
||||
MaxSpeed(width, height, routeModel.maxSpeed)
|
||||
if (speed != null && maxSpeed > 0 && (speed * 3.6) > maxSpeed) {
|
||||
MaxSpeed(width, height, maxSpeed)
|
||||
}
|
||||
//DebugInfo(width, height, routeModel)
|
||||
}
|
||||
@@ -470,7 +470,7 @@ fun DebugInfo(
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
val textMeasurerLocation = rememberTextMeasurer()
|
||||
val location = routeModel.location.latitude.toString()
|
||||
val location = routeModel.currentLocation.latitude.toString()
|
||||
val styleSpeed = TextStyle(
|
||||
fontSize = 26.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.kouros.navigation.car.navigation
|
||||
|
||||
import android.location.Location
|
||||
import android.text.SpannableString
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
@@ -98,17 +99,17 @@ class RouteCarModel() : RouteModel() {
|
||||
}
|
||||
|
||||
fun travelEstimate(carContext: CarContext): TravelEstimate {
|
||||
val timeLeft = travelLeftTime()
|
||||
val timeLeft = routeCalculator.travelLeftTime()
|
||||
val timeToDestinationMillis =
|
||||
TimeUnit.SECONDS.toMillis(timeLeft.toLong())
|
||||
val leftDistance = travelLeftDistance() / 1000
|
||||
val leftDistance = routeCalculator.travelLeftDistance() / 1000
|
||||
val displayUnit = if (leftDistance > 1.0) {
|
||||
Distance.UNIT_KILOMETERS
|
||||
} else {
|
||||
Distance.UNIT_METERS
|
||||
}
|
||||
val arrivalTime = DateTimeWithZone.create(
|
||||
arrivalTime(),
|
||||
routeCalculator.arrivalTime(),
|
||||
TimeZone.getTimeZone("Europe/Berlin")
|
||||
)
|
||||
val travelBuilder = TravelEstimate.Builder( // The estimated distance to the destination.
|
||||
@@ -133,7 +134,7 @@ class RouteCarModel() : RouteModel() {
|
||||
return travelBuilder.build()
|
||||
}
|
||||
|
||||
fun addLanes(carContext: CarContext, step: Step.Builder, stepData: StepData) {
|
||||
fun addLanes(carContext: CarContext, step: Step.Builder, stepData: StepData) {
|
||||
var laneImageAdded = false
|
||||
stepData.lane.forEach {
|
||||
if (it.indications.isNotEmpty() && it.valid) {
|
||||
@@ -146,10 +147,10 @@ fun addLanes(carContext: CarContext, step: Step.Builder, stepData: StepData) {
|
||||
"${direction}_${it2.trim()}"
|
||||
}
|
||||
}
|
||||
val laneDirection = addLanes(direction, stepData)
|
||||
val laneDirection = iconMapper.addLanes(direction, stepData)
|
||||
if (laneDirection != LaneDirection.SHAPE_UNKNOWN) {
|
||||
if (!laneImageAdded) {
|
||||
step.setLanesImage(createCarIcon(createLaneIcon(carContext, stepData)))
|
||||
step.setLanesImage(createCarIcon(iconMapper.createLaneIcon(carContext, stepData)))
|
||||
laneImageAdded = true
|
||||
}
|
||||
val laneType =
|
||||
@@ -160,28 +161,28 @@ fun addLanes(carContext: CarContext, step: Step.Builder, stepData: StepData) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createString(
|
||||
fun createString(
|
||||
text: String
|
||||
): SpannableString {
|
||||
): SpannableString {
|
||||
val spannableString = SpannableString(text)
|
||||
return spannableString
|
||||
}
|
||||
}
|
||||
|
||||
fun createCarText(carContext: CarContext, @StringRes stringRes: Int): CarText {
|
||||
fun createCarText(carContext: CarContext, @StringRes stringRes: Int): CarText {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
// fun createCarIcon(iconCompat: IconCompat): CarIcon {
|
||||
// return CarIcon.Builder(iconCompat).build()
|
||||
// }
|
||||
fun createCarIcon(iconCompat: IconCompat): CarIcon {
|
||||
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)
|
||||
.showAlert(
|
||||
createAlert(
|
||||
@@ -190,13 +191,13 @@ fun showSpeedCamera(carContext: CarContext, distance: Double, maxSpeed: String)
|
||||
createCarIcon(carContext, R.drawable.speed_camera_24px)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun createAlert(
|
||||
fun createAlert(
|
||||
carContext: CarContext,
|
||||
maxSpeed: String?,
|
||||
icon: CarIcon
|
||||
): Alert {
|
||||
): Alert {
|
||||
val title = createCarText(carContext, R.string.speed_camera)
|
||||
val subtitle = CarText.create(maxSpeed!!)
|
||||
|
||||
@@ -215,17 +216,17 @@ fun createAlert(
|
||||
override fun onDismiss() {
|
||||
}
|
||||
}).build()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createToastAction(
|
||||
private fun createToastAction(
|
||||
carContext: CarContext,
|
||||
@StringRes titleRes: Int, @StringRes toastStringRes: Int,
|
||||
flags: Int
|
||||
): Action {
|
||||
): Action {
|
||||
return Action.Builder()
|
||||
.setOnClickListener { }
|
||||
.setTitle(createCarText(carContext, titleRes))
|
||||
.setFlags(flags)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ class NavigationScreen(
|
||||
}
|
||||
|
||||
fun getRoutingInfo(): RoutingInfo {
|
||||
var currentDistance = routeModel.leftStepDistance()
|
||||
var currentDistance = routeModel.routeCalculator.leftStepDistance()
|
||||
val displayUnit = if (currentDistance > 1000.0) {
|
||||
currentDistance /= 1000.0
|
||||
Distance.UNIT_KILOMETERS
|
||||
@@ -496,7 +496,7 @@ class NavigationScreen(
|
||||
|| maneuverType == Maneuver.TYPE_DESTINATION_LEFT
|
||||
|| maneuverType == Maneuver.TYPE_DESTINATION_RIGHT
|
||||
|| maneuverType == Maneuver.TYPE_DESTINATION_STRAIGHT)
|
||||
&& leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
||||
&& routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
||||
) {
|
||||
stopNavigation()
|
||||
arrived = true
|
||||
|
||||
@@ -13,7 +13,7 @@ class Overpass {
|
||||
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
|
||||
httpURLConnection.requestMethod = "POST"
|
||||
httpURLConnection.setRequestProperty(
|
||||
@@ -57,12 +57,13 @@ class Overpass {
|
||||
| node[$type=$category]
|
||||
| ($boundingBox);
|
||||
|);
|
||||
|(._;>;);
|
||||
|out body;
|
||||
""".trimMargin()
|
||||
return overpassApi(httpURLConnection, searchQuery)
|
||||
}
|
||||
|
||||
fun overpassApi(httpURLConnection: HttpURLConnection, searchQuery: String) : List<Elements> {
|
||||
fun overpassApi(httpURLConnection: HttpURLConnection, searchQuery: String): List<Elements> {
|
||||
try {
|
||||
val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream)
|
||||
outputStreamWriter.write(searchQuery)
|
||||
@@ -75,8 +76,10 @@ class Overpass {
|
||||
val gson = GsonBuilder().serializeNulls().create()
|
||||
val overpass = gson.fromJson(response, Amenity::class.java)
|
||||
return overpass.elements
|
||||
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
@@ -35,16 +35,16 @@ class TomTomRoute {
|
||||
var stepDuration = 0.0
|
||||
val allIntersections = mutableListOf<Intersection>()
|
||||
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 nextPointIndex = nextPointIndex(index, route)
|
||||
val maneuver = RouteManeuver(
|
||||
bearingBefore = 0,
|
||||
bearingAfter = 0,
|
||||
type = convertType(instruction.maneuver),
|
||||
waypoints = points.subList(
|
||||
lastPointIndex,
|
||||
instruction.pointIndex,
|
||||
route.guidance.instructions[nextPointIndex].pointIndex
|
||||
),
|
||||
exit = exitNumber(instruction),
|
||||
location = location(
|
||||
@@ -52,6 +52,7 @@ class TomTomRoute {
|
||||
),
|
||||
)
|
||||
|
||||
lastPointIndex = instruction.pointIndex
|
||||
val intersections = mutableListOf<Intersection>()
|
||||
route.sections.forEach { section ->
|
||||
val lanes = mutableListOf<Lane>()
|
||||
@@ -75,8 +76,8 @@ class TomTomRoute {
|
||||
}
|
||||
}
|
||||
allIntersections.addAll(intersections)
|
||||
stepDistance = route.guidance.instructions[nextPointIndex].routeOffsetInMeters - stepDistance
|
||||
stepDuration = route.guidance.instructions[nextPointIndex].travelTimeInSeconds - stepDuration
|
||||
stepDistance = route.guidance.instructions[index].routeOffsetInMeters - stepDistance
|
||||
stepDuration = route.guidance.instructions[index].travelTimeInSeconds - stepDuration
|
||||
val name = instruction.street
|
||||
val step = Step(
|
||||
index = stepIndex,
|
||||
@@ -86,8 +87,8 @@ class TomTomRoute {
|
||||
maneuver = maneuver,
|
||||
intersection = intersections
|
||||
)
|
||||
stepDistance = route.guidance.instructions[nextPointIndex].routeOffsetInMeters.toDouble()
|
||||
stepDuration = route.guidance.instructions[nextPointIndex].travelTimeInSeconds.toDouble()
|
||||
stepDistance = route.guidance.instructions[index].routeOffsetInMeters.toDouble()
|
||||
stepDuration = route.guidance.instructions[index].travelTimeInSeconds.toDouble()
|
||||
steps.add(step)
|
||||
stepIndex += 1
|
||||
}
|
||||
@@ -108,15 +109,6 @@ class TomTomRoute {
|
||||
.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 {
|
||||
var newType = 0
|
||||
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
|
||||
|
||||
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 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
|
||||
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.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
|
||||
import com.kouros.navigation.data.route.Leg
|
||||
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.location
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
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.roundToInt
|
||||
|
||||
open class RouteModel() {
|
||||
|
||||
var route = Route.Builder().buildEmpty()
|
||||
|
||||
val routeCalculator = RouteCalculator(this)
|
||||
|
||||
var iconMapper = IconMapper(this)
|
||||
var navigating: Boolean = false
|
||||
var destination: Place = Place()
|
||||
var arrived: Boolean = false
|
||||
var maneuverType: Int = 0
|
||||
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 routeBearing: Float = 0F
|
||||
|
||||
@@ -63,6 +41,7 @@ open class RouteModel() {
|
||||
val curLeg: Leg
|
||||
get() = route.routes[currentRouteIndex].legs.first()
|
||||
|
||||
|
||||
fun startNavigation(routeString: String, context: Context) {
|
||||
val routeEngine = getIntKeyValue(context = context, ROUTING_ENGINE)
|
||||
route = Route.Builder()
|
||||
@@ -88,36 +67,10 @@ open class RouteModel() {
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun updateLocation(curLocation: Location, viewModel: ViewModel) {
|
||||
location = curLocation
|
||||
findStep(curLocation)
|
||||
updateSpeedLimit(curLocation, viewModel)
|
||||
lastLocation = location
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
currentLocation = curLocation
|
||||
routeCalculator.findStep(curLocation)
|
||||
routeCalculator.updateSpeedLimit(curLocation, viewModel)
|
||||
lastLocation = currentLocation
|
||||
}
|
||||
|
||||
private fun currentLanes(location: Location): List<Lane> {
|
||||
@@ -135,68 +88,21 @@ open class RouteModel() {
|
||||
}
|
||||
}
|
||||
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 {
|
||||
val distanceToNextStep = leftStepDistance()
|
||||
val distanceToNextStep = routeCalculator.leftStepDistance()
|
||||
// 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
|
||||
val streetName = currentStep.name
|
||||
val curManeuverType = currentStep.maneuver.type
|
||||
val exitNumber = currentStep.maneuver.exit
|
||||
val maneuverIcon = maneuverIcon(curManeuverType)
|
||||
val maneuverIcon = iconMapper.maneuverIcon(curManeuverType)
|
||||
maneuverType = curManeuverType
|
||||
|
||||
val lanes = currentLanes(location)
|
||||
val lanes = currentLanes(currentLocation)
|
||||
|
||||
// Construct and return the final StepData object
|
||||
return StepData(
|
||||
@@ -204,17 +110,17 @@ open class RouteModel() {
|
||||
distanceToNextStep,
|
||||
maneuverType,
|
||||
maneuverIcon,
|
||||
arrivalTime(),
|
||||
travelLeftDistance(),
|
||||
routeCalculator.arrivalTime(),
|
||||
routeCalculator.travelLeftDistance(),
|
||||
lanes,
|
||||
exitNumber
|
||||
)
|
||||
}
|
||||
|
||||
fun nextStep(): StepData {
|
||||
val step = route.nextStep(2)
|
||||
val step = route.nextStep(1)
|
||||
val maneuverType = step.maneuver.type
|
||||
val distanceLeft = leftStepDistance()
|
||||
val distanceLeft = routeCalculator.leftStepDistance()
|
||||
var text = ""
|
||||
when (distanceLeft) {
|
||||
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
|
||||
return StepData(
|
||||
text,
|
||||
distanceLeft,
|
||||
maneuverType,
|
||||
maneuverIcon,
|
||||
arrivalTime(),
|
||||
travelLeftDistance(),
|
||||
routeCalculator.arrivalTime(),
|
||||
routeCalculator.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.currentStepIndex + 1..<curLeg.steps.size) {
|
||||
val step = curLeg.steps[i]
|
||||
timeLeft += step.duration
|
||||
fun previousStreet(): String {
|
||||
if (route.currentStepIndex > 0) {
|
||||
return route.legs().first().steps[route.currentStepIndex - 1].name
|
||||
}
|
||||
// time for current step
|
||||
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
|
||||
return ""
|
||||
}
|
||||
|
||||
fun isNavigating(): Boolean {
|
||||
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.TrafficData
|
||||
import com.kouros.navigation.utils.GeoUtils.createPointCollection
|
||||
import com.kouros.navigation.utils.Levenshtein
|
||||
import com.kouros.navigation.utils.NavigationUtils
|
||||
import com.kouros.navigation.utils.location
|
||||
import io.objectbox.kotlin.boxFor
|
||||
@@ -37,7 +38,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
MutableLiveData()
|
||||
}
|
||||
|
||||
val traffic: MutableLiveData<Map<String, String> > by lazy {
|
||||
val traffic: MutableLiveData<Map<String, String>> by lazy {
|
||||
MutableLiveData()
|
||||
}
|
||||
|
||||
@@ -77,6 +78,9 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
MutableLiveData()
|
||||
}
|
||||
|
||||
val maxSpeed: MutableLiveData<Int> by lazy {
|
||||
MutableLiveData()
|
||||
}
|
||||
val routingEngine: MutableLiveData<Int> by lazy {
|
||||
MutableLiveData()
|
||||
}
|
||||
@@ -93,7 +97,13 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
query.close()
|
||||
for (place in results) {
|
||||
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()
|
||||
if (place.distance > 1F) {
|
||||
recentPlace.postValue(place)
|
||||
@@ -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) {
|
||||
try {
|
||||
val placeBox = boxStore.boxFor(Place::class)
|
||||
@@ -149,7 +159,13 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
for (place in results) {
|
||||
val plLocation = location(place.longitude, place.latitude)
|
||||
val distance =
|
||||
repository.getRouteDistance(location, plLocation, carOrientation, getSearchFilter(context), context)
|
||||
repository.getRouteDistance(
|
||||
location,
|
||||
plLocation,
|
||||
carOrientation,
|
||||
getSearchFilter(context),
|
||||
context
|
||||
)
|
||||
place.distance = distance.toFloat()
|
||||
}
|
||||
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) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
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 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()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
|
||||
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) {
|
||||
try {
|
||||
previewRoute.postValue(
|
||||
@@ -277,6 +309,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun searchPlaces(search: String, location: Location) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
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) {
|
||||
val amenities = Overpass().getAmenities("highway", "speed_camera", location, radius)
|
||||
val distAmenities = mutableListOf<Elements>()
|
||||
amenities.forEach {
|
||||
val plLocation =
|
||||
location(longitude = it.lon!!, latitude = it.lat!!)
|
||||
location(longitude = it.lon, latitude = it.lat)
|
||||
val distance = plLocation.distanceTo(location)
|
||||
it.distance = distance.toDouble()
|
||||
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 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) {
|
||||
@@ -350,7 +395,8 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
fun saveRecent(place: Place) {
|
||||
if (place.category == Constants.FUEL_STATION
|
||||
|| place.category == Constants.CHARGING_STATION
|
||||
|| place.category == Constants.PHARMACY) {
|
||||
|| place.category == Constants.PHARMACY
|
||||
) {
|
||||
return
|
||||
}
|
||||
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>()
|
||||
try {
|
||||
val placeBox = boxStore.boxFor(Place::class)
|
||||
@@ -436,7 +486,13 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
for (place in results) {
|
||||
val plLocation = location(place.longitude, place.latitude)
|
||||
val distance =
|
||||
repository.getRouteDistance(location, plLocation, carOrientation, getSearchFilter(context), context)
|
||||
repository.getRouteDistance(
|
||||
location,
|
||||
plLocation,
|
||||
carOrientation,
|
||||
getSearchFilter(context),
|
||||
context
|
||||
)
|
||||
place.distance = distance.toFloat()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
Reference in New Issue
Block a user