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