Lanes
@@ -47,7 +47,6 @@ import com.kouros.navigation.model.BaseStyleModel
|
|||||||
import com.kouros.navigation.model.MockLocation
|
import com.kouros.navigation.model.MockLocation
|
||||||
import com.kouros.navigation.model.RouteModel
|
import com.kouros.navigation.model.RouteModel
|
||||||
import com.kouros.navigation.ui.theme.NavigationTheme
|
import com.kouros.navigation.ui.theme.NavigationTheme
|
||||||
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||||
import com.kouros.navigation.utils.bearing
|
import com.kouros.navigation.utils.bearing
|
||||||
import com.kouros.navigation.utils.calculateZoom
|
import com.kouros.navigation.utils.calculateZoom
|
||||||
@@ -62,7 +61,6 @@ import org.maplibre.compose.location.Location
|
|||||||
import org.maplibre.compose.location.rememberDefaultLocationProvider
|
import org.maplibre.compose.location.rememberDefaultLocationProvider
|
||||||
import org.maplibre.compose.location.rememberUserLocationState
|
import org.maplibre.compose.location.rememberUserLocationState
|
||||||
import org.maplibre.compose.style.BaseStyle
|
import org.maplibre.compose.style.BaseStyle
|
||||||
import org.maplibre.geojson.Point
|
|
||||||
import org.maplibre.spatialk.geojson.Position
|
import org.maplibre.spatialk.geojson.Position
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@@ -222,6 +220,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
SearchSheet(applicationContext, navigationViewModel, lastLocation) { closeSheet() }
|
SearchSheet(applicationContext, navigationViewModel, lastLocation) { closeSheet() }
|
||||||
} else {
|
} else {
|
||||||
NavigationSheet(
|
NavigationSheet(
|
||||||
|
applicationContext,
|
||||||
routeModel, step!!, nextStep!!,
|
routeModel, step!!, nextStep!!,
|
||||||
{ stopNavigation { closeSheet() } },
|
{ stopNavigation { closeSheet() } },
|
||||||
{ simulateNavigation() }
|
{ simulateNavigation() }
|
||||||
@@ -310,8 +309,11 @@ class MainActivity : ComponentActivity() {
|
|||||||
if (routeModel.isNavigating()) {
|
if (routeModel.isNavigating()) {
|
||||||
for ((index, waypoint) in routeModel.route.waypoints!!.withIndex()) {
|
for ((index, waypoint) in routeModel.route.waypoints!!.withIndex()) {
|
||||||
var deviation = 0.0
|
var deviation = 0.0
|
||||||
|
//if (index in 0..350 ) {
|
||||||
mock.setMockLocation(waypoint[1] + deviation, waypoint[0])
|
mock.setMockLocation(waypoint[1] + deviation, waypoint[0])
|
||||||
|
|
||||||
delay(500L) //
|
delay(500L) //
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
|
import android.content.Context
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.VerticalDivider
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
|
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
||||||
import com.kouros.navigation.data.StepData
|
import com.kouros.navigation.data.StepData
|
||||||
import com.kouros.navigation.model.RouteModel
|
import com.kouros.navigation.model.RouteModel
|
||||||
import com.kouros.navigation.utils.formatDateTime
|
import com.kouros.navigation.utils.formatDateTime
|
||||||
@@ -23,6 +23,7 @@ import com.kouros.navigation.utils.round
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NavigationSheet(
|
fun NavigationSheet(
|
||||||
|
applicationContext: Context,
|
||||||
routeModel: RouteModel,
|
routeModel: RouteModel,
|
||||||
step: StepData,
|
step: StepData,
|
||||||
nextStep: StepData,
|
nextStep: StepData,
|
||||||
@@ -30,6 +31,14 @@ fun NavigationSheet(
|
|||||||
simulateNavigation: () -> Unit,
|
simulateNavigation: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val distance = step.leftDistance.round(1)
|
val distance = step.leftDistance.round(1)
|
||||||
|
|
||||||
|
step.lane.forEach {
|
||||||
|
if (it.indications.isNotEmpty()) {
|
||||||
|
routeModel.createLaneIcon(applicationContext, step)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
FlowRow(horizontalArrangement = Arrangement.SpaceEvenly) {
|
FlowRow(horizontalArrangement = Arrangement.SpaceEvenly) {
|
||||||
Text(formatDateTime(step.arrivalTime), fontSize = 22.sp)
|
Text(formatDateTime(step.arrivalTime), fontSize = 22.sp)
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
|
|
||||||
var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? ->
|
var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? ->
|
||||||
val routingEngine = getIntKeyValue(carContext, ROUTING_ENGINE)
|
val routingEngine = getIntKeyValue(carContext, ROUTING_ENGINE)
|
||||||
if (routingEngine == RouteEngine.VALHALLA.ordinal) {
|
// if (routingEngine == RouteEngine.VALHALLA.ordinal) {
|
||||||
updateLocation(location!!)
|
updateLocation(location!!)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val mLifeCycleObserver: LifecycleObserver = object : DefaultLifecycleObserver {
|
private val mLifeCycleObserver: LifecycleObserver = object : DefaultLifecycleObserver {
|
||||||
@@ -97,7 +97,9 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
OnCarDataAvailableListener { data ->
|
OnCarDataAvailableListener { data ->
|
||||||
if (data.location.status == CarValue.STATUS_SUCCESS) {
|
if (data.location.status == CarValue.STATUS_SUCCESS) {
|
||||||
val location = data.location.value
|
val location = data.location.value
|
||||||
surfaceRenderer.updateCarLocation(location!!)
|
if (location != null) {
|
||||||
|
updateLocation(location)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,9 +36,12 @@ import androidx.car.app.navigation.model.Step
|
|||||||
import androidx.car.app.navigation.model.TravelEstimate
|
import androidx.car.app.navigation.model.TravelEstimate
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
|
import com.kouros.navigation.data.StepData
|
||||||
import com.kouros.navigation.model.RouteModel
|
import com.kouros.navigation.model.RouteModel
|
||||||
|
import java.util.Collections
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.text.trim
|
||||||
|
|
||||||
/** A class that provides models for the routing demos. */
|
/** A class that provides models for the routing demos. */
|
||||||
class RouteCarModel() : RouteModel() {
|
class RouteCarModel() : RouteModel() {
|
||||||
@@ -48,23 +51,16 @@ class RouteCarModel() : RouteModel() {
|
|||||||
val stepData = currentStep()
|
val stepData = currentStep()
|
||||||
val currentStepCueWithImage: SpannableString =
|
val currentStepCueWithImage: SpannableString =
|
||||||
createString(stepData.instruction)
|
createString(stepData.instruction)
|
||||||
val straightNormal =
|
|
||||||
Lane.Builder()
|
|
||||||
.addDirection(LaneDirection.create(LaneDirection.SHAPE_STRAIGHT, false))
|
|
||||||
.build()
|
|
||||||
val step =
|
val step =
|
||||||
Step.Builder(currentStepCueWithImage)
|
Step.Builder(currentStepCueWithImage)
|
||||||
.setManeuver(
|
.setManeuver(
|
||||||
Maneuver.Builder(stepData.maneuverType)
|
Maneuver.Builder(stepData.currentManeuverType)
|
||||||
.setIcon(createCarIcon(carContext, stepData.icon))
|
.setIcon(createCarIcon(carContext, stepData.icon))
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.setRoad(routeState.destination.street!!)
|
.setRoad(routeState.destination.street!!)
|
||||||
stepData.lane.forEach {
|
if (stepData.lane.isNotEmpty()) {
|
||||||
if (it.indications.isNotEmpty() ) {
|
addLanes(carContext, step, stepData)
|
||||||
step.setLanesImage(createCarIcon(createLaneIcon(carContext, stepData)))
|
|
||||||
step.addLane(straightNormal)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return step.build()
|
return step.build()
|
||||||
}
|
}
|
||||||
@@ -77,7 +73,7 @@ class RouteCarModel() : RouteModel() {
|
|||||||
val step =
|
val step =
|
||||||
Step.Builder(currentStepCueWithImage)
|
Step.Builder(currentStepCueWithImage)
|
||||||
.setManeuver(
|
.setManeuver(
|
||||||
Maneuver.Builder(stepData.maneuverType)
|
Maneuver.Builder(stepData.currentManeuverType)
|
||||||
.setIcon(createCarIcon(carContext, stepData.icon))
|
.setIcon(createCarIcon(carContext, stepData.icon))
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
@@ -121,6 +117,90 @@ class RouteCarModel() : RouteModel() {
|
|||||||
return travelBuilder.build()
|
return travelBuilder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addLanes(carContext: CarContext, step: Step.Builder, stepData: StepData) {
|
||||||
|
var laneImageAdded = false
|
||||||
|
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 = when (direction) {
|
||||||
|
"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" -> {
|
||||||
|
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" -> {
|
||||||
|
when (stepData.currentManeuverType) {
|
||||||
|
Maneuver.TYPE_TURN_SLIGHT_LEFT -> LaneDirection.SHAPE_SLIGHT_LEFT
|
||||||
|
else
|
||||||
|
-> LaneDirection.SHAPE_UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"right_slight" -> {
|
||||||
|
when (stepData.currentManeuverType) {
|
||||||
|
Maneuver.TYPE_TURN_SLIGHT_RIGHT-> LaneDirection.SHAPE_NORMAL_RIGHT
|
||||||
|
else
|
||||||
|
-> LaneDirection.SHAPE_UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
LaneDirection.SHAPE_UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (laneDirection != LaneDirection.SHAPE_UNKNOWN) {
|
||||||
|
if (!laneImageAdded) {
|
||||||
|
step.setLanesImage(createCarIcon(createLaneIcon(carContext, stepData)))
|
||||||
|
laneImageAdded = true
|
||||||
|
}
|
||||||
|
val laneType =
|
||||||
|
Lane.Builder()
|
||||||
|
.addDirection(LaneDirection.create(laneDirection, false))
|
||||||
|
.build()
|
||||||
|
step.addLane(laneType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun createString(
|
fun createString(
|
||||||
text: String
|
text: String
|
||||||
): SpannableString {
|
): SpannableString {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.location.Location
|
|||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.kouros.navigation.data.route.Lane
|
import com.kouros.navigation.data.route.Lane
|
||||||
|
import com.kouros.navigation.utils.location
|
||||||
import io.objectbox.annotation.Entity
|
import io.objectbox.annotation.Entity
|
||||||
import io.objectbox.annotation.Id
|
import io.objectbox.annotation.Id
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
@@ -58,7 +59,7 @@ data class StepData (
|
|||||||
|
|
||||||
var leftStepDistance: Double,
|
var leftStepDistance: Double,
|
||||||
|
|
||||||
var maneuverType: Int,
|
var currentManeuverType: Int,
|
||||||
|
|
||||||
var icon: Int,
|
var icon: Int,
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ data class StepData (
|
|||||||
|
|
||||||
var leftDistance: Double,
|
var leftDistance: Double,
|
||||||
|
|
||||||
var lane: List<Lane> = listOf(Lane(valid = false, indications = emptyList())),
|
var lane: List<Lane> = listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList())),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -176,7 +177,7 @@ object Constants {
|
|||||||
|
|
||||||
const val ROUTING_ENGINE = "RoutingEngine"
|
const val ROUTING_ENGINE = "RoutingEngine"
|
||||||
|
|
||||||
const val NEXT_STEP_THRESHOLD = 100.0
|
const val NEXT_STEP_THRESHOLD = 120.0
|
||||||
|
|
||||||
const val MAXIMAL_SNAP_CORRECTION = 50.0
|
const val MAXIMAL_SNAP_CORRECTION = 50.0
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ import kotlinx.serialization.json.Json
|
|||||||
|
|
||||||
abstract class NavigationRepository {
|
abstract class NavigationRepository {
|
||||||
|
|
||||||
private val nominatimUrl = "https://nominatim.openstreetmap.org/"
|
//private val nominatimUrl = "https://nominatim.openstreetmap.org/"
|
||||||
|
|
||||||
|
private val nominatimUrl = "https://kouros-online.de/nominatim/"
|
||||||
|
|
||||||
|
|
||||||
abstract fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String
|
abstract fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.kouros.navigation.data.route.Step
|
|||||||
import com.kouros.navigation.data.route.Summary
|
import com.kouros.navigation.data.route.Summary
|
||||||
import com.kouros.navigation.utils.GeoUtils.createLineStringCollection
|
import com.kouros.navigation.utils.GeoUtils.createLineStringCollection
|
||||||
import com.kouros.navigation.utils.GeoUtils.decodePolyline
|
import com.kouros.navigation.utils.GeoUtils.decodePolyline
|
||||||
|
import com.kouros.navigation.utils.location
|
||||||
|
|
||||||
class OsrmRoute {
|
class OsrmRoute {
|
||||||
|
|
||||||
@@ -34,9 +35,15 @@ class OsrmRoute {
|
|||||||
if (it2.location[0] != 0.0) {
|
if (it2.location[0] != 0.0) {
|
||||||
val lanes = mutableListOf<Lane>()
|
val lanes = mutableListOf<Lane>()
|
||||||
it2.lanes.forEach { it3 ->
|
it2.lanes.forEach { it3 ->
|
||||||
val lane = Lane(it3.valid, it3.indications)
|
if (it3.indications.isNotEmpty() && it3.indications.first() != "none") {
|
||||||
|
val lane = Lane(
|
||||||
|
location(it2.location[0], it2.location[1]),
|
||||||
|
it3.valid,
|
||||||
|
it3.indications
|
||||||
|
)
|
||||||
lanes.add(lane)
|
lanes.add(lane)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
intersections.add(Intersection(it2.location, lanes))
|
intersections.add(Intersection(it2.location, lanes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.kouros.navigation.data.route
|
package com.kouros.navigation.data.route
|
||||||
|
|
||||||
|
import android.location.Location
|
||||||
|
|
||||||
data class Lane (
|
data class Lane (
|
||||||
|
val location: Location,
|
||||||
val valid: Boolean,
|
val valid: Boolean,
|
||||||
var indications: List<String>,
|
var indications: List<String>,
|
||||||
)
|
)
|
||||||
@@ -28,6 +28,7 @@ import kotlinx.coroutines.DelicateCoroutinesApi
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import java.util.Collections
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@@ -104,7 +105,8 @@ open class RouteModel() {
|
|||||||
step.waypointIndex = wayIndex
|
step.waypointIndex = wayIndex
|
||||||
step.wayPointLocation = location(waypoint[0], waypoint[1])
|
step.wayPointLocation = location(waypoint[0], waypoint[1])
|
||||||
val bearing = routeState.lastLocation.bearingTo(location)
|
val bearing = routeState.lastLocation.bearingTo(location)
|
||||||
this.routeState = routeState.copy(lastLocation = location, bearing = bearing)
|
this.routeState =
|
||||||
|
routeState.copy(lastLocation = location, bearing = bearing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nearestDistance == 0F) {
|
if (nearestDistance == 0F) {
|
||||||
@@ -174,15 +176,15 @@ open class RouteModel() {
|
|||||||
if (shouldAdvance) {
|
if (shouldAdvance) {
|
||||||
maneuverType = relevantStep.maneuver.type
|
maneuverType = relevantStep.maneuver.type
|
||||||
}
|
}
|
||||||
val maneuverIconPair = maneuverIcon(maneuverType)
|
val maneuverIcon = maneuverIcon(maneuverType)
|
||||||
routeState = routeState.copy(maneuverType = maneuverIconPair.first)
|
routeState = routeState.copy(maneuverType = maneuverType)
|
||||||
// Construct and return the final StepData object
|
// Construct and return the final StepData object
|
||||||
val intersection = currentIntersection(routeState.location)
|
val intersection = currentIntersection(routeState.location)
|
||||||
return StepData(
|
return StepData(
|
||||||
streetName,
|
streetName,
|
||||||
distanceToNextStep,
|
distanceToNextStep,
|
||||||
maneuverIconPair.first,
|
maneuverType,
|
||||||
maneuverIconPair.second,
|
maneuverIcon,
|
||||||
arrivalTime(),
|
arrivalTime(),
|
||||||
travelLeftDistance(),
|
travelLeftDistance(),
|
||||||
intersection.lane
|
intersection.lane
|
||||||
@@ -206,13 +208,13 @@ open class RouteModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val routing: (Pair<Int, Int>) = maneuverIcon(maneuverType)
|
val maneuverIcon = maneuverIcon(maneuverType)
|
||||||
// Construct and return the final StepData object
|
// Construct and return the final StepData object
|
||||||
return StepData(
|
return StepData(
|
||||||
text,
|
text,
|
||||||
distanceLeft,
|
distanceLeft,
|
||||||
routing.first,
|
maneuverType,
|
||||||
routing.second,
|
maneuverIcon,
|
||||||
arrivalTime(),
|
arrivalTime(),
|
||||||
travelLeftDistance()
|
travelLeftDistance()
|
||||||
)
|
)
|
||||||
@@ -247,13 +249,15 @@ open class RouteModel() {
|
|||||||
/** Returns the current [Step] left distance in m. */
|
/** Returns the current [Step] left distance in m. */
|
||||||
fun leftStepDistance(): Double {
|
fun leftStepDistance(): Double {
|
||||||
val step = route.currentStep()
|
val step = route.currentStep()
|
||||||
var leftDistance = step.distance
|
var leftDistance = 0.0
|
||||||
val percent =
|
for (i in step.waypointIndex..<step.maneuver.waypoints.size - 1) {
|
||||||
100 * (step.maneuver.waypoints.size - step.waypointIndex) / (step.maneuver.waypoints.size)
|
val loc1 = location(step.maneuver.waypoints[i][0], step.maneuver.waypoints[i][1])
|
||||||
leftDistance = leftDistance * percent / 100
|
val loc2 =
|
||||||
// The remaining distance to the step, rounded to the nearest 10 units.
|
location(step.maneuver.waypoints[i + 1][0], step.maneuver.waypoints[i + 1][1])
|
||||||
return (leftDistance * 1000 / 10.0).roundToInt() * 10.0
|
val distance = loc1.distanceTo(loc2)
|
||||||
|
leftDistance += distance
|
||||||
|
}
|
||||||
|
return (leftDistance / 10.0).roundToInt() * 10.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the left distance in km. */
|
/** Returns the left distance in km. */
|
||||||
@@ -263,17 +267,11 @@ open class RouteModel() {
|
|||||||
val step = route.legs!![0].steps[i]
|
val step = route.legs!![0].steps[i]
|
||||||
leftDistance += step.distance
|
leftDistance += step.distance
|
||||||
}
|
}
|
||||||
|
leftDistance += leftStepDistance() / 1000
|
||||||
val step = route.currentStep()
|
|
||||||
val curDistance = step.distance
|
|
||||||
val percent =
|
|
||||||
100 * (step.maneuver.waypoints.size - step.waypointIndex) / (step.maneuver.waypoints.size)
|
|
||||||
val distance = curDistance * percent / 100
|
|
||||||
leftDistance += distance
|
|
||||||
return leftDistance
|
return leftDistance
|
||||||
}
|
}
|
||||||
|
|
||||||
fun maneuverIcon(routeManeuverType: Int): (Pair<Int, Int>) {
|
fun maneuverIcon(routeManeuverType: Int): Int {
|
||||||
var currentTurnIcon = R.drawable.ic_turn_name_change
|
var currentTurnIcon = R.drawable.ic_turn_name_change
|
||||||
when (routeManeuverType) {
|
when (routeManeuverType) {
|
||||||
Maneuver.TYPE_STRAIGHT -> {
|
Maneuver.TYPE_STRAIGHT -> {
|
||||||
@@ -304,9 +302,11 @@ open class RouteModel() {
|
|||||||
Maneuver.TYPE_KEEP_RIGHT -> {
|
Maneuver.TYPE_KEEP_RIGHT -> {
|
||||||
currentTurnIcon = R.drawable.ic_turn_name_change
|
currentTurnIcon = R.drawable.ic_turn_name_change
|
||||||
}
|
}
|
||||||
|
|
||||||
Maneuver.TYPE_KEEP_LEFT -> {
|
Maneuver.TYPE_KEEP_LEFT -> {
|
||||||
currentTurnIcon = R.drawable.ic_turn_name_change
|
currentTurnIcon = R.drawable.ic_turn_name_change
|
||||||
}
|
}
|
||||||
|
|
||||||
Maneuver.TYPE_ROUNDABOUT_ENTER_CCW -> {
|
Maneuver.TYPE_ROUNDABOUT_ENTER_CCW -> {
|
||||||
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
||||||
}
|
}
|
||||||
@@ -316,7 +316,7 @@ open class RouteModel() {
|
|||||||
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Pair(routeManeuverType, currentTurnIcon)
|
return currentTurnIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isNavigating(): Boolean {
|
fun isNavigating(): Boolean {
|
||||||
@@ -333,11 +333,9 @@ open class RouteModel() {
|
|||||||
fun createLaneIcon(context: Context, stepData: StepData): IconCompat {
|
fun createLaneIcon(context: Context, stepData: StepData): IconCompat {
|
||||||
val bitmaps = mutableListOf<Bitmap>()
|
val bitmaps = mutableListOf<Bitmap>()
|
||||||
stepData.lane.forEach {
|
stepData.lane.forEach {
|
||||||
if (it.indications.isNotEmpty()) {
|
if (it.indications.isNotEmpty() && it.valid) {
|
||||||
it.indications.forEach { it2 ->
|
Collections.sort<String>(it.indications)
|
||||||
val resource = laneToResource(it2, it, stepData)
|
val resource = laneToResource(it.indications, stepData)
|
||||||
if (it2 != "none") {
|
|
||||||
println("Direction $resource")
|
|
||||||
if (resource.isNotEmpty()) {
|
if (resource.isNotEmpty()) {
|
||||||
val id = resourceId(resource);
|
val id = resourceId(resource);
|
||||||
val bitMap = BitmapFactory.decodeResource(context.resources, id)
|
val bitMap = BitmapFactory.decodeResource(context.resources, id)
|
||||||
@@ -345,11 +343,12 @@ open class RouteModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return if (bitmaps.isEmpty()) {
|
||||||
|
IconCompat.createWithResource(context, R.drawable.ic_close_white_24dp)
|
||||||
|
} else {
|
||||||
|
IconCompat.createWithBitmap(overlay(bitmaps = bitmaps))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return IconCompat.createWithBitmap(overlay(bitmaps = bitmaps))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun overlay(bitmaps: List<Bitmap>): Bitmap {
|
fun overlay(bitmaps: List<Bitmap>): Bitmap {
|
||||||
val matrix = Matrix()
|
val matrix = Matrix()
|
||||||
@@ -357,7 +356,7 @@ open class RouteModel() {
|
|||||||
return bitmaps.first()
|
return bitmaps.first()
|
||||||
}
|
}
|
||||||
val bmOverlay = createBitmap(
|
val bmOverlay = createBitmap(
|
||||||
bitmaps.first().getWidth() * bitmaps.size,
|
bitmaps.first().getWidth() * (bitmaps.size) ,
|
||||||
bitmaps.first().getHeight(),
|
bitmaps.first().getHeight(),
|
||||||
bitmaps.first().getConfig()!!
|
bitmaps.first().getConfig()!!
|
||||||
)
|
)
|
||||||
@@ -366,7 +365,7 @@ open class RouteModel() {
|
|||||||
var i = 0
|
var i = 0
|
||||||
bitmaps.forEach {
|
bitmaps.forEach {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
matrix.setTranslate(i * 40F, 0F)
|
matrix.setTranslate(i * 45F, 0F)
|
||||||
canvas.drawBitmap(it, matrix, null)
|
canvas.drawBitmap(it, matrix, null)
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
@@ -374,15 +373,42 @@ open class RouteModel() {
|
|||||||
return bmOverlay
|
return bmOverlay
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun laneToResource(direction: String, lane: Lane, stepData: StepData): String {
|
private fun laneToResource(directions: List<String>, stepData: StepData): String {
|
||||||
println("Maneuver ${stepData.maneuverType}")
|
var direction = ""
|
||||||
return when (val direction = direction.replace(" ", "_")) {
|
directions.forEach {
|
||||||
"left" -> if (stepData.maneuverType == Maneuver.TYPE_TURN_NORMAL_LEFT) "${direction}_valid" else "${direction}_not_valid"
|
direction = if (direction.isEmpty()) {
|
||||||
"right" -> if (stepData.maneuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT) "${direction}_valid" else "${direction}_not_valid"
|
it.trim()
|
||||||
"straight" -> if (stepData.maneuverType == Maneuver.TYPE_STRAIGHT) "${direction}_valid" else "${direction}_not_valid"
|
} else {
|
||||||
"slight_right" -> if (stepData.maneuverType == Maneuver.TYPE_TURN_SLIGHT_RIGHT) "${direction}_valid" else "${direction}_not_valid"
|
"${direction}_${it.trim()}"
|
||||||
"slight_left" -> if (stepData.maneuverType == Maneuver.TYPE_TURN_SLIGHT_LEFT) "${direction}_valid" else "${direction}_not_valid"
|
}
|
||||||
else -> {""}
|
}
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT) "${direction}_o" else "${direction}_x"
|
||||||
|
"straight" -> if (stepData.currentManeuverType == Maneuver.TYPE_STRAIGHT) "${direction}_o" else "${direction}_x"
|
||||||
|
"right_slight" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_RIGHT) "${direction}_o" else "${direction}_x"
|
||||||
|
"left_slight" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_LEFT) "${direction}_o" else "${direction}_x"
|
||||||
|
else -> {
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,17 +416,23 @@ open class RouteModel() {
|
|||||||
variableName: String,
|
variableName: String,
|
||||||
): Int {
|
): Int {
|
||||||
return when (variableName) {
|
return when (variableName) {
|
||||||
"left_not_valid" -> R.drawable.left_not_valid
|
"left_x" -> R.drawable.left_x
|
||||||
"left_valid" -> R.drawable.left_valid
|
"left_o" -> R.drawable.left_o
|
||||||
"left_valid_right_not_valid" -> R.drawable.left_valid_right_not_valid
|
"left_o_right_x" -> R.drawable.left_o_right_x
|
||||||
"right_not_valid" -> R.drawable.right_not_valid
|
"right_x" -> R.drawable.right_x
|
||||||
"right_valid" -> R.drawable.right_valid
|
"right_o" -> R.drawable.right_o
|
||||||
"slight_right_not_valid" -> R.drawable.slight_right_not_valid
|
"slight_right_x" -> R.drawable.slight_right_x
|
||||||
"slight_right_valid" -> R.drawable.slight_right_valid
|
"slight_right_o" -> R.drawable.slight_right_o
|
||||||
"straight_not_valid" -> R.drawable.straight_not_valid
|
"straight_x" -> R.drawable.straight_x
|
||||||
"straight_not_valid_right_valid" -> R.drawable.straight_not_valid_right_valid
|
"right_o_straight_x" -> R.drawable.right_o_straight_x
|
||||||
"straight_valid" -> R.drawable.straight_valid
|
"right_x_straight_x" -> R.drawable.right_x_straight_x
|
||||||
else -> {R.drawable.ic_close_white_24dp}
|
"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.ic_close_white_24dp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 914 B After Width: | Height: | Size: 914 B |
|
Before Width: | Height: | Size: 883 B After Width: | Height: | Size: 883 B |
BIN
common/data/src/main/res/drawable/left_o_straight_x.png
Normal file
|
After Width: | Height: | Size: 881 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
BIN
common/data/src/main/res/drawable/left_x_straight_o.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 888 B After Width: | Height: | Size: 888 B |
|
Before Width: | Height: | Size: 895 B After Width: | Height: | Size: 895 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
BIN
common/data/src/main/res/drawable/right_x_straight_x.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 888 B After Width: | Height: | Size: 888 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 723 B After Width: | Height: | Size: 723 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |