FindStep
This commit is contained in:
@@ -17,8 +17,8 @@ android {
|
|||||||
applicationId = "com.kouros.navigation"
|
applicationId = "com.kouros.navigation"
|
||||||
minSdk = 33
|
minSdk = 33
|
||||||
targetSdk = 37
|
targetSdk = 37
|
||||||
versionCode = 112
|
versionCode = 118
|
||||||
versionName = "0.3.0.112"
|
versionName = "0.3.1.118"
|
||||||
base.archivesName = "navi-$versionName"
|
base.archivesName = "navi-$versionName"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ android {
|
|||||||
release {
|
release {
|
||||||
signingConfig = signingConfigs.getByName("release")
|
signingConfig = signingConfigs.getByName("release")
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
isShrinkResources = false
|
//isShrinkResources = false
|
||||||
|
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
|||||||
@@ -446,6 +446,17 @@ class RouteModelTest {
|
|||||||
assertEquals(nextStepData.instruction, "Schmalkaldener Straße")
|
assertEquals(nextStepData.instruction, "Schmalkaldener Straße")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun checkArrival() {
|
||||||
|
location.latitude = 48.116829
|
||||||
|
location.longitude = 11.594309
|
||||||
|
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()))
|
||||||
|
val stepData = routeModel.currentStep()
|
||||||
|
assertEquals(stepData.currentManeuverType, ManeuverType.TYPE_DESTINATION_LEFT.value)
|
||||||
|
assertEquals(stepData.instruction, "Hohenwaldeckstraße")
|
||||||
|
assertEquals(stepData.leftDistance, 0.0, 5.0)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun checkSchmalkadener20() {
|
fun checkSchmalkadener20() {
|
||||||
|
|||||||
@@ -80,10 +80,10 @@ class DeviceLocationManager(
|
|||||||
*/
|
*/
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
fun locationFlow(): Flow<Location> = callbackFlow {
|
fun locationFlow(): Flow<Location> = callbackFlow {
|
||||||
val listener = LocationListener { location ->
|
val listener = LocationListener { location ->
|
||||||
if (shouldUseDeviceLocation && isListening) {
|
if (shouldUseDeviceLocation && isListening) {
|
||||||
trySend(location) // Emit the location update to the flow
|
trySend(location) // Emit the location update to the flow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000L, 5f, listener)
|
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000L, 5f, listener)
|
||||||
awaitClose {
|
awaitClose {
|
||||||
@@ -101,22 +101,17 @@ class DeviceLocationManager(
|
|||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
fun startLocationUpdates(minTimeMs: Long = 1000, minDistanceM: Float = 5f) {
|
fun startLocationUpdates(minTimeMs: Long = 1000, minDistanceM: Float = 5f) {
|
||||||
if (isListening) return
|
if (isListening) return
|
||||||
val setIndividualLocation = BuildConfig.DEBUG
|
if (BuildConfig.DEBUG) {
|
||||||
|
onInitialLocation(homeVogelhart)
|
||||||
// Get and deliver last known location first
|
onLocationUpdate(homeVogelhart)
|
||||||
val lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
|
} else {
|
||||||
if (lastLocation != null) {
|
// Get and deliver last known location first
|
||||||
if (setIndividualLocation) {
|
val lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
|
||||||
onInitialLocation(homeVogelhart)
|
if (lastLocation != null) {
|
||||||
onLocationUpdate(homeVogelhart)
|
|
||||||
} else {
|
|
||||||
onInitialLocation(lastLocation)
|
onInitialLocation(lastLocation)
|
||||||
onLocationUpdate(lastLocation)
|
onLocationUpdate(lastLocation)
|
||||||
}
|
}
|
||||||
}
|
// Start continuous location updates
|
||||||
|
|
||||||
// Start continuous location updates
|
|
||||||
if (!setIndividualLocation) {
|
|
||||||
locationManager.requestLocationUpdates(
|
locationManager.requestLocationUpdates(
|
||||||
LocationManager.GPS_PROVIDER,
|
LocationManager.GPS_PROVIDER,
|
||||||
minTimeMs,
|
minTimeMs,
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
|||||||
checkArrival()
|
checkArrival()
|
||||||
}
|
}
|
||||||
val end = System.currentTimeMillis()
|
val end = System.currentTimeMillis()
|
||||||
// Log.d(TAG, "UpdateLoction ${end-start} ms")
|
Log.d(TAG, "UpdateLocation ${end-start} ms")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -493,6 +493,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
|||||||
val distance = location.distanceTo(snappedLocation)
|
val distance = location.distanceTo(snappedLocation)
|
||||||
when {
|
when {
|
||||||
distance > MAXIMAL_ROUTE_DEVIATION -> {
|
distance > MAXIMAL_ROUTE_DEVIATION -> {
|
||||||
|
Log.d(TAG, "Recalculate $distance ")
|
||||||
stopNavigation()
|
stopNavigation()
|
||||||
navigationScreen.calculateNewRoute(routeModel.navState.destination)
|
navigationScreen.calculateNewRoute(routeModel.navState.destination)
|
||||||
return false
|
return false
|
||||||
@@ -548,6 +549,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
|||||||
* Checks for arrival
|
* Checks for arrival
|
||||||
*/
|
*/
|
||||||
fun checkArrival() {
|
fun checkArrival() {
|
||||||
|
Log.d(TAG, "maneuverType ${routeModel.navState.maneuverType} Distance ${routeModel.routeCalculator.leftStepDistance()}")
|
||||||
if (routeModel.isManeuverArrival()
|
if (routeModel.isManeuverArrival()
|
||||||
&& routeModel.routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
&& routeModel.routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import com.kouros.data.R
|
|||||||
import com.kouros.navigation.data.Constants
|
import com.kouros.navigation.data.Constants
|
||||||
import com.kouros.navigation.data.HeavyColor
|
import com.kouros.navigation.data.HeavyColor
|
||||||
import com.kouros.navigation.data.LaneColor
|
import com.kouros.navigation.data.LaneColor
|
||||||
|
import com.kouros.navigation.data.NavigationCircle
|
||||||
import com.kouros.navigation.data.NavigationColorDark
|
import com.kouros.navigation.data.NavigationColorDark
|
||||||
import com.kouros.navigation.data.NavigationColorLight
|
import com.kouros.navigation.data.NavigationColorLight
|
||||||
import com.kouros.navigation.data.PharmacyColor
|
import com.kouros.navigation.data.PharmacyColor
|
||||||
@@ -161,7 +162,7 @@ fun RouteLayer(routeData: String?) {
|
|||||||
fun TrafficLayer(trafficData: Map<String, String>) {
|
fun TrafficLayer(trafficData: Map<String, String>) {
|
||||||
trafficData.forEach {
|
trafficData.forEach {
|
||||||
if (it.key == "closed") {
|
if (it.key == "closed") {
|
||||||
ClosedLayer(it)
|
//ClosedLayer(it)
|
||||||
} else {
|
} else {
|
||||||
val traffic = rememberGeoJsonSource(GeoJsonData.JsonString(it.value))
|
val traffic = rememberGeoJsonSource(GeoJsonData.JsonString(it.value))
|
||||||
LineLayer(
|
LineLayer(
|
||||||
@@ -464,7 +465,7 @@ fun NavigationImage(
|
|||||||
.size(imageSize.dp, imageSize.dp)
|
.size(imageSize.dp, imageSize.dp)
|
||||||
) {
|
) {
|
||||||
scale(scaleX = 1f, scaleY = scaleY) {
|
scale(scaleX = 1f, scaleY = scaleY) {
|
||||||
drawCircle(navigationColor.copy(alpha = 0.2f))
|
drawCircle(NavigationCircle.copy(alpha = 0.4f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Icon(
|
Icon(
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class Simulation {
|
|||||||
// Update your app's state as if a real GPS update occurred
|
// Update your app's state as if a real GPS update occurred
|
||||||
updateLocation(fakeLocation)
|
updateLocation(fakeLocation)
|
||||||
// Wait before moving to the next point (e.g., every 1 second)
|
// Wait before moving to the next point (e.g., every 1 second)
|
||||||
delay(1500)
|
delay(1000)
|
||||||
lastLocation = fakeLocation
|
lastLocation = fakeLocation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import androidx.compose.ui.graphics.Color
|
|||||||
val NavigationColorLight = Color(0xFF17A119)
|
val NavigationColorLight = Color(0xFF17A119)
|
||||||
val NavigationColorDark = Color(0xFF2B007A)
|
val NavigationColorDark = Color(0xFF2B007A)
|
||||||
|
|
||||||
|
val NavigationCircle = Color(0xFFFFEB3B)
|
||||||
|
|
||||||
val RouteColor = Color(0xFF5201B4)
|
val RouteColor = Color(0xFF5201B4)
|
||||||
|
|
||||||
val SpeedColor = Color(0xFF262525)
|
val SpeedColor = Color(0xFF262525)
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ object Constants {
|
|||||||
val homeHohenwaldeck = location( 11.594322, 48.1164817)
|
val homeHohenwaldeck = location( 11.594322, 48.1164817)
|
||||||
val ioannina = location( 20.826237, 39.690174)
|
val ioannina = location( 20.826237, 39.690174)
|
||||||
val subislawa = location(18.570808, 54.420647)
|
val subislawa = location(18.570808, 54.420647)
|
||||||
val a94 = location(11.872097,48.163449)
|
val a94 = location(11.947101,48.213251)
|
||||||
val a9 = location(11.621556, 48.204402,)
|
val a9 = location(11.621556, 48.204402,)
|
||||||
|
|
||||||
val a22 = location(10.845749, 45.602507)
|
val a22 = location(10.845749, 45.602507)
|
||||||
@@ -141,7 +141,7 @@ object Constants {
|
|||||||
|
|
||||||
const val MAXIMAL_SNAP_CORRECTION = 50.0
|
const val MAXIMAL_SNAP_CORRECTION = 50.0
|
||||||
|
|
||||||
const val MAXIMAL_ROUTE_DEVIATION = 100.0
|
const val MAXIMAL_ROUTE_DEVIATION = 150.0
|
||||||
|
|
||||||
const val DESTINATION_ARRIVAL_DISTANCE = 10.0
|
const val DESTINATION_ARRIVAL_DISTANCE = 10.0
|
||||||
|
|
||||||
|
|||||||
@@ -22,62 +22,42 @@ class RouteCalculator(var routeModel: RouteModel) {
|
|||||||
var lastSpeedIndex: Int = 0
|
var lastSpeedIndex: Int = 0
|
||||||
|
|
||||||
fun findStep(location: Location) {
|
fun findStep(location: Location) {
|
||||||
val route = routeModel.navState.route
|
|
||||||
val steps = routeModel.curLeg.steps
|
|
||||||
// Search from one step prior to handle deviations/U-turns
|
|
||||||
val startIndex = (route.currentStepIndex - 1).coerceAtLeast(0)
|
|
||||||
val endIndex = steps.size - 1
|
|
||||||
|
|
||||||
var nearestDistance = MAXIMUM_LOCATION_DISTANCE
|
var nearestDistance = MAXIMUM_LOCATION_DISTANCE
|
||||||
var searchCount = 0
|
var count = 0
|
||||||
|
var lastDistance = 0F
|
||||||
for (i in startIndex..endIndex) {
|
var increaseDistance = 0
|
||||||
val step = steps[i]
|
for ((index, step) in routeModel.curLeg.steps.withIndex()) {
|
||||||
val waypoints = step.maneuver.waypoints
|
count++
|
||||||
// Only offset search if we are exactly on the current stored step index
|
var distance = 0F
|
||||||
val startWayIndex = if (i == route.currentStepIndex) step.waypointIndex else 0
|
if (index >= routeModel.navState.route.currentStepIndex) {
|
||||||
|
for ((wayIndex, waypoint) in step.maneuver.waypoints.withIndex()) {
|
||||||
var lastDistance = Float.MAX_VALUE
|
count++
|
||||||
var increaseCount = 0
|
if (wayIndex >= step.waypointIndex) {
|
||||||
|
distance = location.distanceTo(waypoint)
|
||||||
for (j in startWayIndex until waypoints.size) {
|
if (distance < nearestDistance ) {
|
||||||
searchCount++
|
nearestDistance = distance
|
||||||
val waypoint = waypoints[j]
|
routeModel.navState.route.currentStepIndex = step.index
|
||||||
val distance = location.distanceTo(waypoint)
|
step.waypointIndex = wayIndex
|
||||||
|
step.wayPointLocation = waypoint
|
||||||
if (distance < nearestDistance) {
|
}
|
||||||
nearestDistance = distance
|
|
||||||
bestMatch = StepMatch(step.index, j, waypoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (distance > lastDistance) {
|
|
||||||
increaseCount++
|
|
||||||
} else {
|
|
||||||
increaseCount = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stopSearch(nearestDistance, distance, increaseCount)) {
|
|
||||||
bestMatch?.let { match ->
|
|
||||||
route.currentStepIndex = match.stepIndex
|
|
||||||
steps[match.stepIndex].waypointIndex = match.waypointIndex
|
|
||||||
steps[match.stepIndex].wayPointLocation = match.location
|
|
||||||
}
|
}
|
||||||
return
|
if (stopSearch(nearestDistance, distance)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (distance > lastDistance) {
|
||||||
|
increaseDistance++
|
||||||
|
}
|
||||||
|
lastDistance = distance
|
||||||
}
|
}
|
||||||
lastDistance = distance
|
|
||||||
}
|
}
|
||||||
}
|
if (stopSearch(nearestDistance, distance)) {
|
||||||
bestMatch?.let { match ->
|
break
|
||||||
route.currentStepIndex = match.stepIndex
|
}
|
||||||
steps[match.stepIndex].waypointIndex = match.waypointIndex
|
|
||||||
steps[match.stepIndex].wayPointLocation = match.location
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopSearch(nearestDistance: Float, distance: Float, increaseCount: Int): Boolean {
|
private fun stopSearch(nearestDistance: Float, distance: Float): Boolean {
|
||||||
// Increased multiplier from 10 to 20 to be less aggressive in sharp curves
|
return nearestDistance < NEAREST_LOCATION_DISTANCE && distance > NEAREST_LOCATION_DISTANCE * 10
|
||||||
return (nearestDistance < NEAREST_LOCATION_DISTANCE && distance > NEAREST_LOCATION_DISTANCE * 20)
|
|
||||||
|| increaseCount > 15
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun travelLeftTime(): Double {
|
fun travelLeftTime(): Double {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package com.kouros.navigation.model
|
package com.kouros.navigation.model
|
||||||
|
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
|
import android.util.Log
|
||||||
import androidx.car.app.connection.CarConnection.CONNECTION_TYPE_NATIVE
|
import androidx.car.app.connection.CarConnection.CONNECTION_TYPE_NATIVE
|
||||||
import androidx.car.app.connection.CarConnection.CONNECTION_TYPE_PROJECTION
|
import androidx.car.app.connection.CarConnection.CONNECTION_TYPE_PROJECTION
|
||||||
import androidx.car.app.navigation.model.Maneuver
|
import androidx.car.app.navigation.model.Maneuver
|
||||||
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
||||||
|
import com.kouros.navigation.data.Constants.TAG
|
||||||
import com.kouros.navigation.data.NavigationState
|
import com.kouros.navigation.data.NavigationState
|
||||||
import com.kouros.navigation.data.Route
|
import com.kouros.navigation.data.Route
|
||||||
import com.kouros.navigation.data.StepData
|
import com.kouros.navigation.data.StepData
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ object GeoUtils {
|
|||||||
fun movePoint(
|
fun movePoint(
|
||||||
latitude: Double,
|
latitude: Double,
|
||||||
longitude: Double,
|
longitude: Double,
|
||||||
distanceMeters: Double = 8.0,
|
distanceMeters: Double = 5.0,
|
||||||
bearingDegrees: Double = 0.0
|
bearingDegrees: Double = 0.0
|
||||||
): org.maplibre.spatialk.geojson.Point {
|
): org.maplibre.spatialk.geojson.Point {
|
||||||
val radius = EARTH_RADIUS * 1000 // meters
|
val radius = EARTH_RADIUS * 1000 // meters
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ object NavigationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun calculateZoom(speed: Double?): Double {
|
fun calculateZoom(speed: Double?): Double {
|
||||||
val zoom = 16.5
|
val zoom = 17.0
|
||||||
if (speed == null) {
|
if (speed == null) {
|
||||||
return zoom
|
return zoom
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user