Refactoring Route, Speed
This commit is contained in:
@@ -14,8 +14,8 @@ android {
|
|||||||
applicationId = "com.kouros.navigation"
|
applicationId = "com.kouros.navigation"
|
||||||
minSdk = 33
|
minSdk = 33
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 14
|
versionCode = 15
|
||||||
versionName = "0.1.3.14"
|
versionName = "0.1.3.15"
|
||||||
base.archivesName = "navi-$versionName"
|
base.archivesName = "navi-$versionName"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,34 @@ package com.kouros.navigation
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.kouros.navigation.data.Constants.DARK_MODE_SETTINGS
|
||||||
|
import com.kouros.navigation.data.Constants.ROUTE_ENGINE
|
||||||
|
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
|
||||||
import com.kouros.navigation.data.NavigationRepository
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
import com.kouros.navigation.data.ObjectBox
|
import com.kouros.navigation.data.ObjectBox
|
||||||
|
import com.kouros.navigation.data.RouteEngine
|
||||||
import com.kouros.navigation.data.osrm.OsrmRepository
|
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
import com.kouros.navigation.di.appModule
|
import com.kouros.navigation.di.appModule
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.ViewModel
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils.getRouteEngine
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils.setIntKeyValue
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.android.ext.koin.androidLogger
|
import org.koin.android.ext.koin.androidLogger
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
import org.koin.core.logger.Level
|
import org.koin.core.logger.Level
|
||||||
|
import org.maplibre.compose.expressions.dsl.switch
|
||||||
|
|
||||||
class MainApplication : Application() {
|
class MainApplication : Application() {
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
ObjectBox.init(this);
|
ObjectBox.init(this);
|
||||||
appContext = applicationContext
|
appContext = applicationContext
|
||||||
|
setIntKeyValue(appContext!!, RouteEngine.VALHALLA.ordinal, ROUTE_ENGINE)
|
||||||
|
navigationViewModel = getRouteEngine(appContext!!)
|
||||||
startKoin {
|
startKoin {
|
||||||
androidLogger(Level.DEBUG)
|
androidLogger(Level.DEBUG)
|
||||||
androidContext(this@MainApplication)
|
androidContext(this@MainApplication)
|
||||||
@@ -26,11 +38,12 @@ class MainApplication : Application() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
var appContext: Context? = null
|
var appContext: Context? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var useContacts = false
|
var useContacts = false
|
||||||
|
|
||||||
val navigationViewModel = ViewModel(ValhallaRepository())
|
lateinit var navigationViewModel : ViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,4 +11,5 @@ import org.koin.dsl.module
|
|||||||
val appModule = module {
|
val appModule = module {
|
||||||
viewModelOf(::ViewModel)
|
viewModelOf(::ViewModel)
|
||||||
singleOf(::ValhallaRepository)
|
singleOf(::ValhallaRepository)
|
||||||
|
singleOf(::OsrmRepository)
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,9 @@ import com.kouros.navigation.ui.theme.NavigationTheme
|
|||||||
import com.kouros.navigation.utils.bearing
|
import com.kouros.navigation.utils.bearing
|
||||||
import com.kouros.navigation.utils.calculateZoom
|
import com.kouros.navigation.utils.calculateZoom
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -77,7 +79,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
var lastLocation = location(0.0, 0.0)
|
var lastLocation = location(0.0, 0.0)
|
||||||
val observer = Observer<String> { newRoute ->
|
val observer = Observer<String> { newRoute ->
|
||||||
if (newRoute.isNotEmpty()) {
|
if (newRoute.isNotEmpty()) {
|
||||||
routeModel.startNavigation(newRoute)
|
routeModel.startNavigation(newRoute, applicationContext)
|
||||||
routeData.value = routeModel.route.routeGeoJson
|
routeData.value = routeModel.route.routeGeoJson
|
||||||
simulate()
|
simulate()
|
||||||
//test()
|
//test()
|
||||||
@@ -295,12 +297,15 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
fun simulate() {
|
||||||
fun simulate() = GlobalScope.async {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
for ((index, step) in routeModel.legs.steps.withIndex()) {
|
for ((index, step) in routeModel.legs.steps.withIndex()) {
|
||||||
for ((windex, waypoint) in step.maneuver.waypoints.withIndex()) {
|
for ((windex, waypoint) in step.maneuver.waypoints.withIndex()) {
|
||||||
mock.setMockLocation(waypoint[1], waypoint[0])
|
if (routeModel.isNavigating()) {
|
||||||
delay(600L) //
|
mock.setMockLocation(waypoint[1], waypoint[0])
|
||||||
|
delay(800L) //
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,13 @@ fun MapView(
|
|||||||
Column {
|
Column {
|
||||||
NavigationInfo(step)
|
NavigationInfo(step)
|
||||||
Box(contentAlignment = Alignment.Center) {
|
Box(contentAlignment = Alignment.Center) {
|
||||||
MapLibre(applicationContext, cameraState, baseStyle, route, ViewStyle.VIEW)
|
MapLibre(
|
||||||
|
applicationContext,
|
||||||
|
cameraState,
|
||||||
|
baseStyle,
|
||||||
|
route,
|
||||||
|
ViewStyle.VIEW
|
||||||
|
)
|
||||||
LocationTrackingEffect(
|
LocationTrackingEffect(
|
||||||
locationState = userLocationState,
|
locationState = userLocationState,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ private fun RecentPlaces(
|
|||||||
modifier = Modifier.size(24.dp, 24.dp),
|
modifier = Modifier.size(24.dp, 24.dp),
|
||||||
)
|
)
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text("${place.name} ${place.postalCode}") },
|
headlineContent = { Text("${place.street} ${place.postalCode} ${place.city}") },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable {
|
.clickable {
|
||||||
val toLocation = location(place.longitude, place.latitude)
|
val toLocation = location(place.longitude, place.latitude)
|
||||||
|
|||||||
@@ -24,11 +24,15 @@ import com.kouros.navigation.car.screen.RequestPermissionScreen
|
|||||||
import com.kouros.navigation.car.screen.SearchScreen
|
import com.kouros.navigation.car.screen.SearchScreen
|
||||||
import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION
|
import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION
|
||||||
import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION
|
import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION
|
||||||
|
import com.kouros.navigation.data.Constants.ROUTE_ENGINE
|
||||||
import com.kouros.navigation.data.Constants.TAG
|
import com.kouros.navigation.data.Constants.TAG
|
||||||
import com.kouros.navigation.data.NavigationRepository
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
|
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.ViewModel
|
||||||
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils.getRouteEngine
|
||||||
|
|
||||||
class NavigationSession : Session(), NavigationScreen.Listener {
|
class NavigationSession : Session(), NavigationScreen.Listener {
|
||||||
|
|
||||||
@@ -69,7 +73,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val navigationViewModel = ViewModel(ValhallaRepository())
|
lateinit var navigationViewModel : ViewModel
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val lifecycle: Lifecycle = lifecycle
|
val lifecycle: Lifecycle = lifecycle
|
||||||
@@ -77,6 +81,8 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateScreen(intent: Intent): Screen {
|
override fun onCreateScreen(intent: Intent): Screen {
|
||||||
|
|
||||||
|
navigationViewModel = getRouteEngine(carContext)
|
||||||
routeModel = RouteCarModel()
|
routeModel = RouteCarModel()
|
||||||
|
|
||||||
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel)
|
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel)
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import com.kouros.navigation.car.map.cameraState
|
|||||||
import com.kouros.navigation.car.map.getPaddingValues
|
import com.kouros.navigation.car.map.getPaddingValues
|
||||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||||
import com.kouros.navigation.data.Constants
|
import com.kouros.navigation.data.Constants
|
||||||
|
import com.kouros.navigation.data.Constants.homeLocation
|
||||||
import com.kouros.navigation.data.ObjectBox
|
import com.kouros.navigation.data.ObjectBox
|
||||||
import com.kouros.navigation.model.RouteModel
|
import com.kouros.navigation.model.RouteModel
|
||||||
import com.kouros.navigation.utils.bearing
|
import com.kouros.navigation.utils.bearing
|
||||||
@@ -57,7 +58,7 @@ class SurfaceRenderer(
|
|||||||
private val cameraPosition = MutableLiveData(
|
private val cameraPosition = MutableLiveData(
|
||||||
CameraPosition(
|
CameraPosition(
|
||||||
zoom = 15.0,
|
zoom = 15.0,
|
||||||
target = Position(latitude = 48.1857475, longitude = 11.5793627)
|
target = Position(latitude = homeLocation.latitude, longitude = homeLocation.longitude)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private var visibleArea = MutableLiveData(
|
private var visibleArea = MutableLiveData(
|
||||||
@@ -68,6 +69,7 @@ class SurfaceRenderer(
|
|||||||
var height = 0
|
var height = 0
|
||||||
var lastBearing = 0.0
|
var lastBearing = 0.0
|
||||||
val routeData = MutableLiveData("")
|
val routeData = MutableLiveData("")
|
||||||
|
val speedCamerasData = MutableLiveData("")
|
||||||
val speed = MutableLiveData(0F)
|
val speed = MutableLiveData(0F)
|
||||||
lateinit var centerLocation: Location
|
lateinit var centerLocation: Location
|
||||||
var viewStyle = ViewStyle.VIEW
|
var viewStyle = ViewStyle.VIEW
|
||||||
@@ -165,11 +167,11 @@ class SurfaceRenderer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MapView() {
|
fun MapView() {
|
||||||
val position: CameraPosition? by cameraPosition.observeAsState()
|
val position: CameraPosition? by cameraPosition.observeAsState()
|
||||||
val route: String? by routeData.observeAsState()
|
val route: String? by routeData.observeAsState()
|
||||||
|
val speedCameras: String? by speedCamerasData.observeAsState()
|
||||||
val paddingValues = getPaddingValues(height, viewStyle)
|
val paddingValues = getPaddingValues(height, viewStyle)
|
||||||
val cameraState = cameraState(paddingValues, position, tilt)
|
val cameraState = cameraState(paddingValues, position, tilt)
|
||||||
|
|
||||||
@@ -177,7 +179,7 @@ class SurfaceRenderer(
|
|||||||
mutableStateOf(BaseStyle.Uri(Constants.STYLE))
|
mutableStateOf(BaseStyle.Uri(Constants.STYLE))
|
||||||
}
|
}
|
||||||
DarkMode(carContext, baseStyle)
|
DarkMode(carContext, baseStyle)
|
||||||
MapLibre(carContext, cameraState, baseStyle, route, viewStyle)
|
MapLibre(carContext, cameraState, baseStyle, route, viewStyle, speedCameras)
|
||||||
ShowPosition(cameraState, position, paddingValues)
|
ShowPosition(cameraState, position, paddingValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,9 +191,15 @@ class SurfaceRenderer(
|
|||||||
) {
|
) {
|
||||||
val cameraDuration =
|
val cameraDuration =
|
||||||
duration(viewStyle == ViewStyle.PREVIEW, position!!.bearing, lastBearing)
|
duration(viewStyle == ViewStyle.PREVIEW, position!!.bearing, lastBearing)
|
||||||
val currentSpeed: Float? by speed.observeAsState()
|
val currentSpeed: Float? by speed.observeAsState()
|
||||||
if (viewStyle == ViewStyle.VIEW) {
|
if (viewStyle == ViewStyle.VIEW) {
|
||||||
DrawNavigationImages(paddingValues, currentSpeed, routeModel.routeState.maxSpeed, width, height)
|
DrawNavigationImages(
|
||||||
|
paddingValues,
|
||||||
|
currentSpeed,
|
||||||
|
routeModel.routeState.maxSpeed,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
)
|
||||||
}
|
}
|
||||||
LaunchedEffect(position, viewStyle) {
|
LaunchedEffect(position, viewStyle) {
|
||||||
cameraState.animateTo(
|
cameraState.animateTo(
|
||||||
@@ -237,7 +245,11 @@ class SurfaceRenderer(
|
|||||||
fun updateLocation(location: Location) {
|
fun updateLocation(location: Location) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
||||||
val bearing = bearing(lastLocation, location, cameraPosition.value!!.bearing)
|
val bearing = bearing(
|
||||||
|
lastLocation,
|
||||||
|
location,
|
||||||
|
cameraPosition.value!!.bearing
|
||||||
|
)
|
||||||
val zoom = if (viewStyle == ViewStyle.VIEW) {
|
val zoom = if (viewStyle == ViewStyle.VIEW) {
|
||||||
calculateZoom(location.speed.toDouble())
|
calculateZoom(location.speed.toDouble())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import org.maplibre.spatialk.geojson.Feature
|
|||||||
import org.maplibre.spatialk.geojson.FeatureCollection
|
import org.maplibre.spatialk.geojson.FeatureCollection
|
||||||
import org.maplibre.spatialk.geojson.Point
|
import org.maplibre.spatialk.geojson.Point
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
@@ -244,8 +245,8 @@ private fun rememberLocationSource(locationState: Location): GeoJsonSource {
|
|||||||
buildJsonObject {
|
buildJsonObject {
|
||||||
put("accuracy", location.accuracy)
|
put("accuracy", location.accuracy)
|
||||||
put("bearing", location.bearing)
|
put("bearing", location.bearing)
|
||||||
//put("bearingAccuracy", location.bearingAccuracy)
|
put("bearingAccuracy", location.hasBearingAccuracy())
|
||||||
//put("age", location.timestamp.elapsedNow().inWholeNanoseconds)
|
put("age", location.time.absoluteValue)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.kouros.navigation.car.map
|
package com.kouros.navigation.car.map
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.foundation.Canvas
|
||||||
@@ -36,6 +35,7 @@ import com.kouros.navigation.data.RouteColor
|
|||||||
import com.kouros.navigation.data.SpeedColor
|
import com.kouros.navigation.data.SpeedColor
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||||
|
import com.kouros.navigation.utils.location
|
||||||
import org.maplibre.compose.camera.CameraPosition
|
import org.maplibre.compose.camera.CameraPosition
|
||||||
import org.maplibre.compose.camera.CameraState
|
import org.maplibre.compose.camera.CameraState
|
||||||
import org.maplibre.compose.camera.rememberCameraState
|
import org.maplibre.compose.camera.rememberCameraState
|
||||||
@@ -89,7 +89,8 @@ fun MapLibre(
|
|||||||
cameraState: CameraState,
|
cameraState: CameraState,
|
||||||
baseStyle: MutableState<BaseStyle.Uri>,
|
baseStyle: MutableState<BaseStyle.Uri>,
|
||||||
route: String?,
|
route: String?,
|
||||||
viewStyle: ViewStyle
|
viewStyle: ViewStyle,
|
||||||
|
speedCameras: String? = ""
|
||||||
) {
|
) {
|
||||||
MaplibreMap(
|
MaplibreMap(
|
||||||
options = MapOptions(
|
options = MapOptions(
|
||||||
@@ -108,11 +109,12 @@ fun MapLibre(
|
|||||||
} else {
|
} else {
|
||||||
RouteLayer(route)
|
RouteLayer(route)
|
||||||
}
|
}
|
||||||
|
SpeedCameraLayer(speedCameras)
|
||||||
}
|
}
|
||||||
|
//val lastLocation = location(cameraState.position.target.longitude, cameraState.position.target.latitude)
|
||||||
//Puck(cameraState, lastLocation)
|
//Puck(cameraState, lastLocation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RouteLayer(routeData: String?) {
|
fun RouteLayer(routeData: String?) {
|
||||||
if (routeData != null && routeData.isNotEmpty()) {
|
if (routeData != null && routeData.isNotEmpty()) {
|
||||||
@@ -167,6 +169,28 @@ fun AmenityLayer(routeData: String?) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SpeedCameraLayer(speedCameras: String?) {
|
||||||
|
if (speedCameras != null && speedCameras.isNotEmpty()) {
|
||||||
|
val color = const(Color.DarkGray)
|
||||||
|
val cameraSource = rememberGeoJsonSource(GeoJsonData.JsonString(speedCameras))
|
||||||
|
SymbolLayer(
|
||||||
|
id = "speed-camera-layer",
|
||||||
|
source = cameraSource,
|
||||||
|
iconImage = image(painterResource(R.drawable.speed_camera_48px), drawAsSdf = true),
|
||||||
|
iconColor = color,
|
||||||
|
iconSize =
|
||||||
|
interpolate(
|
||||||
|
type = exponential(1.2f),
|
||||||
|
input = zoom(),
|
||||||
|
5 to const(0.4f),
|
||||||
|
6 to const(0.7f),
|
||||||
|
7 to const(1.75f),
|
||||||
|
20 to const(3f),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@Composable
|
@Composable
|
||||||
fun BuildingLayer(tiles: Source) {
|
fun BuildingLayer(tiles: Source) {
|
||||||
Anchor.Replace("building-3d") {
|
Anchor.Replace("building-3d") {
|
||||||
@@ -188,7 +212,9 @@ fun DrawNavigationImages(
|
|||||||
height: Int
|
height: Int
|
||||||
) {
|
) {
|
||||||
NavigationImage(padding, width, height)
|
NavigationImage(padding, width, height)
|
||||||
CurrentSpeed(width, height, speed)
|
if (speed != null) {
|
||||||
|
CurrentSpeed(width, height, speed, maxSpeed)
|
||||||
|
}
|
||||||
if (speed != null && maxSpeed > 0 && (speed * 3.6) > maxSpeed) {
|
if (speed != null && maxSpeed > 0 && (speed * 3.6) > maxSpeed) {
|
||||||
MaxSpeed(width, height, maxSpeed)
|
MaxSpeed(width, height, maxSpeed)
|
||||||
}
|
}
|
||||||
@@ -211,7 +237,8 @@ fun NavigationImage(padding: PaddingValues, width: Int, height: Int) {
|
|||||||
painter = painterResource(id = R.drawable.navigation_48px),
|
painter = painterResource(id = R.drawable.navigation_48px),
|
||||||
"Navigation",
|
"Navigation",
|
||||||
tint = color.copy(alpha = 0.7f),
|
tint = color.copy(alpha = 0.7f),
|
||||||
modifier = Modifier.size(imageSize.dp, imageSize.dp)
|
modifier = Modifier
|
||||||
|
.size(imageSize.dp, imageSize.dp)
|
||||||
.scale(scaleX = 1f, scaleY = 0.7f),
|
.scale(scaleX = 1f, scaleY = 0.7f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -221,7 +248,8 @@ fun NavigationImage(padding: PaddingValues, width: Int, height: Int) {
|
|||||||
private fun CurrentSpeed(
|
private fun CurrentSpeed(
|
||||||
width: Int,
|
width: Int,
|
||||||
height: Int,
|
height: Int,
|
||||||
speed: Float?
|
curSpeed: Float,
|
||||||
|
maxSpeed: Int
|
||||||
) {
|
) {
|
||||||
val radius = 32
|
val radius = 32
|
||||||
Box(
|
Box(
|
||||||
@@ -234,7 +262,8 @@ private fun CurrentSpeed(
|
|||||||
) {
|
) {
|
||||||
val textMeasurerSpeed = rememberTextMeasurer()
|
val textMeasurerSpeed = rememberTextMeasurer()
|
||||||
val textMeasurerKm = rememberTextMeasurer()
|
val textMeasurerKm = rememberTextMeasurer()
|
||||||
val speed = (speed!! * 3.6).toInt().toString()
|
val speed = (curSpeed * 3.6).toInt().toString()
|
||||||
|
|
||||||
val kmh = "km/h"
|
val kmh = "km/h"
|
||||||
val styleSpeed = TextStyle(
|
val styleSpeed = TextStyle(
|
||||||
fontSize = 22.sp,
|
fontSize = 22.sp,
|
||||||
@@ -245,10 +274,10 @@ private fun CurrentSpeed(
|
|||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
)
|
)
|
||||||
val textLayoutSpeed = remember(speed) {
|
val textLayoutSpeed = remember(speed, maxSpeed) {
|
||||||
textMeasurerSpeed.measure(speed, styleSpeed)
|
textMeasurerSpeed.measure(speed, styleSpeed)
|
||||||
}
|
}
|
||||||
val textLayoutKm = remember(kmh) {
|
val textLayoutKm = remember(kmh, maxSpeed) {
|
||||||
textMeasurerSpeed.measure(kmh, styleKm)
|
textMeasurerSpeed.measure(kmh, styleKm)
|
||||||
}
|
}
|
||||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import com.kouros.navigation.data.Place
|
|||||||
import com.kouros.navigation.data.nominatim.SearchResult
|
import com.kouros.navigation.data.nominatim.SearchResult
|
||||||
import com.kouros.navigation.data.overpass.Elements
|
import com.kouros.navigation.data.overpass.Elements
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.ViewModel
|
||||||
|
import com.kouros.navigation.utils.GeoUtils
|
||||||
import com.kouros.navigation.utils.bearing
|
import com.kouros.navigation.utils.bearing
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
@@ -60,7 +61,7 @@ class NavigationScreen(
|
|||||||
val observer = Observer<String> { route ->
|
val observer = Observer<String> { route ->
|
||||||
if (route.isNotEmpty()) {
|
if (route.isNotEmpty()) {
|
||||||
navigationType = NavigationType.NAVIGATION
|
navigationType = NavigationType.NAVIGATION
|
||||||
routeModel.startNavigation(route)
|
routeModel.startNavigation(route, carContext)
|
||||||
surfaceRenderer.setRouteData()
|
surfaceRenderer.setRouteData()
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
@@ -92,6 +93,16 @@ class NavigationScreen(
|
|||||||
var speedCameras = listOf<Elements>()
|
var speedCameras = listOf<Elements>()
|
||||||
val speedObserver = Observer<List<Elements>> { cameras ->
|
val speedObserver = Observer<List<Elements>> { cameras ->
|
||||||
speedCameras = cameras
|
speedCameras = cameras
|
||||||
|
|
||||||
|
val coordinates = mutableListOf<List<Double>>()
|
||||||
|
val loc = location(0.0, 0.0)
|
||||||
|
cameras.forEach {
|
||||||
|
val loc =
|
||||||
|
location(longitude = it.lon!!, latitude = it.lat!!)
|
||||||
|
coordinates.add(listOf(it.lon!!, it.lat!!))
|
||||||
|
}
|
||||||
|
val speedData = GeoUtils.createPointCollection(coordinates, "radar")
|
||||||
|
surfaceRenderer.speedCamerasData.value =speedData
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -339,7 +350,7 @@ class NavigationScreen(
|
|||||||
|
|
||||||
private fun settingsAction(): Action {
|
private fun settingsAction(): Action {
|
||||||
return Action.Builder()
|
return Action.Builder()
|
||||||
.setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_applications_48px))
|
.setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_48px))
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
screenManager.push(SettingsScreen(carContext))
|
screenManager.push(SettingsScreen(carContext))
|
||||||
}
|
}
|
||||||
@@ -472,7 +483,7 @@ class NavigationScreen(
|
|||||||
|
|
||||||
private fun updateSpeedCamera(location: Location) {
|
private fun updateSpeedCamera(location: Location) {
|
||||||
if (lastCameraSearch++ % 100 == 0) {
|
if (lastCameraSearch++ % 100 == 0) {
|
||||||
viewModel.getSpeedCameras(location)
|
viewModel.getSpeedCameras(location, 5.0)
|
||||||
}
|
}
|
||||||
if (speedCameras.isNotEmpty()) {
|
if (speedCameras.isNotEmpty()) {
|
||||||
updateDistance(location)
|
updateDistance(location)
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ class PlaceListScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (category == Constants.RECENT) {
|
if (category == RECENT) {
|
||||||
viewModel.places.observe(this, observer)
|
viewModel.places.observe(this, observer)
|
||||||
}
|
}
|
||||||
if (category == Constants.CONTACTS) {
|
if (category == CONTACTS) {
|
||||||
viewModel.contactAddress.observe(this, observerAddress)
|
viewModel.contactAddress.observe(this, observerAddress)
|
||||||
}
|
}
|
||||||
if (category == Constants.FAVORITES) {
|
if (category == FAVORITES) {
|
||||||
viewModel.favorites.observe(this, observer)
|
viewModel.favorites.observe(this, observer)
|
||||||
}
|
}
|
||||||
loadPlaces()
|
loadPlaces()
|
||||||
@@ -84,7 +84,7 @@ class PlaceListScreen(
|
|||||||
places.forEach {
|
places.forEach {
|
||||||
val row = Row.Builder()
|
val row = Row.Builder()
|
||||||
.setImage(contactIcon(it.avatar, it.category))
|
.setImage(contactIcon(it.avatar, it.category))
|
||||||
.setTitle(it.name!!)
|
.setTitle("${it.street!!} ${it.city}")
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
val place = Place(
|
val place = Place(
|
||||||
0,
|
0,
|
||||||
@@ -152,7 +152,7 @@ class PlaceListScreen(
|
|||||||
.setIcon(
|
.setIcon(
|
||||||
RouteCarModel().createCarIcon(
|
RouteCarModel().createCarIcon(
|
||||||
carContext,
|
carContext,
|
||||||
R.drawable.ic_pan_24
|
R.drawable.ic_close_white_24dp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
@@ -167,7 +167,7 @@ class PlaceListScreen(
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun contactIcon(avatar: Uri?, category: String?): CarIcon {
|
fun contactIcon(avatar: Uri?, category: String?): CarIcon {
|
||||||
if (category == Constants.RECENT || avatar == null) {
|
if (category == RECENT || avatar == null) {
|
||||||
return CarIcon.Builder(
|
return CarIcon.Builder(
|
||||||
IconCompat.createWithResource(
|
IconCompat.createWithResource(
|
||||||
carContext, R.drawable.ic_place_white_24dp
|
carContext, R.drawable.ic_place_white_24dp
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class RoutePreviewScreen(
|
|||||||
val navigationMessage = NavigationMessage(carContext)
|
val navigationMessage = NavigationMessage(carContext)
|
||||||
val observer = Observer<String> { route ->
|
val observer = Observer<String> { route ->
|
||||||
if (route.isNotEmpty()) {
|
if (route.isNotEmpty()) {
|
||||||
routeModel.startNavigation(route)
|
routeModel.startNavigation(route, carContext)
|
||||||
surfaceRenderer.setPreviewRouteData(routeModel)
|
surfaceRenderer.setPreviewRouteData(routeModel)
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="48dp"
|
|
||||||
android:height="48dp"
|
|
||||||
android:viewportWidth="960"
|
|
||||||
android:viewportHeight="960"
|
|
||||||
android:tint="?attr/colorControlNormal">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M190,840L160,810L480,80L800,810L770,840L480,708L190,840Z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -8,6 +8,6 @@ val RouteColor = Color(0xFF5582D0)
|
|||||||
|
|
||||||
val SpeedColor = Color(0xFF262525)
|
val SpeedColor = Color(0xFF262525)
|
||||||
|
|
||||||
val MaxSpeedColor = Color(0xFF262525)
|
val MaxSpeedColor = Color(0xFFB71515)
|
||||||
|
|
||||||
val PlaceColor = Color(0xFF868005)
|
val PlaceColor = Color(0xFF868005)
|
||||||
@@ -124,9 +124,12 @@ data class BoundingBox (
|
|||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
|
|
||||||
const val STYLE: String = "https://kouros-online.de/liberty.json"
|
//const val STYLE: String = "https://kouros-online.de/liberty.json"
|
||||||
const val STYLE_DARK: String = "https://kouros-online.de/liberty_night.json"
|
|
||||||
//const val STYLE: String = "https://tiles.openfreemap.org/styles/liberty"
|
//const val STYLE_DARK: String = "https://kouros-online.de/liberty_night.json"
|
||||||
|
|
||||||
|
const val STYLE: String = "https://tiles.openfreemap.org/styles/liberty"
|
||||||
|
const val STYLE_DARK: String = "https://tiles.openfreemap.org/styles/liberty"
|
||||||
const val TAG: String = "Navigation"
|
const val TAG: String = "Navigation"
|
||||||
|
|
||||||
const val CATEGORIES: String = "Categories"
|
const val CATEGORIES: String = "Categories"
|
||||||
@@ -176,9 +179,10 @@ object Constants {
|
|||||||
|
|
||||||
const val DESTINATION_ARRIVAL_DISTANCE = 40.0
|
const val DESTINATION_ARRIVAL_DISTANCE = 40.0
|
||||||
|
|
||||||
|
val ROUTE_ENGINE = RouteEngine.VALHALLA.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
enum class RouteEngine {
|
enum class RouteEngine {
|
||||||
VALHALLA, OSRM, GRAPHHOPPER
|
VALHALLA, OSRM, GRAPHHOPPER
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.kouros.navigation.data
|
package com.kouros.navigation.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import com.kouros.navigation.data.overpass.Elements
|
import com.kouros.navigation.data.overpass.Elements
|
||||||
import com.kouros.navigation.model.RouteModel
|
import com.kouros.navigation.model.RouteModel
|
||||||
@@ -36,10 +37,10 @@ abstract class NavigationRepository {
|
|||||||
|
|
||||||
abstract fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String
|
abstract fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String
|
||||||
|
|
||||||
fun getRouteDistance(currentLocation: Location, location: Location, searchFilter: SearchFilter): Double {
|
fun getRouteDistance(currentLocation: Location, location: Location, searchFilter: SearchFilter, context: Context): Double {
|
||||||
val route = getRoute(currentLocation, location, searchFilter)
|
val route = getRoute(currentLocation, location, searchFilter)
|
||||||
val routeModel = RouteModel()
|
val routeModel = RouteModel()
|
||||||
routeModel.startNavigation(route)
|
routeModel.startNavigation(route, context)
|
||||||
return routeModel.route.summary!!.distance
|
return routeModel.route.summary!!.distance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.kouros.navigation.data
|
|||||||
|
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.kouros.navigation.data.Constants.ROUTE_ENGINE
|
||||||
import com.kouros.navigation.data.osrm.OsrmResponse
|
import com.kouros.navigation.data.osrm.OsrmResponse
|
||||||
import com.kouros.navigation.data.osrm.OsrmRoute
|
import com.kouros.navigation.data.osrm.OsrmRoute
|
||||||
import com.kouros.navigation.data.route.Leg
|
import com.kouros.navigation.data.route.Leg
|
||||||
@@ -10,6 +11,8 @@ import com.kouros.navigation.data.route.Summary
|
|||||||
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.GeoUtils.createCenterLocation
|
import com.kouros.navigation.utils.GeoUtils.createCenterLocation
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils.getRouteEngine
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
@@ -23,39 +26,51 @@ data class Route(
|
|||||||
val summary: Summary?,
|
val summary: Summary?,
|
||||||
val legs: List<Leg>?,
|
val legs: List<Leg>?,
|
||||||
val routeGeoJson: String = "",
|
val routeGeoJson: String = "",
|
||||||
val centerLocation : Location = location(0.0, 0.0),
|
val centerLocation: Location = location(0.0, 0.0),
|
||||||
var currentStep : Int = 0,
|
var currentStep: Int = 0,
|
||||||
val waypoints: List<List<Double>>?,
|
val waypoints: List<List<Double>>?,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
data class Builder (
|
data class Builder(
|
||||||
|
|
||||||
var routeEngine : Int = RouteEngine.VALHALLA.ordinal,
|
var routeEngine: Int = 0,
|
||||||
var summary: Summary? = null,
|
var summary: Summary? = null,
|
||||||
var legs: List<Leg>? = null,
|
var legs: List<Leg>? = null,
|
||||||
var routeGeoJson: String = "",
|
var routeGeoJson: String = "",
|
||||||
var centerLocation: Location = location(0.0, 0.0),
|
var centerLocation: Location = location(0.0, 0.0),
|
||||||
var waypoints : List<List<Double>>? = null,) {
|
var waypoints: List<List<Double>>? = null,
|
||||||
|
) {
|
||||||
|
|
||||||
fun routeType (routeEngine: Int) = apply {this.routeEngine = routeEngine }
|
fun routeType(routeEngine: Int) = apply { this.routeEngine = routeEngine }
|
||||||
fun summary(summary: Summary) = apply { this.summary = summary }
|
fun summary(summary: Summary) = apply { this.summary = summary }
|
||||||
fun legs(legs: List<Leg>) = apply { this.legs = legs }
|
fun legs(legs: List<Leg>) = apply { this.legs = legs }
|
||||||
fun routeGeoJson(routeGeoJson: String) = apply {
|
fun routeGeoJson(routeGeoJson: String) = apply {
|
||||||
this.routeGeoJson = routeGeoJson
|
this.routeGeoJson = routeGeoJson
|
||||||
centerLocation = createCenterLocation(routeGeoJson)
|
centerLocation = createCenterLocation(routeGeoJson)
|
||||||
}
|
}
|
||||||
fun waypoints(waypoints: List<List<Double>>) = apply { this.waypoints = waypoints }
|
|
||||||
|
fun routeEngine(routeEngine: Int) = apply { this.routeEngine = routeEngine }
|
||||||
|
fun waypoints(waypoints: List<List<Double>>) = apply { this.waypoints = waypoints }
|
||||||
fun route(route: String) = apply {
|
fun route(route: String) = apply {
|
||||||
if (route.isNotEmpty() && route != "[]") {
|
if (route.isNotEmpty() && route != "[]") {
|
||||||
val gson = GsonBuilder().serializeNulls().create()
|
val gson = GsonBuilder().serializeNulls().create()
|
||||||
if (routeEngine == RouteEngine.VALHALLA.ordinal) {
|
when (this.routeEngine) {
|
||||||
val jsonObject: Map<String, JsonElement> = Json.parseToJsonElement(route).jsonObject
|
RouteEngine.VALHALLA.ordinal -> {
|
||||||
val routeJson = gson.fromJson(jsonObject["trip"].toString(), ValhallaResponse::class.java)
|
val jsonObject: Map<String, JsonElement> =
|
||||||
|
Json.parseToJsonElement(route).jsonObject
|
||||||
|
val routeJson =
|
||||||
|
gson.fromJson(
|
||||||
|
jsonObject["trip"].toString(),
|
||||||
|
ValhallaResponse::class.java
|
||||||
|
)
|
||||||
ValhallaRoute().mapJsonToValhalla(routeJson, this)
|
ValhallaRoute().mapJsonToValhalla(routeJson, this)
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
val osrmJson = gson.fromJson(route, OsrmResponse::class.java)
|
val osrmJson = gson.fromJson(route, OsrmResponse::class.java)
|
||||||
OsrmRoute().mapToOsrm(osrmJson, this)
|
OsrmRoute().mapToOsrm(osrmJson, this)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import android.location.Location
|
|||||||
import com.kouros.navigation.data.NavigationRepository
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
import com.kouros.navigation.data.SearchFilter
|
import com.kouros.navigation.data.SearchFilter
|
||||||
|
|
||||||
private const val routeUrl = "https://router.project-osrm.org/route/v1/driving/"
|
private const val routeUrl = "https://kouros-online.de/osrm/route/v1/driving/"
|
||||||
|
|
||||||
class OsrmRepository : NavigationRepository() {
|
class OsrmRepository : NavigationRepository() {
|
||||||
override fun getRoute(
|
override fun getRoute(
|
||||||
@@ -12,7 +12,7 @@ class OsrmRepository : NavigationRepository() {
|
|||||||
location: Location,
|
location: Location,
|
||||||
searchFilter: SearchFilter
|
searchFilter: SearchFilter
|
||||||
): String {
|
): String {
|
||||||
val routeLocation = "${currentLocation.latitude},${currentLocation.longitude};${location.latitude},${location.longitude}?steps=true"
|
val routeLocation = "${currentLocation.longitude},${currentLocation.latitude};${location.longitude},${location.latitude}?steps=true"
|
||||||
return fetchUrl(routeUrl + routeLocation, true)
|
return fetchUrl(routeUrl + routeLocation, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,84 @@
|
|||||||
package com.kouros.navigation.data.osrm
|
package com.kouros.navigation.data.osrm
|
||||||
|
|
||||||
import com.kouros.navigation.data.Route
|
import com.kouros.navigation.data.Route
|
||||||
import com.kouros.navigation.data.valhalla.ValhallaResponse
|
import com.kouros.navigation.data.route.Leg
|
||||||
|
import com.kouros.navigation.data.route.Maneuver as RouteManeuver
|
||||||
|
import com.kouros.navigation.data.route.Step
|
||||||
|
import com.kouros.navigation.data.route.Summary
|
||||||
|
import com.kouros.navigation.utils.GeoUtils.createLineStringCollection
|
||||||
|
import com.kouros.navigation.utils.GeoUtils.decodePolyline
|
||||||
|
|
||||||
class OsrmRoute {
|
class OsrmRoute {
|
||||||
|
|
||||||
fun mapToOsrm(routeJson: OsrmResponse, builder: Route.Builder) {
|
fun mapToOsrm(routeJson: OsrmResponse, builder: Route.Builder) {
|
||||||
|
val waypoints = mutableListOf<List<Double>>()
|
||||||
|
val summary = Summary()
|
||||||
|
summary.distance = routeJson.routes.first().distance!!
|
||||||
|
summary.duration = routeJson.routes.first().duration!!
|
||||||
|
val steps = mutableListOf<Step>()
|
||||||
|
var stepIndex = 0
|
||||||
|
routeJson.routes.first().legs.first().steps.forEach {
|
||||||
|
if (it.maneuver != null) {
|
||||||
|
val points = decodePolyline(it.geometry!!, 5)
|
||||||
|
waypoints.addAll(points)
|
||||||
|
val maneuver = RouteManeuver(
|
||||||
|
bearingBefore = it.maneuver!!.bearingBefore ?: 0,
|
||||||
|
bearingAfter = it.maneuver!!.bearingAfter ?: 0,
|
||||||
|
type = convertType(it.maneuver!!),
|
||||||
|
waypoints = points
|
||||||
|
)
|
||||||
|
val step = Step( index = stepIndex, name = it.name!!, distance = it.distance!!, duration = it.duration!!, maneuver = maneuver)
|
||||||
|
steps.add(step)
|
||||||
|
stepIndex += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val leg = Leg(steps)
|
||||||
|
builder
|
||||||
|
.routeType(1)
|
||||||
|
.summary(summary)
|
||||||
|
.routeGeoJson(createLineStringCollection(waypoints))
|
||||||
|
.legs(listOf(leg))
|
||||||
|
.waypoints(waypoints.toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun convertType(maneuver: Maneuver): Int {
|
||||||
|
var newType = 0
|
||||||
|
when (maneuver.type) {
|
||||||
|
ManeuverType.depart.value -> {
|
||||||
|
newType = androidx.car.app.navigation.model.Maneuver.TYPE_DEPART
|
||||||
|
}
|
||||||
|
ManeuverType.arrive.value -> {
|
||||||
|
newType = androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION
|
||||||
|
}
|
||||||
|
ManeuverType.continue_.value -> {
|
||||||
|
newType = androidx.car.app.navigation.model.Maneuver.TYPE_STRAIGHT
|
||||||
|
}
|
||||||
|
ManeuverType.turn.value -> {
|
||||||
|
if (maneuver.modifier == "right") {
|
||||||
|
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_NORMAL_RIGHT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ManeuverType(val value: String) {
|
||||||
|
turn("turn"),
|
||||||
|
depart("depart"),
|
||||||
|
arrive("arrive"),
|
||||||
|
merge("merge"),
|
||||||
|
onRamp("on ramp"),
|
||||||
|
offRamp("off ramp"),
|
||||||
|
fork("fork"),
|
||||||
|
endOfRoad("end of road"),
|
||||||
|
continue_("continue"),
|
||||||
|
roundAbout("roundabout"),
|
||||||
|
rotary("rotary"),
|
||||||
|
roundaboutTurn("roundabout turn"),
|
||||||
|
notification("notification"),
|
||||||
|
exitRoundabout("exit roundabout"),
|
||||||
|
exitRotary("exit rotary")
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,10 @@ import java.net.URL
|
|||||||
|
|
||||||
class Overpass {
|
class Overpass {
|
||||||
|
|
||||||
val overpassUrl = "https://overpass.kumi.systems/api/interpreter"
|
//val overpassUrl = "https://overpass.kumi.systems/api/interpreter"
|
||||||
|
val overpassUrl = "https://kouros-online.de/overpass/interpreter"
|
||||||
|
|
||||||
|
|
||||||
fun getAround(radius: Int, linestring: String) : List<Elements> {
|
fun getAround(radius: Int, linestring: String) : List<Elements> {
|
||||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||||
httpURLConnection.requestMethod = "POST"
|
httpURLConnection.requestMethod = "POST"
|
||||||
@@ -28,7 +31,7 @@ class Overpass {
|
|||||||
|);
|
|);
|
||||||
|out body;
|
|out body;
|
||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
println("way[highway](around:$radius,$linestring)")
|
//println("way[highway](around:$radius,$linestring)")
|
||||||
return overpassApi(httpURLConnection, searchQuery)
|
return overpassApi(httpURLConnection, searchQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +74,7 @@ class Overpass {
|
|||||||
.use { it.readText() } // defaults to UTF-8
|
.use { it.readText() } // defaults to UTF-8
|
||||||
val gson = GsonBuilder().serializeNulls().create()
|
val gson = GsonBuilder().serializeNulls().create()
|
||||||
val overpass = gson.fromJson(response, Amenity::class.java)
|
val overpass = gson.fromJson(response, Amenity::class.java)
|
||||||
//println("Overpass: $response")
|
// println("Overpass: $response")
|
||||||
return overpass.elements
|
return overpass.elements
|
||||||
}
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
|||||||
@@ -245,6 +245,7 @@
|
|||||||
"type": "fill",
|
"type": "fill",
|
||||||
"source": "openmaptiles",
|
"source": "openmaptiles",
|
||||||
"source-layer": "water",
|
"source-layer": "water",
|
||||||
|
"maxzoom": 24,
|
||||||
"filter": ["!=", ["get", "brunnel"], "tunnel"],
|
"filter": ["!=", ["get", "brunnel"], "tunnel"],
|
||||||
"paint": {"fill-color": "rgb(158,189,255)"}
|
"paint": {"fill-color": "rgb(158,189,255)"}
|
||||||
},
|
},
|
||||||
@@ -860,6 +861,7 @@
|
|||||||
true,
|
true,
|
||||||
false
|
false
|
||||||
],
|
],
|
||||||
|
"layout": {"visibility": "visible"},
|
||||||
"paint": {"fill-pattern": "pedestrian_polygon"}
|
"paint": {"fill-pattern": "pedestrian_polygon"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1376,11 +1378,13 @@
|
|||||||
"type": "line",
|
"type": "line",
|
||||||
"source": "openmaptiles",
|
"source": "openmaptiles",
|
||||||
"source-layer": "transportation",
|
"source-layer": "transportation",
|
||||||
|
"maxzoom": 24,
|
||||||
"filter": [
|
"filter": [
|
||||||
"all",
|
"all",
|
||||||
["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true],
|
["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true],
|
||||||
["==", ["get", "class"], "transit"]
|
["==", ["get", "class"], "transit"]
|
||||||
],
|
],
|
||||||
|
"layout": {"visibility": "visible"},
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-color": "#bbb",
|
"line-color": "#bbb",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
@@ -2201,7 +2205,8 @@
|
|||||||
"text-font": ["Noto Sans Italic"],
|
"text-font": ["Noto Sans Italic"],
|
||||||
"text-max-width": 9,
|
"text-max-width": 9,
|
||||||
"text-offset": [0, 0.6],
|
"text-offset": [0, 0.6],
|
||||||
"text-size": 12
|
"text-size": 12,
|
||||||
|
"visibility": "none"
|
||||||
},
|
},
|
||||||
"paint": {
|
"paint": {
|
||||||
"text-color": "#666",
|
"text-color": "#666",
|
||||||
@@ -2240,7 +2245,8 @@
|
|||||||
"text-font": ["Noto Sans Italic"],
|
"text-font": ["Noto Sans Italic"],
|
||||||
"text-max-width": 9,
|
"text-max-width": 9,
|
||||||
"text-offset": [0, 0.6],
|
"text-offset": [0, 0.6],
|
||||||
"text-size": 12
|
"text-size": 12,
|
||||||
|
"visibility": "none"
|
||||||
},
|
},
|
||||||
"paint": {
|
"paint": {
|
||||||
"text-color": "#666",
|
"text-color": "#666",
|
||||||
@@ -2279,7 +2285,8 @@
|
|||||||
"text-font": ["Noto Sans Italic"],
|
"text-font": ["Noto Sans Italic"],
|
||||||
"text-max-width": 9,
|
"text-max-width": 9,
|
||||||
"text-offset": [0, 0.6],
|
"text-offset": [0, 0.6],
|
||||||
"text-size": 12
|
"text-size": 12,
|
||||||
|
"visibility": "none"
|
||||||
},
|
},
|
||||||
"paint": {
|
"paint": {
|
||||||
"text-color": "#666",
|
"text-color": "#666",
|
||||||
@@ -2313,7 +2320,8 @@
|
|||||||
"text-font": ["Noto Sans Italic"],
|
"text-font": ["Noto Sans Italic"],
|
||||||
"text-max-width": 9,
|
"text-max-width": 9,
|
||||||
"text-offset": [0.9, 0],
|
"text-offset": [0.9, 0],
|
||||||
"text-size": 12
|
"text-size": 12,
|
||||||
|
"visibility": "none"
|
||||||
},
|
},
|
||||||
"paint": {
|
"paint": {
|
||||||
"text-color": "#2e5a80",
|
"text-color": "#2e5a80",
|
||||||
|
|||||||
@@ -137,7 +137,7 @@
|
|||||||
"source": "openmaptiles",
|
"source": "openmaptiles",
|
||||||
"source-layer": "landuse",
|
"source-layer": "landuse",
|
||||||
"filter": ["==", ["get", "class"], "pitch"],
|
"filter": ["==", ["get", "class"], "pitch"],
|
||||||
"paint": {"fill-color": "#DEE3CD"}
|
"paint": {"fill-color": "rgba(49, 49, 40, 1)"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "landuse_track",
|
"id": "landuse_track",
|
||||||
@@ -247,7 +247,7 @@
|
|||||||
"source": "openmaptiles",
|
"source": "openmaptiles",
|
||||||
"source-layer": "water",
|
"source-layer": "water",
|
||||||
"filter": ["!=", ["get", "brunnel"], "tunnel"],
|
"filter": ["!=", ["get", "brunnel"], "tunnel"],
|
||||||
"paint": {"fill-color": "rgb(158,189,255)"}
|
"paint": {"fill-color": "rgba(34, 54, 98, 1)"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "landcover_sand",
|
"id": "landcover_sand",
|
||||||
@@ -255,7 +255,7 @@
|
|||||||
"source": "openmaptiles",
|
"source": "openmaptiles",
|
||||||
"source-layer": "landcover",
|
"source-layer": "landcover",
|
||||||
"filter": ["==", ["get", "class"], "sand"],
|
"filter": ["==", ["get", "class"], "sand"],
|
||||||
"paint": {"fill-color": "rgba(247, 239, 195, 1)"}
|
"paint": {"fill-color": "rgba(148, 146, 138, 1)"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "aeroway_fill",
|
"id": "aeroway_fill",
|
||||||
@@ -654,7 +654,7 @@
|
|||||||
],
|
],
|
||||||
"layout": {"line-join": "round"},
|
"layout": {"line-join": "round"},
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-color": "#fff",
|
"line-color": "rgba(74, 64, 64, 1)",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["exponential", 1.2],
|
["exponential", 1.2],
|
||||||
@@ -706,7 +706,7 @@
|
|||||||
],
|
],
|
||||||
"layout": {"line-join": "round"},
|
"layout": {"line-join": "round"},
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-color": "#fff4c6",
|
"line-color": "rgba(72, 70, 58, 1)",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["exponential", 1.2],
|
["exponential", 1.2],
|
||||||
@@ -1154,7 +1154,7 @@
|
|||||||
],
|
],
|
||||||
"layout": {"line-cap": "round", "line-join": "round"},
|
"layout": {"line-cap": "round", "line-join": "round"},
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-color": "#fff",
|
"line-color": "rgba(55, 53, 53, 1)",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["exponential", 1.2],
|
["exponential", 1.2],
|
||||||
@@ -1691,7 +1691,7 @@
|
|||||||
["match", ["get", "class"], ["path", "pedestrian"], true, false]
|
["match", ["get", "class"], ["path", "pedestrian"], true, false]
|
||||||
],
|
],
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-color": "hsl(0,0%,100%)",
|
"line-color": "rgba(107, 102, 102, 1)",
|
||||||
"line-dasharray": [1, 0.3],
|
"line-dasharray": [1, 0.3],
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
@@ -2208,7 +2208,8 @@
|
|||||||
"text-font": ["Noto Sans Italic"],
|
"text-font": ["Noto Sans Italic"],
|
||||||
"text-max-width": 9,
|
"text-max-width": 9,
|
||||||
"text-offset": [0, 0.6],
|
"text-offset": [0, 0.6],
|
||||||
"text-size": 12
|
"text-size": 12,
|
||||||
|
"visibility": "none"
|
||||||
},
|
},
|
||||||
"paint": {
|
"paint": {
|
||||||
"text-color": "#666",
|
"text-color": "#666",
|
||||||
@@ -2247,7 +2248,8 @@
|
|||||||
"text-font": ["Noto Sans Italic"],
|
"text-font": ["Noto Sans Italic"],
|
||||||
"text-max-width": 9,
|
"text-max-width": 9,
|
||||||
"text-offset": [0, 0.6],
|
"text-offset": [0, 0.6],
|
||||||
"text-size": 12
|
"text-size": 12,
|
||||||
|
"visibility": "none"
|
||||||
},
|
},
|
||||||
"paint": {
|
"paint": {
|
||||||
"text-color": "#666",
|
"text-color": "#666",
|
||||||
@@ -2286,7 +2288,8 @@
|
|||||||
"text-font": ["Noto Sans Italic"],
|
"text-font": ["Noto Sans Italic"],
|
||||||
"text-max-width": 9,
|
"text-max-width": 9,
|
||||||
"text-offset": [0, 0.6],
|
"text-offset": [0, 0.6],
|
||||||
"text-size": 12
|
"text-size": 12,
|
||||||
|
"visibility": "none"
|
||||||
},
|
},
|
||||||
"paint": {
|
"paint": {
|
||||||
"text-color": "#666",
|
"text-color": "#666",
|
||||||
@@ -2320,7 +2323,8 @@
|
|||||||
"text-font": ["Noto Sans Italic"],
|
"text-font": ["Noto Sans Italic"],
|
||||||
"text-max-width": 9,
|
"text-max-width": 9,
|
||||||
"text-offset": [0.9, 0],
|
"text-offset": [0.9, 0],
|
||||||
"text-size": 12
|
"text-size": 12,
|
||||||
|
"visibility": "none"
|
||||||
},
|
},
|
||||||
"paint": {
|
"paint": {
|
||||||
"text-color": "#2e5a80",
|
"text-color": "#2e5a80",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.kouros.navigation.data
|
package com.kouros.navigation.data.valhalla
|
||||||
|
|
||||||
enum class ManeuverType(val value: Int) {
|
enum class ManeuverType(val value: Int) {
|
||||||
None(0),
|
None(0),
|
||||||
@@ -8,6 +8,8 @@ import com.kouros.navigation.data.ValhallaLocation
|
|||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
private const val routeUrl = "https://kouros-online.de/valhalla/route?json="
|
private const val routeUrl = "https://kouros-online.de/valhalla/route?json="
|
||||||
|
|
||||||
|
|
||||||
class ValhallaRepository : NavigationRepository() {
|
class ValhallaRepository : NavigationRepository() {
|
||||||
|
|
||||||
override fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String {
|
override fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String {
|
||||||
|
|||||||
@@ -1,20 +1,28 @@
|
|||||||
package com.kouros.navigation.model
|
package com.kouros.navigation.model
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import androidx.car.app.navigation.model.Maneuver
|
import androidx.car.app.navigation.model.Maneuver
|
||||||
import androidx.car.app.navigation.model.Step
|
import androidx.car.app.navigation.model.Step
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
||||||
import com.kouros.navigation.data.ManeuverType
|
import com.kouros.navigation.data.Constants.ROUTE_ENGINE
|
||||||
|
import com.kouros.navigation.data.valhalla.ManeuverType
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
import com.kouros.navigation.data.Route
|
import com.kouros.navigation.data.Route
|
||||||
|
import com.kouros.navigation.data.RouteEngine
|
||||||
import com.kouros.navigation.data.StepData
|
import com.kouros.navigation.data.StepData
|
||||||
import com.kouros.navigation.data.route.Leg
|
import com.kouros.navigation.data.route.Leg
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import kotlinx.coroutines.invoke
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@@ -43,8 +51,10 @@ open class RouteModel() {
|
|||||||
val legs: Leg
|
val legs: Leg
|
||||||
get() = routeState.route!!.legs!!.first()
|
get() = routeState.route!!.legs!!.first()
|
||||||
|
|
||||||
fun startNavigation(routeString: String) {
|
fun startNavigation(routeString: String, context: Context) {
|
||||||
|
val routeEngine = getIntKeyValue(context = context, ROUTE_ENGINE)
|
||||||
val newRoute = Route.Builder()
|
val newRoute = Route.Builder()
|
||||||
|
.routeEngine(routeEngine)
|
||||||
.route(routeString)
|
.route(routeString)
|
||||||
.build()
|
.build()
|
||||||
this.routeState = routeState.copy(
|
this.routeState = routeState.copy(
|
||||||
@@ -65,15 +75,13 @@ open class RouteModel() {
|
|||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
fun updateLocation(location: Location, viewModel: ViewModel) {
|
fun updateLocation(location: Location, viewModel: ViewModel) {
|
||||||
findStep(location)
|
findStep(location)
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
updateSpeedLimit(location, viewModel)
|
||||||
updateSpeedLimit(location, viewModel)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findStep(location: Location) {
|
private fun findStep(location: Location) {
|
||||||
var nearestDistance = 100000.0f
|
var nearestDistance = 100000.0f
|
||||||
for ((index, step) in legs.steps.withIndex()) {
|
for ((index, step) in legs.steps.withIndex()) {
|
||||||
if (index >= route.currentStep && nearestDistance > 0) {
|
if (index >= route.currentStep) {
|
||||||
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]))
|
val distance = location.distanceTo(location(waypoint[0], waypoint[1]))
|
||||||
@@ -83,22 +91,31 @@ open class RouteModel() {
|
|||||||
step.waypointIndex = wayIndex
|
step.waypointIndex = wayIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (nearestDistance == 0F) {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (nearestDistance == 0F) {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
//println("Current Index ${route.currentStep} WayPoint: ${route.currentStep().waypointIndex}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSpeedLimit(location: Location, viewModel: ViewModel) {
|
fun updateSpeedLimit(location: Location, viewModel: ViewModel) = runBlocking {
|
||||||
// speed limit
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val distance = routeState.lastSpeedLocation.distanceTo(location)
|
// speed limit
|
||||||
if (distance > 500 || routeState.lastSpeedIndex < route.currentStep) {
|
val distance = routeState.lastSpeedLocation.distanceTo(location)
|
||||||
routeState = routeState.copy(lastSpeedIndex = route.currentStep)
|
if (distance > 500 || routeState.lastSpeedIndex < route.currentStep) {
|
||||||
routeState = routeState.copy(lastSpeedLocation = location)
|
routeState = routeState.copy(lastSpeedIndex = route.currentStep)
|
||||||
val elements = viewModel.getMaxSpeed(location)
|
routeState = routeState.copy(lastSpeedLocation = location)
|
||||||
elements.forEach {
|
val elements = viewModel.getMaxSpeed(location)
|
||||||
if (it.tags.name != null && it.tags.maxspeed != null) {
|
elements.forEach {
|
||||||
val speed = it.tags.maxspeed!!.toInt()
|
if (it.tags.name != null && it.tags.maxspeed != null) {
|
||||||
routeState = routeState.copy(maxSpeed = speed)
|
val speed = it.tags.maxspeed!!.toInt()
|
||||||
|
routeState = routeState.copy(maxSpeed = speed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,11 +165,9 @@ open class RouteModel() {
|
|||||||
val maneuverType = step.maneuver.type
|
val maneuverType = step.maneuver.type
|
||||||
val distanceLeft = leftStepDistance()
|
val distanceLeft = leftStepDistance()
|
||||||
var text = ""
|
var text = ""
|
||||||
|
|
||||||
when (distanceLeft) {
|
when (distanceLeft) {
|
||||||
in 0.0..NEXT_STEP_THRESHOLD -> {
|
in 0.0..NEXT_STEP_THRESHOLD -> {
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
if (step.name.isNotEmpty()) {
|
if (step.name.isNotEmpty()) {
|
||||||
text = step.name
|
text = step.name
|
||||||
@@ -174,11 +189,13 @@ open class RouteModel() {
|
|||||||
|
|
||||||
fun travelLeftTime(): Double {
|
fun travelLeftTime(): Double {
|
||||||
var timeLeft = 0.0
|
var timeLeft = 0.0
|
||||||
|
// time for next step until end step
|
||||||
for (i in route.currentStep + 1..<legs.steps.size) {
|
for (i in route.currentStep + 1..<legs.steps.size) {
|
||||||
val step = legs.steps[i]
|
val step = legs.steps[i]
|
||||||
timeLeft += step.duration
|
timeLeft += step.duration
|
||||||
}
|
}
|
||||||
val step = route.nextStep()
|
// time for current step
|
||||||
|
val step = route.currentStep()
|
||||||
val curTime = step.duration
|
val curTime = step.duration
|
||||||
val percent =
|
val percent =
|
||||||
100 * (step.maneuver.waypoints.size - step.waypointIndex) / (step.maneuver.waypoints.size)
|
100 * (step.maneuver.waypoints.size - step.waypointIndex) / (step.maneuver.waypoints.size)
|
||||||
@@ -220,8 +237,8 @@ open class RouteModel() {
|
|||||||
val curDistance = step.distance
|
val curDistance = step.distance
|
||||||
val percent =
|
val percent =
|
||||||
100 * (step.maneuver.waypoints.size - step.waypointIndex) / (step.maneuver.waypoints.size)
|
100 * (step.maneuver.waypoints.size - step.waypointIndex) / (step.maneuver.waypoints.size)
|
||||||
val time = curDistance * percent / 100
|
val distance = curDistance * percent / 100
|
||||||
leftDistance += time
|
leftDistance += distance
|
||||||
return leftDistance
|
return leftDistance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
repository.getRouteDistance(
|
repository.getRouteDistance(
|
||||||
location,
|
location,
|
||||||
plLocation,
|
plLocation,
|
||||||
getSearchFilter(context)
|
getSearchFilter(context), context
|
||||||
)
|
)
|
||||||
place.distance = distance.toFloat()
|
place.distance = distance.toFloat()
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
for (place in results) {
|
for (place in results) {
|
||||||
val plLocation = location(place.longitude, place.latitude)
|
val plLocation = location(place.longitude, place.latitude)
|
||||||
val distance =
|
val distance =
|
||||||
repository.getRouteDistance(location, plLocation, getSearchFilter(context))
|
repository.getRouteDistance(location, plLocation, getSearchFilter(context), context)
|
||||||
place.distance = distance.toFloat()
|
place.distance = distance.toFloat()
|
||||||
}
|
}
|
||||||
favorites.postValue(results)
|
favorites.postValue(results)
|
||||||
@@ -265,9 +265,9 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSpeedCameras(location: Location) {
|
fun getSpeedCameras(location: Location, radius : Double) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val amenities = Overpass().getAmenities("highway", "speed_camera", location, 5.0)
|
val amenities = Overpass().getAmenities("highway", "speed_camera", location, radius)
|
||||||
val distAmenities = mutableListOf<Elements>()
|
val distAmenities = mutableListOf<Elements>()
|
||||||
amenities.forEach {
|
amenities.forEach {
|
||||||
val plLocation =
|
val plLocation =
|
||||||
@@ -380,7 +380,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
for (place in results) {
|
for (place in results) {
|
||||||
val plLocation = location(place.longitude, place.latitude)
|
val plLocation = location(place.longitude, place.latitude)
|
||||||
val distance =
|
val distance =
|
||||||
repository.getRouteDistance(location, plLocation, getSearchFilter(context))
|
repository.getRouteDistance(location, plLocation, getSearchFilter(context), context)
|
||||||
place.distance = distance.toFloat()
|
place.distance = distance.toFloat()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import org.maplibre.spatialk.geojson.Feature
|
|||||||
import org.maplibre.spatialk.geojson.dsl.addFeature
|
import org.maplibre.spatialk.geojson.dsl.addFeature
|
||||||
import org.maplibre.spatialk.geojson.dsl.buildFeatureCollection
|
import org.maplibre.spatialk.geojson.dsl.buildFeatureCollection
|
||||||
import org.maplibre.spatialk.geojson.dsl.buildLineString
|
import org.maplibre.spatialk.geojson.dsl.buildLineString
|
||||||
|
import org.maplibre.spatialk.geojson.dsl.buildMultiPoint
|
||||||
import org.maplibre.spatialk.geojson.toJson
|
import org.maplibre.spatialk.geojson.toJson
|
||||||
import org.maplibre.turf.TurfMeasurement
|
import org.maplibre.turf.TurfMeasurement
|
||||||
import org.maplibre.turf.TurfMisc
|
import org.maplibre.turf.TurfMisc
|
||||||
@@ -32,8 +33,9 @@ object GeoUtils {
|
|||||||
return newLocation
|
return newLocation
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decodePolyline(encoded: String, vararg precisionOptional: Int): List<List<Double>> {
|
|
||||||
val precision = if (precisionOptional.isNotEmpty()) precisionOptional[0] else 6
|
fun decodePolyline(encoded: String, precision: Int = 6,): List<List<Double>> {
|
||||||
|
//val precisionOptional = if (precisionOptional.isNotEmpty()) precisionOptional[0] else 6
|
||||||
val factor = 10.0.pow(precision)
|
val factor = 10.0.pow(precision)
|
||||||
|
|
||||||
var lat = 0
|
var lat = 0
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import android.content.Context
|
|||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import com.kouros.navigation.data.Constants.ROUTE_ENGINE
|
||||||
import com.kouros.navigation.data.Constants.SHARED_PREF_KEY
|
import com.kouros.navigation.data.Constants.SHARED_PREF_KEY
|
||||||
|
import com.kouros.navigation.data.RouteEngine
|
||||||
|
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||||
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
|
import com.kouros.navigation.model.ViewModel
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
@@ -20,6 +25,14 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
|
|
||||||
object NavigationUtils {
|
object NavigationUtils {
|
||||||
|
|
||||||
|
fun getRouteEngine(context: Context): ViewModel {
|
||||||
|
val routeEngine = getIntKeyValue(context = context, ROUTE_ENGINE)
|
||||||
|
return when (routeEngine) {
|
||||||
|
RouteEngine.VALHALLA.ordinal -> ViewModel(ValhallaRepository())
|
||||||
|
else -> ViewModel(OsrmRepository())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getBooleanKeyValue(context: Context, key: String): Boolean {
|
fun getBooleanKeyValue(context: Context, key: String): Boolean {
|
||||||
return context
|
return context
|
||||||
.getSharedPreferences(
|
.getSharedPreferences(
|
||||||
@@ -43,13 +56,13 @@ object NavigationUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIntKeyValue(context: Context, key: String): Int {
|
fun getIntKeyValue(context: Context, key: String, default: Int = 0): Int {
|
||||||
return context
|
return context
|
||||||
.getSharedPreferences(
|
.getSharedPreferences(
|
||||||
SHARED_PREF_KEY,
|
SHARED_PREF_KEY,
|
||||||
Context.MODE_PRIVATE
|
Context.MODE_PRIVATE
|
||||||
)
|
)
|
||||||
.getInt(key, 0)
|
.getInt(key, default)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setIntKeyValue(context: Context, `val`: Int, key: String) {
|
fun setIntKeyValue(context: Context, `val`: Int, key: String) {
|
||||||
|
|||||||
@@ -6,5 +6,5 @@
|
|||||||
android:tint="?attr/colorControlNormal">
|
android:tint="?attr/colorControlNormal">
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
android:fillColor="@android:color/white"
|
||||||
android:pathData="M190,840L160,810L480,80L800,810L770,840L480,708L190,840ZM258,742L480,644L702,742L480,228L258,742ZM480,644L480,644L480,644L480,644Z"/>
|
android:pathData="M190,840L160,810L480,80L800,810L770,840L480,708L190,840Z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
10
common/data/src/main/res/drawable/settings_48px.xml
Normal file
10
common/data/src/main/res/drawable/settings_48px.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M388,880L368,754Q349,747 328,735Q307,723 291,710L173,764L80,600L188,521Q186,512 185.5,500.5Q185,489 185,480Q185,471 185.5,459.5Q186,448 188,439L80,360L173,196L291,250Q307,237 328,225Q349,213 368,207L388,80L572,80L592,206Q611,213 632.5,224.5Q654,236 669,250L787,196L880,360L772,437Q774,447 774.5,458.5Q775,470 775,480Q775,490 774.5,501Q774,512 772,522L880,600L787,764L669,710Q653,723 632.5,735.5Q612,748 592,754L572,880L388,880ZM436,820L524,820L538,708Q571,700 600.5,683Q630,666 654,642L760,688L800,616L706,547Q710,530 712.5,513.5Q715,497 715,480Q715,463 713,446.5Q711,430 706,413L800,344L760,272L654,318Q631,292 602,274.5Q573,257 538,252L524,140L436,140L422,252Q388,259 358.5,276Q329,293 306,318L200,272L160,344L254,413Q250,430 247.5,446.5Q245,463 245,480Q245,497 247.5,513.5Q250,530 254,547L160,616L200,688L306,642Q330,666 359.5,683Q389,700 422,708L436,820ZM480,610Q534,610 572,572Q610,534 610,480Q610,426 572,388Q534,350 480,350Q426,350 388,388Q350,426 350,480Q350,534 388,572Q426,610 480,610ZM480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Z"/>
|
||||||
|
</vector>
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="48dp"
|
|
||||||
android:height="48dp"
|
|
||||||
android:viewportWidth="960"
|
|
||||||
android:viewportHeight="960"
|
|
||||||
android:tint="?attr/colorControlNormal">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M452,674L508,674L518,620Q538,614 552,605Q566,596 578,584L640,603L666,549L619,519Q623,498 623,480Q623,462 619,441L666,411L640,357L578,376Q566,364 552,355Q538,346 518,340L508,286L452,286L442,340Q422,346 408,355Q394,364 382,376L320,357L294,411L341,441Q337,462 337,480Q337,498 341,519L294,549L320,603L382,584Q394,596 408,605Q422,614 442,620L452,674ZM480,565Q444,565 419.5,540.5Q395,516 395,480Q395,444 419.5,419.5Q444,395 480,395Q516,395 540.5,419.5Q565,444 565,480Q565,516 540.5,540.5Q516,565 480,565ZM180,840Q156,840 138,822Q120,804 120,780L120,180Q120,156 138,138Q156,120 180,120L780,120Q804,120 822,138Q840,156 840,180L840,780Q840,804 822,822Q804,840 780,840L180,840ZM180,780L780,780Q780,780 780,780Q780,780 780,780L780,180Q780,180 780,180Q780,180 780,180L180,180Q180,180 180,180Q180,180 180,180L180,780Q180,780 180,780Q180,780 180,780ZM180,180L180,180Q180,180 180,180Q180,180 180,180L180,780Q180,780 180,780Q180,780 180,780L180,780Q180,780 180,780Q180,780 180,780L180,180Q180,180 180,180Q180,180 180,180Z"/>
|
|
||||||
</vector>
|
|
||||||
Reference in New Issue
Block a user