Overpass, Traffic
This commit is contained in:
@@ -17,8 +17,8 @@ android {
|
||||
applicationId = "com.kouros.navigation"
|
||||
minSdk = 33
|
||||
targetSdk = 37
|
||||
versionCode = 98
|
||||
versionName = "0.2.3.98"
|
||||
versionCode = 103
|
||||
versionName = "0.3.0.103"
|
||||
base.archivesName = "navi-$versionName"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ fun MapView(
|
||||
duration = 1.seconds
|
||||
)
|
||||
}
|
||||
NavigationImage(paddingValues, width, height / 6, "", dark)
|
||||
NavigationImage(paddingValues, width, height / 6, "", dark, tilt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,31 +17,45 @@ class OverpassTest {
|
||||
@Test
|
||||
fun `maxSpeed Schmalkaldener 30 `() {
|
||||
val curLocation = location(11.582495, 48.186863)
|
||||
executeSpeedTest( curLocation, "Schmalkaldener Straße", emptyList(), 90, 30)
|
||||
executeSpeedTest(curLocation, "Schmalkaldener Straße", emptyList(), 90, 30)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `maxSpeed Ingolstädter 50 `() {
|
||||
val curLocation = location(11.584384, 48.186338)
|
||||
executeSpeedTest( curLocation, "Ingolstädter Straße", listOf("B13"), 180, 50)
|
||||
executeSpeedTest(curLocation, "Ingolstädter Straße", listOf("B13"), 180, 50)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `maxSpeed Isarring 50 `() {
|
||||
val curLocation = location(11.5989114, 48.1694783)
|
||||
executeSpeedTest( curLocation, "Isarring", listOf("B2R"), 190, 60)
|
||||
fun `maxSpeed Isarring `() {
|
||||
var curLocation = location(11.5999989, 48.1732329)
|
||||
executeSpeedTest(curLocation, "Isarring", listOf("B2R"), 200, 60)
|
||||
|
||||
curLocation = location(11.5995437, 48.1703931)
|
||||
executeSpeedTest(curLocation, "Isarring", listOf("B2R"), 204, 50)
|
||||
|
||||
val locations = listOf(
|
||||
location( 11.5964253, 48.1658679),
|
||||
location( 11.5960449, 48.1650396),
|
||||
location( 11.5959427, 48.1645584),
|
||||
location( 11.5959714, 48.1641186),
|
||||
)
|
||||
locations.forEach {
|
||||
executeSpeedTest(it, "Biedersteiner", listOf("B2R"), 190, 60)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `maxSpeed A94 `() {
|
||||
val curLocation = location(11.88117, 48.16595)
|
||||
executeSpeedTest( curLocation, "", listOf("A94", "E552"), 90, 130)
|
||||
executeSpeedTest(curLocation, "", listOf("A94", "E552"), 90, 130)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `maxSpeed Fendsbach `() {
|
||||
val curLocation = location(11.94989, 48.21522)
|
||||
executeSpeedTest( curLocation, "Fendsbach", listOf("St 2331"), 0, 60)
|
||||
executeSpeedTest(curLocation, "Fendsbach", listOf("St 2331"), 0, 60)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -53,10 +67,10 @@ class OverpassTest {
|
||||
location(11.5850147, 48.1774400)
|
||||
)
|
||||
|
||||
executeSpeedTest( locations[0], "Leopoldstraße", emptyList(), 0, 50)
|
||||
executeSpeedTest( locations[1], "Leopoldstraße", emptyList(), 180, 30)
|
||||
executeSpeedTest( locations[2], "Leopoldstraße", emptyList(), 180, 30)
|
||||
executeSpeedTest( locations[3], "Leopoldstraße", emptyList(), 180, 50)
|
||||
executeSpeedTest(locations[0], "Leopoldstraße", emptyList(), 0, 50)
|
||||
executeSpeedTest(locations[1], "Leopoldstraße", emptyList(), 180, 30)
|
||||
executeSpeedTest(locations[2], "Leopoldstraße", emptyList(), 180, 30)
|
||||
executeSpeedTest(locations[3], "Leopoldstraße", emptyList(), 180, 50)
|
||||
|
||||
}
|
||||
|
||||
@@ -67,8 +81,8 @@ class OverpassTest {
|
||||
location(20.686672, 39.838547),
|
||||
)
|
||||
|
||||
executeSpeedTest( locations[0], "", listOf("E90", "E92"), 100, 120)
|
||||
executeSpeedTest( locations[1], "", listOf("E853"), 320, 90)
|
||||
executeSpeedTest(locations[0], "", listOf("E90", "E92"), 100, 120)
|
||||
executeSpeedTest(locations[1], "", listOf("E853"), 320, 90)
|
||||
}
|
||||
|
||||
fun executeSpeedTest(
|
||||
|
||||
@@ -36,9 +36,9 @@ class RouteModelTest {
|
||||
|
||||
val distance = listOf(
|
||||
1025.5,
|
||||
989.8,
|
||||
963.5,
|
||||
923.7,
|
||||
1012.8,
|
||||
979.5,
|
||||
972.7,
|
||||
915.8,
|
||||
914.6,
|
||||
871.0,
|
||||
@@ -130,7 +130,7 @@ class RouteModelTest {
|
||||
} else {
|
||||
assertEquals(stepData.currentManeuverType, ManeuverType.TYPE_TURN_NORMAL_LEFT.value)
|
||||
}
|
||||
assertEquals(stepData.leftStepDistance, 301.0, 1.0)
|
||||
assertEquals(stepData.leftStepDistance, 297.0, 1.0)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -142,7 +142,7 @@ class RouteModelTest {
|
||||
val stepData = routeModel.currentStep()
|
||||
assertEquals(stepData.currentManeuverType, ManeuverType.TYPE_TURN_NORMAL_LEFT.value)
|
||||
assertEquals(stepData.instruction, "Schenkendorfstraße")
|
||||
assertEquals(stepData.leftStepDistance, 170.0, 10.0)
|
||||
assertEquals(stepData.leftStepDistance, 116.0, 10.0)
|
||||
assertEquals(stepData.lane.size, 4)
|
||||
assertEquals(stepData.lane.first().valid, true)
|
||||
assertEquals(stepData.lane.last().valid, false)
|
||||
@@ -227,10 +227,15 @@ class RouteModelTest {
|
||||
|
||||
@Test
|
||||
fun `leftStepDistance Inglolstädter `() {
|
||||
val location: Location = location(11.584578, 48.183653)
|
||||
|
||||
var location = location( 11.584352, 48.186771)
|
||||
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()))
|
||||
val step = routeModel.currentStep()
|
||||
assertEquals(step.leftStepDistance, 645.0, 1.0)
|
||||
var step = routeModel.currentStep()
|
||||
assertEquals(step.leftStepDistance, 1019.0, 1.0)
|
||||
location = location(11.584578, 48.183653)
|
||||
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()))
|
||||
step = routeModel.currentStep()
|
||||
assertEquals(step.leftStepDistance, 638.0, 1.0)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -107,8 +107,8 @@ class DeviceLocationManager(
|
||||
val lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
|
||||
if (lastLocation != null) {
|
||||
if (setIndividualLocation) {
|
||||
onInitialLocation(a22)
|
||||
onLocationUpdate(a22)
|
||||
onInitialLocation(lastLocation)
|
||||
onLocationUpdate(lastLocation)
|
||||
} else {
|
||||
onInitialLocation(lastLocation)
|
||||
onLocationUpdate(lastLocation)
|
||||
|
||||
@@ -122,16 +122,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
*/
|
||||
private val lifecycleObserver: LifecycleObserver = object : DefaultLifecycleObserver {
|
||||
|
||||
override fun onPause(owner: LifecycleOwner) {
|
||||
Log.d(TAG, "NavigationSession paused")
|
||||
super.onPause(owner)
|
||||
}
|
||||
|
||||
override fun onResume(owner: LifecycleOwner) {
|
||||
Log.d(TAG, "NavigationSession resumed")
|
||||
super.onResume(owner)
|
||||
}
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
if (::navigationManager.isInitialized) {
|
||||
navigationManager.clearNavigationManagerCallback()
|
||||
@@ -465,25 +455,23 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
*/
|
||||
private fun handleNavigationLocation(location: Location) {
|
||||
val startTime = System.currentTimeMillis()
|
||||
routeModel.updateLocation(location, navigationViewModel) // 30 ms
|
||||
val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations()) // 160 ms
|
||||
routeModel.updateLocation(location, navigationViewModel)
|
||||
val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations())
|
||||
val streetName = routeModel.currentStep().street
|
||||
if (checkLocationDeviation(location, snappedLocation, streetName)) { // 1 ms
|
||||
if (checkLocationDeviation(location, snappedLocation, streetName)) {
|
||||
if (routeModel.navState.arrived) return
|
||||
if (guidanceAudio == 1) {
|
||||
handleGuidanceAudio()
|
||||
}
|
||||
val currentDate = LocalDateTime.now(ZoneOffset.UTC)
|
||||
checkTraffic(currentDate, snappedLocation) // 0 ms
|
||||
updateSpeedCamera(snappedLocation) // 0 ms
|
||||
checkRoute(currentDate, snappedLocation) // 0 ms
|
||||
updateNavigationScreen() // 660 ms
|
||||
val endTime = System.currentTimeMillis() - startTime
|
||||
// Log.d(TAG, "updateNavigationScreen: $endTime")
|
||||
checkTraffic(currentDate, snappedLocation)
|
||||
updateSpeedCamera(snappedLocation)
|
||||
checkRoute(currentDate, snappedLocation)
|
||||
updateNavigationScreen()
|
||||
checkArrival()
|
||||
|
||||
}
|
||||
|
||||
val endTime = System.currentTimeMillis() - startTime
|
||||
//Log.d(TAG, "handleNavigationLocation: $endTime")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -565,7 +553,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
* Called when user starts navigation
|
||||
*/
|
||||
override fun startNavigation() {
|
||||
Log.d(TAG, "startNavigation")
|
||||
surfaceRenderer.navigation = true
|
||||
surfaceRenderer.viewStyle = ViewStyle.VIEW
|
||||
navigationManager.navigationStarted()
|
||||
@@ -584,7 +571,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
* Called when user exits navigation or arrives at destination.
|
||||
*/
|
||||
override fun stopNavigation() {
|
||||
Log.d(TAG, "stopNavigation")
|
||||
surfaceRenderer.navigation = false
|
||||
routeModel.stopNavigation()
|
||||
navigationManager.navigationEnded()
|
||||
@@ -636,7 +622,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
* Starts navigation and invalidates the screen.
|
||||
*/
|
||||
override fun onRouteReceived(route: String) {
|
||||
Log.d(TAG, "onRouteReceived")
|
||||
if (route.isNotEmpty()) {
|
||||
this.route = route
|
||||
if (routeModel.isNavigating()) {
|
||||
@@ -722,7 +707,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
* Loads a route to the specified place and sets it as the destination.
|
||||
*/
|
||||
override fun navigateToPlace(place: Place) {
|
||||
Log.d(TAG, "navigateToPlace ${place.street}")
|
||||
var prevDestination = Place()
|
||||
if (surfaceRenderer.navigation) {
|
||||
prevDestination = routeModel.navState.destination
|
||||
|
||||
@@ -340,7 +340,8 @@ class SurfaceRenderer(
|
||||
width,
|
||||
height,
|
||||
streetName,
|
||||
darkMode
|
||||
darkMode,
|
||||
tilt
|
||||
)
|
||||
}
|
||||
LaunchedEffect(position, viewStyle) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import androidx.compose.ui.text.drawText
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.kouros.data.R
|
||||
@@ -45,6 +46,7 @@ import org.maplibre.compose.expressions.dsl.image
|
||||
import org.maplibre.compose.expressions.dsl.interpolate
|
||||
import org.maplibre.compose.expressions.dsl.zoom
|
||||
import org.maplibre.compose.expressions.value.ColorValue
|
||||
import org.maplibre.compose.expressions.value.DpValue
|
||||
import org.maplibre.compose.layers.Anchor
|
||||
import org.maplibre.compose.layers.FillLayer
|
||||
import org.maplibre.compose.layers.LineLayer
|
||||
@@ -107,11 +109,15 @@ fun MapLibre(
|
||||
BuildingLayer(tiles)
|
||||
}
|
||||
if (viewStyle == ViewStyle.AMENITY_VIEW) {
|
||||
val lastLocation = location(cameraState.position.target.longitude, cameraState.position.target.latitude)
|
||||
val lastLocation = location(
|
||||
cameraState.position.target.longitude,
|
||||
cameraState.position.target.latitude
|
||||
)
|
||||
Puck(cameraState, lastLocation)
|
||||
AmenityLayer(route)
|
||||
} else {
|
||||
RouteLayer(route, traffic!!)
|
||||
RouteLayer(route)
|
||||
TrafficLayer(traffic!!)
|
||||
StartEndLayer(route)
|
||||
//RouteLayerPoint(route )
|
||||
}
|
||||
@@ -146,72 +152,60 @@ fun StartEndLayer(routeData: String?) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RouteLayer(routeData: String?, trafficData: Map<String, String>) {
|
||||
fun RouteLayer(routeData: String?) {
|
||||
if (!routeData.isNullOrEmpty()) {
|
||||
val routes = rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
|
||||
LineLayer(
|
||||
id = "routes-casing",
|
||||
source = routes,
|
||||
color = const(Color.White),
|
||||
width =
|
||||
interpolate(
|
||||
type = exponential(1.2f),
|
||||
input = zoom(),
|
||||
5 to const(0.4.dp),
|
||||
6 to const(0.8.dp),
|
||||
7 to const(2.0.dp),
|
||||
20 to const(24.dp),
|
||||
),
|
||||
color = const(Color.Green),
|
||||
width = routeLineWidth(base = 1.dp, isCasing = true),
|
||||
)
|
||||
LineLayer(
|
||||
id = "routes",
|
||||
source = routes,
|
||||
color = const(RouteColor),
|
||||
width =
|
||||
interpolate(
|
||||
type = exponential(1.2f),
|
||||
input = zoom(),
|
||||
5 to const(0.7.dp),
|
||||
6 to const(1.0.dp),
|
||||
7 to const(2.4.dp),
|
||||
20 to const(26.dp),
|
||||
),
|
||||
width = routeLineWidth(base = 1.dp, isCasing = false),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TrafficLayer(trafficData: Map<String, String>) {
|
||||
trafficData.forEach {
|
||||
val traffic = rememberGeoJsonSource(GeoJsonData.JsonString(it.value))
|
||||
LineLayer(
|
||||
id = "traffic-${it.key}-casing",
|
||||
source = traffic,
|
||||
color = const(Color.White),
|
||||
width =
|
||||
interpolate(
|
||||
type = exponential(1.2f),
|
||||
input = zoom(),
|
||||
5 to const(0.4.dp),
|
||||
6 to const(0.6.dp),
|
||||
7 to const(1.8.dp),
|
||||
20 to const(20.dp),
|
||||
),
|
||||
width = routeLineWidth(base = 2.dp, isCasing = true),
|
||||
)
|
||||
LineLayer(
|
||||
id = "traffic-${it.key}",
|
||||
source = traffic,
|
||||
color = trafficColor(it.key),
|
||||
width =
|
||||
interpolate(
|
||||
type = exponential(1.2f),
|
||||
input = zoom(),
|
||||
5 to const(0.4.dp),
|
||||
6 to const(0.5.dp),
|
||||
7 to const(1.6.dp),
|
||||
20 to const(18.dp),
|
||||
),
|
||||
width = routeLineWidth(base = 2.dp, isCasing = false),
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reusable helper for consistent line widths across all route layers.
|
||||
*/
|
||||
@Composable
|
||||
private fun routeLineWidth(base: Dp, isCasing: Boolean = false): Expression<DpValue> {
|
||||
val extra = if (isCasing) 1.dp else 0.dp
|
||||
val width = base + extra
|
||||
|
||||
return interpolate(
|
||||
type = exponential(1.1f),
|
||||
input = zoom(),
|
||||
5 to const(width + 3.dp),
|
||||
10 to const(width + 8.dp),
|
||||
15 to const(width + 13.dp),
|
||||
20 to const(width + 18.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RouteLayerPoint(routeData: String?) {
|
||||
@@ -244,7 +238,7 @@ fun trafficColor(key: String): Expression<ColorValue> {
|
||||
"slow" -> return const(Color(0xFFC43E3E))
|
||||
"stationary" -> return const(Color(0xFF910A0A))
|
||||
"heavy" -> return const(Color(0xFF6B0404))
|
||||
"roadworks" -> return const(Color(0xFF443506))
|
||||
"roadworks" -> return const(Color(0xFFDAA707))
|
||||
}
|
||||
return const(Color.Blue)
|
||||
}
|
||||
@@ -325,8 +319,9 @@ fun DrawNavigationImages(
|
||||
height: Int,
|
||||
streetName: String?,
|
||||
darkMode: Boolean,
|
||||
tilt: Double,
|
||||
) {
|
||||
NavigationImage(padding, width, height, streetName, darkMode)
|
||||
NavigationImage(padding, width, height, streetName, darkMode, tilt)
|
||||
if (speed != null) {
|
||||
CurrentSpeed(width, height, speed, maxSpeed)
|
||||
}
|
||||
@@ -342,7 +337,8 @@ fun NavigationImage(
|
||||
width: Int,
|
||||
height: Int,
|
||||
streetName: String?,
|
||||
darkMode: Boolean
|
||||
darkMode: Boolean,
|
||||
tilt: Double
|
||||
) {
|
||||
|
||||
val imageSize = (height / 8)
|
||||
@@ -362,12 +358,17 @@ fun NavigationImage(
|
||||
textMeasurerStreet.measure(street, styleStreet, overflow = TextOverflow.Ellipsis)
|
||||
}
|
||||
|
||||
val scaleY = if (tilt == 0.0) {
|
||||
1F
|
||||
} else {
|
||||
0.7F
|
||||
}
|
||||
Box(contentAlignment = Alignment.Center, modifier = Modifier.padding(padding)) {
|
||||
Canvas(
|
||||
modifier = Modifier
|
||||
.size(imageSize.dp, imageSize.dp)
|
||||
) {
|
||||
scale(scaleX = 1f, scaleY = 0.7f) {
|
||||
scale(scaleX = 1f, scaleY = scaleY) {
|
||||
drawCircle(navigationColor.copy(alpha = 0.3f))
|
||||
}
|
||||
}
|
||||
@@ -377,19 +378,19 @@ fun NavigationImage(
|
||||
tint = navigationColor.copy(alpha = 0.7f),
|
||||
modifier = Modifier
|
||||
.size(imageSize.dp, imageSize.dp)
|
||||
.scale(scaleX = 1f, scaleY = 0.7f),
|
||||
.scale(scaleX = 1f, scaleY = scaleY),
|
||||
)
|
||||
|
||||
Canvas(
|
||||
modifier = Modifier
|
||||
.size(textLayoutStreet.size.width.dp, textLayoutStreet.size.height.dp * 6 )
|
||||
.size(textLayoutStreet.size.width.dp, textLayoutStreet.size.height.dp * 6)
|
||||
) {
|
||||
if (street.isNotEmpty()) {
|
||||
val topLeftX = center.x - textLayoutStreet.size.width / 2
|
||||
val topLeftY = center.y + textLayoutStreet.size.height
|
||||
drawRoundRect(
|
||||
topLeft = Offset(
|
||||
x = topLeftX ,
|
||||
x = topLeftX,
|
||||
y = topLeftY,
|
||||
),
|
||||
color = if (darkMode) NavigationColorLight else Color.White,
|
||||
@@ -431,7 +432,8 @@ private fun CurrentSpeed(
|
||||
val textMeasurerSpeed = rememberTextMeasurer()
|
||||
val textMeasurerKm = rememberTextMeasurer()
|
||||
|
||||
val speed = if (isMetricSystem()) (curSpeed * 3.6).toInt().toString() else (curSpeed * 3.6 * 0.6214).toInt().toString()
|
||||
val speed = if (isMetricSystem()) (curSpeed * 3.6).toInt()
|
||||
.toString() else (curSpeed * 3.6 * 0.6214).toInt().toString()
|
||||
|
||||
val kmh = if (isMetricSystem()) "km/h" else "mph"
|
||||
|
||||
|
||||
@@ -126,10 +126,9 @@ class RouteCarModel : RouteModel() {
|
||||
.setRemainingTimeColor(CarColor.GREEN)
|
||||
.setRemainingDistanceColor(CarColor.BLUE)
|
||||
if (traffic > 0) {
|
||||
travelBuilder.setTripText(createDelay(traffic))
|
||||
travelBuilder.setTripIcon(createCarIcon(carContext, R.drawable.traffic_jam_48px))
|
||||
travelBuilder.setTripText(createDelay(traffic))
|
||||
}
|
||||
|
||||
if (navState.travelMessage.isNotEmpty()) {
|
||||
travelBuilder.setTripIcon(createCarIcon(carContext, R.drawable.warning_24px))
|
||||
travelBuilder.setTripText(CarText.create(navState.travelMessage))
|
||||
|
||||
@@ -4,9 +4,9 @@ import androidx.compose.ui.graphics.Color
|
||||
|
||||
val NavigationColorLight = Color(0xFF17A119)
|
||||
|
||||
val NavigationColorDark = Color(0xFF03411F)
|
||||
val NavigationColorDark = Color(0xFF07568C)
|
||||
|
||||
val RouteColor = Color(0xFF195D02)
|
||||
val RouteColor = Color(0xFF5201B4)
|
||||
|
||||
val SpeedColor = Color(0xFF262525)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.kouros.navigation.data.tomtom.TomTomRoute
|
||||
import com.kouros.navigation.data.valhalla.ValhallaResponse
|
||||
import com.kouros.navigation.data.valhalla.ValhallaRoute
|
||||
import com.kouros.navigation.utils.location
|
||||
import kotlinx.coroutines.selects.whileSelect
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
@@ -102,17 +103,23 @@ data class Route(
|
||||
return if (isRouteValid()) {
|
||||
legs().first().steps[currentStepIndex]
|
||||
} else {
|
||||
Step(maneuver = Maneuver(waypoints = emptyList(), location = location(0.0, 0.0)))
|
||||
Step(maneuver = Maneuver(waypoints = emptyList(), location = location(0.0, 0.0), leftDistance = emptyList()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maneuver locations to snap location
|
||||
*/
|
||||
fun maneuverLocations(): List<Point> {
|
||||
val wayPointIndex = currentStep().waypointIndex
|
||||
val waypoints = currentStep().maneuver.waypoints
|
||||
val points = mutableListOf<Point>()
|
||||
for (loc in waypoints) {
|
||||
for ((index,loc) in waypoints.withIndex()) {
|
||||
if (index >= wayPointIndex && points.size < 20) {
|
||||
val point = Point.fromLngLat(loc[0], loc[1])
|
||||
points.add(point)
|
||||
}
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.kouros.navigation.utils.GeoUtils.createCenterLocation
|
||||
import com.kouros.navigation.utils.GeoUtils.createLineStringCollection
|
||||
import com.kouros.navigation.utils.GeoUtils.decodePolyline
|
||||
import com.kouros.navigation.utils.location
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
class OsrmRoute {
|
||||
|
||||
@@ -27,8 +28,22 @@ class OsrmRoute {
|
||||
val steps = mutableListOf<Step>()
|
||||
leg.steps.forEach { step ->
|
||||
val intersections = mutableListOf<Intersection>()
|
||||
val leftDistance = mutableListOf<Float>()
|
||||
var lastLocation = location(0.0,0.0)
|
||||
val points = decodePolyline(step.geometry, 5)
|
||||
waypoints.addAll(points)
|
||||
// calculate left step distance for each point
|
||||
var leftStepDistance = step.distance
|
||||
leftDistance.add(leftStepDistance.toFloat())
|
||||
points.forEach {
|
||||
if (lastLocation.latitude != 0.0) {
|
||||
val curLocation = location(it[0], it[1])
|
||||
val dist = curLocation.distanceTo(lastLocation).absoluteValue
|
||||
leftStepDistance -= dist
|
||||
leftDistance.add(leftStepDistance.toFloat())
|
||||
}
|
||||
lastLocation = location(it[0], it[1])
|
||||
}
|
||||
val maneuver = RouteManeuver(
|
||||
bearingBefore = step.maneuver.bearingBefore,
|
||||
bearingAfter = step.maneuver.bearingAfter,
|
||||
@@ -38,7 +53,8 @@ class OsrmRoute {
|
||||
location = location(
|
||||
step.maneuver.location[0],
|
||||
step.maneuver.location[1]
|
||||
)
|
||||
),
|
||||
leftDistance = leftDistance
|
||||
)
|
||||
step.intersections.forEach { it2 ->
|
||||
if (it2.location[0] != 0.0) {
|
||||
|
||||
@@ -56,7 +56,6 @@ class Overpass {
|
||||
""".trimMargin()
|
||||
|
||||
Log.d("OverpassApi", "Overpass Query: $searchQuery")
|
||||
|
||||
val connection = (URL(overpassUrl).openConnection() as HttpURLConnection).apply {
|
||||
requestMethod = "POST"
|
||||
setRequestProperty("Accept", "application/json")
|
||||
|
||||
@@ -9,6 +9,7 @@ data class Tags(
|
||||
val lanes: String = "",
|
||||
val lit: String = "",
|
||||
val maxspeed: String = "0",
|
||||
@SerializedName("maxspeed:variable") val maxSpeedVariable: String = "",
|
||||
val name: String = "",
|
||||
val oneway: String = "",
|
||||
val ref: String = "",
|
||||
|
||||
@@ -12,6 +12,7 @@ data class Maneuver(
|
||||
val street: String = "",
|
||||
val message: String = "",
|
||||
val pointIndex: Int = 0,
|
||||
val leftDistance: List<Float>
|
||||
)
|
||||
|
||||
enum class ManeuverType(val value: Int) {
|
||||
|
||||
@@ -40,8 +40,8 @@ class TomTomRepository : NavigationRepository() {
|
||||
}
|
||||
if (useLocal) {
|
||||
return fetchUrl(
|
||||
//"http://192.168.1.37/tomtom_routing.json",
|
||||
"http://192.168.1.37/verona.json",
|
||||
"http://192.168.1.37/tomtom_routing.json",
|
||||
//"http://192.168.1.37/verona.json",
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ import com.kouros.navigation.data.route.Summary
|
||||
import com.kouros.navigation.utils.GeoUtils.createCenterLocation
|
||||
import com.kouros.navigation.utils.GeoUtils.createLineStringCollection
|
||||
import com.kouros.navigation.utils.GeoUtils.decodePolyline
|
||||
import com.kouros.navigation.utils.isNumeric
|
||||
import com.kouros.navigation.utils.location
|
||||
import kotlin.math.absoluteValue
|
||||
import com.kouros.navigation.data.route.Maneuver as RouteManeuver
|
||||
|
||||
|
||||
@@ -53,6 +55,27 @@ class TomTomRoute {
|
||||
val instruction = route.guidance.instructions[index]
|
||||
val street = lastInstruction.street ?: ""
|
||||
val maneuverStreet = instruction.street ?: ""
|
||||
val leftDistance = mutableListOf<Float>()
|
||||
var lastLocation = location(0.0, 0.0)
|
||||
stepDistance =
|
||||
route.guidance.instructions[index].routeOffsetInMeters - stepDistance
|
||||
stepDuration =
|
||||
route.guidance.instructions[index].travelTimeInSeconds - stepDuration
|
||||
// calculate left step distance for each point
|
||||
var leftStepDistance = stepDistance
|
||||
leftDistance.add(leftStepDistance.toFloat())
|
||||
points.subList(
|
||||
lastPointIndex,
|
||||
instruction.pointIndex + 1,
|
||||
).forEach {
|
||||
if (lastLocation.latitude != 0.0) {
|
||||
val curLocation = location(it[0], it[1])
|
||||
val dist = curLocation.distanceTo(lastLocation).absoluteValue
|
||||
leftStepDistance -= dist
|
||||
leftDistance.add(leftStepDistance.toFloat())
|
||||
}
|
||||
lastLocation = location(it[0], it[1])
|
||||
}
|
||||
val maneuver = RouteManeuver(
|
||||
bearingBefore = 0,
|
||||
bearingAfter = 0,
|
||||
@@ -67,9 +90,9 @@ class TomTomRoute {
|
||||
),
|
||||
street = maneuverStreet,
|
||||
message = instruction.message,
|
||||
pointIndex = instruction.pointIndex
|
||||
pointIndex = instruction.pointIndex,
|
||||
leftDistance = leftDistance
|
||||
)
|
||||
|
||||
lastPointIndex = instruction.pointIndex
|
||||
val intersections = mutableListOf<Intersection>()
|
||||
route.sections?.forEach { section ->
|
||||
@@ -95,10 +118,7 @@ class TomTomRoute {
|
||||
intersections.add(Intersection(waypoints[startIndex], lanes))
|
||||
}
|
||||
}
|
||||
stepDistance =
|
||||
route.guidance.instructions[index].routeOffsetInMeters - stepDistance
|
||||
stepDuration =
|
||||
route.guidance.instructions[index].travelTimeInSeconds - stepDuration
|
||||
|
||||
val roadNumbers = if (lastInstruction.roadNumbers != null) {
|
||||
lastInstruction.roadNumbers
|
||||
} else {
|
||||
@@ -212,6 +232,7 @@ class TomTomRoute {
|
||||
"TAKE_EXIT" -> {
|
||||
newType = ManeuverType.TYPE_TURN_SLIGHT_RIGHT.value
|
||||
}
|
||||
|
||||
"WAYPOINT_RIGHT" -> {
|
||||
newType = ManeuverType.TYPE_WAYPOINT_RIGHT.value
|
||||
}
|
||||
@@ -227,9 +248,9 @@ private fun exitNumber(
|
||||
) {
|
||||
0
|
||||
} else {
|
||||
try {
|
||||
if (isNumeric(instruction.exitNumber)) {
|
||||
instruction.exitNumber.toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ class ValhallaRoute {
|
||||
type = convertType(it),
|
||||
waypoints =waypoints.subList(it.beginShapeIndex, it.endShapeIndex+1),
|
||||
// TODO: calculate from ShapeIndex !
|
||||
location = location(0.0, 0.0)
|
||||
location = location(0.0, 0.0),
|
||||
leftDistance = emptyList()
|
||||
|
||||
)
|
||||
var name = ""
|
||||
|
||||
@@ -2,12 +2,14 @@ package com.kouros.navigation.model
|
||||
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import android.util.Log
|
||||
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.data.Constants.SPEED_BEARING_DEVIATION
|
||||
@@ -24,6 +26,7 @@ import com.kouros.navigation.data.overpass.Overpass
|
||||
import com.kouros.navigation.utils.bearingPositive
|
||||
import com.kouros.navigation.utils.countryCodeSpeedLimit
|
||||
import com.kouros.navigation.utils.getSettingsRepository
|
||||
import com.kouros.navigation.utils.isNumeric
|
||||
import com.kouros.navigation.utils.location
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
@@ -115,7 +118,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
|
||||
MutableLiveData()
|
||||
}
|
||||
|
||||
val gson: com.google.gson.Gson = GsonBuilder().create()
|
||||
val gson: Gson = GsonBuilder().create()
|
||||
|
||||
/**
|
||||
* Retrieves recent places from Preferences as a Flow.
|
||||
@@ -432,7 +435,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
|
||||
val search = mutableListOf<ElementSearch>()
|
||||
|
||||
speedElements.filter { it.type == "way"}.forEach {
|
||||
var distance = 0F
|
||||
var distance: Float
|
||||
var maxDistance = 1000F
|
||||
var geometryFirstLocation = location(0.0, 0.0)
|
||||
var geometryLastLocation = location(0.0, 0.0)
|
||||
@@ -466,7 +469,11 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
|
||||
speed = if (element.tags.maxspeed == "none" && element.tags.highway == "motorway") {
|
||||
countryCodeSpeedLimit(countryCode)
|
||||
} else {
|
||||
if (isNumeric(element.tags.maxspeed)) {
|
||||
element.tags.maxspeed.toInt()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
return speed
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.kouros.navigation.data.Constants.NEAREST_LOCATION_DISTANCE
|
||||
import com.kouros.navigation.data.Constants.SPEED_UPDATE_DISTANCE
|
||||
import com.kouros.navigation.utils.location
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
class RouteCalculator(var routeModel: RouteModel) {
|
||||
|
||||
@@ -17,10 +18,11 @@ class RouteCalculator(var routeModel: RouteModel) {
|
||||
fun findStep(location: Location) {
|
||||
var nearestDistance = MAXIMUM_LOCATION_DISTANCE
|
||||
for ((index, step) in routeModel.curLeg.steps.withIndex()) {
|
||||
var distance = 0F
|
||||
if (index >= routeModel.navState.route.currentStepIndex) {
|
||||
for ((wayIndex, waypoint) in step.maneuver.waypoints.withIndex()) {
|
||||
if (wayIndex >= step.waypointIndex) {
|
||||
val distance = location.distanceTo(location(waypoint[0], waypoint[1]))
|
||||
distance = location.distanceTo(location(waypoint[0], waypoint[1]))
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance
|
||||
routeModel.navState.route.currentStepIndex = step.index
|
||||
@@ -28,9 +30,12 @@ class RouteCalculator(var routeModel: RouteModel) {
|
||||
step.wayPointLocation = location(waypoint[0], waypoint[1])
|
||||
}
|
||||
}
|
||||
if (nearestDistance < NEAREST_LOCATION_DISTANCE && distance > NEAREST_LOCATION_DISTANCE * 10) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (nearestDistance < NEAREST_LOCATION_DISTANCE) {
|
||||
}
|
||||
if (nearestDistance < NEAREST_LOCATION_DISTANCE && distance > NEAREST_LOCATION_DISTANCE * 10) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -69,19 +74,15 @@ class RouteCalculator(var routeModel: RouteModel) {
|
||||
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 locationDistance = loc1.distanceTo(routeModel.navState.lastLocation)
|
||||
val distance = loc1.distanceTo(loc2)
|
||||
leftDistance += if (locationDistance < distance) {
|
||||
locationDistance
|
||||
} else {
|
||||
distance
|
||||
if (step.waypointIndex < step.maneuver.waypoints.size -1) {
|
||||
leftDistance = step.maneuver.leftDistance[step.waypointIndex]
|
||||
val waypointLocation = location(step.maneuver.waypoints[step.waypointIndex][0], step.maneuver.waypoints[step.waypointIndex][1])
|
||||
if (routeModel.navState.lastLocation.latitude != 0.0) {
|
||||
val locationDistance = waypointLocation.distanceTo(routeModel.navState.lastLocation)
|
||||
leftDistance -= locationDistance
|
||||
}
|
||||
}
|
||||
return leftDistance.toDouble()
|
||||
return leftDistance.absoluteValue.toDouble()
|
||||
}
|
||||
|
||||
/** Returns the left distance in m. */
|
||||
|
||||
@@ -139,6 +139,11 @@ fun Double.round(numFractionDigits: Int): Double {
|
||||
return (this * factor).roundToInt() / factor
|
||||
}
|
||||
|
||||
fun isNumeric(toCheck: String): Boolean {
|
||||
val regex = "-?[0-9]+(\\.[0-9]+)?".toRegex()
|
||||
return toCheck.matches(regex)
|
||||
}
|
||||
|
||||
fun duration(
|
||||
viewStyle: ViewStyle,
|
||||
bearing: Double,
|
||||
|
||||
@@ -44,7 +44,7 @@ class RouteModelTest {
|
||||
return Step(
|
||||
index = index,
|
||||
waypointIndex = waypointIndex,
|
||||
maneuver = Maneuver(waypoints = waypoints, location = mock()),
|
||||
maneuver = Maneuver(waypoints = waypoints, location = mock(), leftDistance = mock()),
|
||||
duration = duration,
|
||||
distance = distance,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user