TomTom Routing
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
@@ -64,7 +65,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
val placeLocation: MutableLiveData<SearchResult> by lazy {
|
||||
MutableLiveData()
|
||||
}
|
||||
|
||||
|
||||
val contactAddress: MutableLiveData<List<Place>> 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,11 +97,17 @@ 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)
|
||||
return@launch
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
} 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) {
|
||||
try {
|
||||
val placeBox = boxStore.boxFor(Place::class)
|
||||
@@ -118,16 +128,16 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
query.close()
|
||||
for (place in results) {
|
||||
val plLocation = location(place.longitude, place.latitude)
|
||||
if (place.latitude != 0.0) {
|
||||
val distance =
|
||||
repository.getRouteDistance(
|
||||
location,
|
||||
plLocation,
|
||||
carOrientation,
|
||||
getSearchFilter(context), context
|
||||
)
|
||||
place.distance = distance.toFloat()
|
||||
}
|
||||
if (place.latitude != 0.0) {
|
||||
val distance =
|
||||
repository.getRouteDistance(
|
||||
location,
|
||||
plLocation,
|
||||
carOrientation,
|
||||
getSearchFilter(context), context
|
||||
)
|
||||
place.distance = distance.toFloat()
|
||||
}
|
||||
}
|
||||
places.postValue(results)
|
||||
} catch (e: Exception) {
|
||||
@@ -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