Refactoring Route, Speed
This commit is contained in:
@@ -14,8 +14,8 @@ android {
|
||||
applicationId = "com.kouros.navigation"
|
||||
minSdk = 33
|
||||
targetSdk = 36
|
||||
versionCode = 14
|
||||
versionName = "0.1.3.14"
|
||||
versionCode = 15
|
||||
versionName = "0.1.3.15"
|
||||
base.archivesName = "navi-$versionName"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -2,22 +2,34 @@ package com.kouros.navigation
|
||||
|
||||
import android.app.Application
|
||||
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.ObjectBox
|
||||
import com.kouros.navigation.data.RouteEngine
|
||||
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||
import com.kouros.navigation.di.appModule
|
||||
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.androidLogger
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.logger.Level
|
||||
import org.maplibre.compose.expressions.dsl.switch
|
||||
|
||||
class MainApplication : Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
ObjectBox.init(this);
|
||||
appContext = applicationContext
|
||||
setIntKeyValue(appContext!!, RouteEngine.VALHALLA.ordinal, ROUTE_ENGINE)
|
||||
navigationViewModel = getRouteEngine(appContext!!)
|
||||
startKoin {
|
||||
androidLogger(Level.DEBUG)
|
||||
androidContext(this@MainApplication)
|
||||
@@ -26,11 +38,12 @@ class MainApplication : Application() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
var appContext: Context? = null
|
||||
private set
|
||||
|
||||
var useContacts = false
|
||||
|
||||
val navigationViewModel = ViewModel(ValhallaRepository())
|
||||
lateinit var navigationViewModel : ViewModel
|
||||
}
|
||||
}
|
||||
@@ -11,4 +11,5 @@ import org.koin.dsl.module
|
||||
val appModule = module {
|
||||
viewModelOf(::ViewModel)
|
||||
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.calculateZoom
|
||||
import com.kouros.navigation.utils.location
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -77,7 +79,7 @@ class MainActivity : ComponentActivity() {
|
||||
var lastLocation = location(0.0, 0.0)
|
||||
val observer = Observer<String> { newRoute ->
|
||||
if (newRoute.isNotEmpty()) {
|
||||
routeModel.startNavigation(newRoute)
|
||||
routeModel.startNavigation(newRoute, applicationContext)
|
||||
routeData.value = routeModel.route.routeGeoJson
|
||||
simulate()
|
||||
//test()
|
||||
@@ -295,12 +297,15 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun simulate() = GlobalScope.async {
|
||||
fun simulate() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
for ((index, step) in routeModel.legs.steps.withIndex()) {
|
||||
for ((windex, waypoint) in step.maneuver.waypoints.withIndex()) {
|
||||
if (routeModel.isNavigating()) {
|
||||
mock.setMockLocation(waypoint[1], waypoint[0])
|
||||
delay(600L) //
|
||||
delay(800L) //
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,13 @@ fun MapView(
|
||||
Column {
|
||||
NavigationInfo(step)
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
MapLibre(applicationContext, cameraState, baseStyle, route, ViewStyle.VIEW)
|
||||
MapLibre(
|
||||
applicationContext,
|
||||
cameraState,
|
||||
baseStyle,
|
||||
route,
|
||||
ViewStyle.VIEW
|
||||
)
|
||||
LocationTrackingEffect(
|
||||
locationState = userLocationState,
|
||||
) {
|
||||
|
||||
@@ -257,7 +257,7 @@ private fun RecentPlaces(
|
||||
modifier = Modifier.size(24.dp, 24.dp),
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text("${place.name} ${place.postalCode}") },
|
||||
headlineContent = { Text("${place.street} ${place.postalCode} ${place.city}") },
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
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.data.Constants.MAXIMAL_ROUTE_DEVIATION
|
||||
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.NavigationRepository
|
||||
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
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 {
|
||||
|
||||
@@ -69,7 +73,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
}
|
||||
}
|
||||
|
||||
val navigationViewModel = ViewModel(ValhallaRepository())
|
||||
lateinit var navigationViewModel : ViewModel
|
||||
|
||||
init {
|
||||
val lifecycle: Lifecycle = lifecycle
|
||||
@@ -77,6 +81,8 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
}
|
||||
|
||||
override fun onCreateScreen(intent: Intent): Screen {
|
||||
|
||||
navigationViewModel = getRouteEngine(carContext)
|
||||
routeModel = RouteCarModel()
|
||||
|
||||
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.navigation.RouteCarModel
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.data.Constants.homeLocation
|
||||
import com.kouros.navigation.data.ObjectBox
|
||||
import com.kouros.navigation.model.RouteModel
|
||||
import com.kouros.navigation.utils.bearing
|
||||
@@ -57,7 +58,7 @@ class SurfaceRenderer(
|
||||
private val cameraPosition = MutableLiveData(
|
||||
CameraPosition(
|
||||
zoom = 15.0,
|
||||
target = Position(latitude = 48.1857475, longitude = 11.5793627)
|
||||
target = Position(latitude = homeLocation.latitude, longitude = homeLocation.longitude)
|
||||
)
|
||||
)
|
||||
private var visibleArea = MutableLiveData(
|
||||
@@ -68,6 +69,7 @@ class SurfaceRenderer(
|
||||
var height = 0
|
||||
var lastBearing = 0.0
|
||||
val routeData = MutableLiveData("")
|
||||
val speedCamerasData = MutableLiveData("")
|
||||
val speed = MutableLiveData(0F)
|
||||
lateinit var centerLocation: Location
|
||||
var viewStyle = ViewStyle.VIEW
|
||||
@@ -165,11 +167,11 @@ class SurfaceRenderer(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun MapView() {
|
||||
val position: CameraPosition? by cameraPosition.observeAsState()
|
||||
val route: String? by routeData.observeAsState()
|
||||
val speedCameras: String? by speedCamerasData.observeAsState()
|
||||
val paddingValues = getPaddingValues(height, viewStyle)
|
||||
val cameraState = cameraState(paddingValues, position, tilt)
|
||||
|
||||
@@ -177,7 +179,7 @@ class SurfaceRenderer(
|
||||
mutableStateOf(BaseStyle.Uri(Constants.STYLE))
|
||||
}
|
||||
DarkMode(carContext, baseStyle)
|
||||
MapLibre(carContext, cameraState, baseStyle, route, viewStyle)
|
||||
MapLibre(carContext, cameraState, baseStyle, route, viewStyle, speedCameras)
|
||||
ShowPosition(cameraState, position, paddingValues)
|
||||
}
|
||||
|
||||
@@ -191,7 +193,13 @@ class SurfaceRenderer(
|
||||
duration(viewStyle == ViewStyle.PREVIEW, position!!.bearing, lastBearing)
|
||||
val currentSpeed: Float? by speed.observeAsState()
|
||||
if (viewStyle == ViewStyle.VIEW) {
|
||||
DrawNavigationImages(paddingValues, currentSpeed, routeModel.routeState.maxSpeed, width, height)
|
||||
DrawNavigationImages(
|
||||
paddingValues,
|
||||
currentSpeed,
|
||||
routeModel.routeState.maxSpeed,
|
||||
width,
|
||||
height
|
||||
)
|
||||
}
|
||||
LaunchedEffect(position, viewStyle) {
|
||||
cameraState.animateTo(
|
||||
@@ -237,7 +245,11 @@ class SurfaceRenderer(
|
||||
fun updateLocation(location: Location) {
|
||||
synchronized(this) {
|
||||
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) {
|
||||
calculateZoom(location.speed.toDouble())
|
||||
} else {
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.maplibre.spatialk.geojson.Feature
|
||||
import org.maplibre.spatialk.geojson.FeatureCollection
|
||||
import org.maplibre.spatialk.geojson.Point
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
@@ -244,8 +245,8 @@ private fun rememberLocationSource(locationState: Location): GeoJsonSource {
|
||||
buildJsonObject {
|
||||
put("accuracy", location.accuracy)
|
||||
put("bearing", location.bearing)
|
||||
//put("bearingAccuracy", location.bearingAccuracy)
|
||||
//put("age", location.timestamp.elapsedNow().inWholeNanoseconds)
|
||||
put("bearingAccuracy", location.hasBearingAccuracy())
|
||||
put("age", location.time.absoluteValue)
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.kouros.navigation.car.map
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.location.Location
|
||||
import android.content.Context
|
||||
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.utils.NavigationUtils.getBooleanKeyValue
|
||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||
import com.kouros.navigation.utils.location
|
||||
import org.maplibre.compose.camera.CameraPosition
|
||||
import org.maplibre.compose.camera.CameraState
|
||||
import org.maplibre.compose.camera.rememberCameraState
|
||||
@@ -89,7 +89,8 @@ fun MapLibre(
|
||||
cameraState: CameraState,
|
||||
baseStyle: MutableState<BaseStyle.Uri>,
|
||||
route: String?,
|
||||
viewStyle: ViewStyle
|
||||
viewStyle: ViewStyle,
|
||||
speedCameras: String? = ""
|
||||
) {
|
||||
MaplibreMap(
|
||||
options = MapOptions(
|
||||
@@ -108,11 +109,12 @@ fun MapLibre(
|
||||
} else {
|
||||
RouteLayer(route)
|
||||
}
|
||||
SpeedCameraLayer(speedCameras)
|
||||
}
|
||||
//val lastLocation = location(cameraState.position.target.longitude, cameraState.position.target.latitude)
|
||||
//Puck(cameraState, lastLocation)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RouteLayer(routeData: String?) {
|
||||
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
|
||||
fun BuildingLayer(tiles: Source) {
|
||||
Anchor.Replace("building-3d") {
|
||||
@@ -188,7 +212,9 @@ fun DrawNavigationImages(
|
||||
height: Int
|
||||
) {
|
||||
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) {
|
||||
MaxSpeed(width, height, maxSpeed)
|
||||
}
|
||||
@@ -211,7 +237,8 @@ fun NavigationImage(padding: PaddingValues, width: Int, height: Int) {
|
||||
painter = painterResource(id = R.drawable.navigation_48px),
|
||||
"Navigation",
|
||||
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),
|
||||
)
|
||||
}
|
||||
@@ -221,7 +248,8 @@ fun NavigationImage(padding: PaddingValues, width: Int, height: Int) {
|
||||
private fun CurrentSpeed(
|
||||
width: Int,
|
||||
height: Int,
|
||||
speed: Float?
|
||||
curSpeed: Float,
|
||||
maxSpeed: Int
|
||||
) {
|
||||
val radius = 32
|
||||
Box(
|
||||
@@ -234,7 +262,8 @@ private fun CurrentSpeed(
|
||||
) {
|
||||
val textMeasurerSpeed = rememberTextMeasurer()
|
||||
val textMeasurerKm = rememberTextMeasurer()
|
||||
val speed = (speed!! * 3.6).toInt().toString()
|
||||
val speed = (curSpeed * 3.6).toInt().toString()
|
||||
|
||||
val kmh = "km/h"
|
||||
val styleSpeed = TextStyle(
|
||||
fontSize = 22.sp,
|
||||
@@ -245,10 +274,10 @@ private fun CurrentSpeed(
|
||||
fontSize = 12.sp,
|
||||
color = Color.White,
|
||||
)
|
||||
val textLayoutSpeed = remember(speed) {
|
||||
val textLayoutSpeed = remember(speed, maxSpeed) {
|
||||
textMeasurerSpeed.measure(speed, styleSpeed)
|
||||
}
|
||||
val textLayoutKm = remember(kmh) {
|
||||
val textLayoutKm = remember(kmh, maxSpeed) {
|
||||
textMeasurerSpeed.measure(kmh, styleKm)
|
||||
}
|
||||
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.overpass.Elements
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.utils.GeoUtils
|
||||
import com.kouros.navigation.utils.bearing
|
||||
import com.kouros.navigation.utils.location
|
||||
import kotlin.math.absoluteValue
|
||||
@@ -60,7 +61,7 @@ class NavigationScreen(
|
||||
val observer = Observer<String> { route ->
|
||||
if (route.isNotEmpty()) {
|
||||
navigationType = NavigationType.NAVIGATION
|
||||
routeModel.startNavigation(route)
|
||||
routeModel.startNavigation(route, carContext)
|
||||
surfaceRenderer.setRouteData()
|
||||
invalidate()
|
||||
}
|
||||
@@ -92,6 +93,16 @@ class NavigationScreen(
|
||||
var speedCameras = listOf<Elements>()
|
||||
val speedObserver = Observer<List<Elements>> { 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 {
|
||||
@@ -339,7 +350,7 @@ class NavigationScreen(
|
||||
|
||||
private fun settingsAction(): Action {
|
||||
return Action.Builder()
|
||||
.setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_applications_48px))
|
||||
.setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_48px))
|
||||
.setOnClickListener {
|
||||
screenManager.push(SettingsScreen(carContext))
|
||||
}
|
||||
@@ -472,7 +483,7 @@ class NavigationScreen(
|
||||
|
||||
private fun updateSpeedCamera(location: Location) {
|
||||
if (lastCameraSearch++ % 100 == 0) {
|
||||
viewModel.getSpeedCameras(location)
|
||||
viewModel.getSpeedCameras(location, 5.0)
|
||||
}
|
||||
if (speedCameras.isNotEmpty()) {
|
||||
updateDistance(location)
|
||||
|
||||
@@ -54,13 +54,13 @@ class PlaceListScreen(
|
||||
}
|
||||
|
||||
init {
|
||||
if (category == Constants.RECENT) {
|
||||
if (category == RECENT) {
|
||||
viewModel.places.observe(this, observer)
|
||||
}
|
||||
if (category == Constants.CONTACTS) {
|
||||
if (category == CONTACTS) {
|
||||
viewModel.contactAddress.observe(this, observerAddress)
|
||||
}
|
||||
if (category == Constants.FAVORITES) {
|
||||
if (category == FAVORITES) {
|
||||
viewModel.favorites.observe(this, observer)
|
||||
}
|
||||
loadPlaces()
|
||||
@@ -84,7 +84,7 @@ class PlaceListScreen(
|
||||
places.forEach {
|
||||
val row = Row.Builder()
|
||||
.setImage(contactIcon(it.avatar, it.category))
|
||||
.setTitle(it.name!!)
|
||||
.setTitle("${it.street!!} ${it.city}")
|
||||
.setOnClickListener {
|
||||
val place = Place(
|
||||
0,
|
||||
@@ -152,7 +152,7 @@ class PlaceListScreen(
|
||||
.setIcon(
|
||||
RouteCarModel().createCarIcon(
|
||||
carContext,
|
||||
R.drawable.ic_pan_24
|
||||
R.drawable.ic_close_white_24dp
|
||||
)
|
||||
)
|
||||
.setOnClickListener {
|
||||
@@ -167,7 +167,7 @@ class PlaceListScreen(
|
||||
.build()
|
||||
|
||||
fun contactIcon(avatar: Uri?, category: String?): CarIcon {
|
||||
if (category == Constants.RECENT || avatar == null) {
|
||||
if (category == RECENT || avatar == null) {
|
||||
return CarIcon.Builder(
|
||||
IconCompat.createWithResource(
|
||||
carContext, R.drawable.ic_place_white_24dp
|
||||
|
||||
@@ -52,7 +52,7 @@ class RoutePreviewScreen(
|
||||
val navigationMessage = NavigationMessage(carContext)
|
||||
val observer = Observer<String> { route ->
|
||||
if (route.isNotEmpty()) {
|
||||
routeModel.startNavigation(route)
|
||||
routeModel.startNavigation(route, carContext)
|
||||
surfaceRenderer.setPreviewRouteData(routeModel)
|
||||
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 MaxSpeedColor = Color(0xFF262525)
|
||||
val MaxSpeedColor = Color(0xFFB71515)
|
||||
|
||||
val PlaceColor = Color(0xFF868005)
|
||||
@@ -124,9 +124,12 @@ data class BoundingBox (
|
||||
|
||||
object Constants {
|
||||
|
||||
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: 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://tiles.openfreemap.org/styles/liberty"
|
||||
const val TAG: String = "Navigation"
|
||||
|
||||
const val CATEGORIES: String = "Categories"
|
||||
@@ -176,6 +179,7 @@ object Constants {
|
||||
|
||||
const val DESTINATION_ARRIVAL_DISTANCE = 40.0
|
||||
|
||||
val ROUTE_ENGINE = RouteEngine.VALHALLA.name
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.kouros.navigation.data
|
||||
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import com.kouros.navigation.data.overpass.Elements
|
||||
import com.kouros.navigation.model.RouteModel
|
||||
@@ -36,10 +37,10 @@ abstract class NavigationRepository {
|
||||
|
||||
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 routeModel = RouteModel()
|
||||
routeModel.startNavigation(route)
|
||||
routeModel.startNavigation(route, context)
|
||||
return routeModel.route.summary!!.distance
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.kouros.navigation.data
|
||||
|
||||
import android.location.Location
|
||||
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.OsrmRoute
|
||||
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.ValhallaRoute
|
||||
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 kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
@@ -30,12 +33,13 @@ data class Route(
|
||||
|
||||
data class Builder(
|
||||
|
||||
var routeEngine : Int = RouteEngine.VALHALLA.ordinal,
|
||||
var routeEngine: Int = 0,
|
||||
var summary: Summary? = null,
|
||||
var legs: List<Leg>? = null,
|
||||
var routeGeoJson: String = "",
|
||||
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 summary(summary: Summary) = apply { this.summary = summary }
|
||||
@@ -44,20 +48,31 @@ data class Route(
|
||||
this.routeGeoJson = routeGeoJson
|
||||
centerLocation = createCenterLocation(routeGeoJson)
|
||||
}
|
||||
|
||||
fun routeEngine(routeEngine: Int) = apply { this.routeEngine = routeEngine }
|
||||
fun waypoints(waypoints: List<List<Double>>) = apply { this.waypoints = waypoints }
|
||||
fun route(route: String) = apply {
|
||||
if (route.isNotEmpty() && route != "[]") {
|
||||
val gson = GsonBuilder().serializeNulls().create()
|
||||
if (routeEngine == RouteEngine.VALHALLA.ordinal) {
|
||||
val jsonObject: Map<String, JsonElement> = Json.parseToJsonElement(route).jsonObject
|
||||
val routeJson = gson.fromJson(jsonObject["trip"].toString(), ValhallaResponse::class.java)
|
||||
when (this.routeEngine) {
|
||||
RouteEngine.VALHALLA.ordinal -> {
|
||||
val jsonObject: Map<String, JsonElement> =
|
||||
Json.parseToJsonElement(route).jsonObject
|
||||
val routeJson =
|
||||
gson.fromJson(
|
||||
jsonObject["trip"].toString(),
|
||||
ValhallaResponse::class.java
|
||||
)
|
||||
ValhallaRoute().mapJsonToValhalla(routeJson, this)
|
||||
} else {
|
||||
}
|
||||
|
||||
else -> {
|
||||
val osrmJson = gson.fromJson(route, OsrmResponse::class.java)
|
||||
OsrmRoute().mapToOsrm(osrmJson, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun build(): Route {
|
||||
return Route(
|
||||
|
||||
@@ -4,7 +4,7 @@ import android.location.Location
|
||||
import com.kouros.navigation.data.NavigationRepository
|
||||
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() {
|
||||
override fun getRoute(
|
||||
@@ -12,7 +12,7 @@ class OsrmRepository : NavigationRepository() {
|
||||
location: Location,
|
||||
searchFilter: SearchFilter
|
||||
): 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)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,84 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
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 {
|
||||
|
||||
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 {
|
||||
|
||||
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> {
|
||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||
httpURLConnection.requestMethod = "POST"
|
||||
@@ -28,7 +31,7 @@ class Overpass {
|
||||
|);
|
||||
|out body;
|
||||
""".trimMargin()
|
||||
println("way[highway](around:$radius,$linestring)")
|
||||
//println("way[highway](around:$radius,$linestring)")
|
||||
return overpassApi(httpURLConnection, searchQuery)
|
||||
}
|
||||
|
||||
|
||||
@@ -245,6 +245,7 @@
|
||||
"type": "fill",
|
||||
"source": "openmaptiles",
|
||||
"source-layer": "water",
|
||||
"maxzoom": 24,
|
||||
"filter": ["!=", ["get", "brunnel"], "tunnel"],
|
||||
"paint": {"fill-color": "rgb(158,189,255)"}
|
||||
},
|
||||
@@ -860,6 +861,7 @@
|
||||
true,
|
||||
false
|
||||
],
|
||||
"layout": {"visibility": "visible"},
|
||||
"paint": {"fill-pattern": "pedestrian_polygon"}
|
||||
},
|
||||
{
|
||||
@@ -1376,11 +1378,13 @@
|
||||
"type": "line",
|
||||
"source": "openmaptiles",
|
||||
"source-layer": "transportation",
|
||||
"maxzoom": 24,
|
||||
"filter": [
|
||||
"all",
|
||||
["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true],
|
||||
["==", ["get", "class"], "transit"]
|
||||
],
|
||||
"layout": {"visibility": "visible"},
|
||||
"paint": {
|
||||
"line-color": "#bbb",
|
||||
"line-width": [
|
||||
@@ -2201,7 +2205,8 @@
|
||||
"text-font": ["Noto Sans Italic"],
|
||||
"text-max-width": 9,
|
||||
"text-offset": [0, 0.6],
|
||||
"text-size": 12
|
||||
"text-size": 12,
|
||||
"visibility": "none"
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#666",
|
||||
@@ -2240,7 +2245,8 @@
|
||||
"text-font": ["Noto Sans Italic"],
|
||||
"text-max-width": 9,
|
||||
"text-offset": [0, 0.6],
|
||||
"text-size": 12
|
||||
"text-size": 12,
|
||||
"visibility": "none"
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#666",
|
||||
@@ -2279,7 +2285,8 @@
|
||||
"text-font": ["Noto Sans Italic"],
|
||||
"text-max-width": 9,
|
||||
"text-offset": [0, 0.6],
|
||||
"text-size": 12
|
||||
"text-size": 12,
|
||||
"visibility": "none"
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#666",
|
||||
@@ -2313,7 +2320,8 @@
|
||||
"text-font": ["Noto Sans Italic"],
|
||||
"text-max-width": 9,
|
||||
"text-offset": [0.9, 0],
|
||||
"text-size": 12
|
||||
"text-size": 12,
|
||||
"visibility": "none"
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#2e5a80",
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
"source": "openmaptiles",
|
||||
"source-layer": "landuse",
|
||||
"filter": ["==", ["get", "class"], "pitch"],
|
||||
"paint": {"fill-color": "#DEE3CD"}
|
||||
"paint": {"fill-color": "rgba(49, 49, 40, 1)"}
|
||||
},
|
||||
{
|
||||
"id": "landuse_track",
|
||||
@@ -247,7 +247,7 @@
|
||||
"source": "openmaptiles",
|
||||
"source-layer": "water",
|
||||
"filter": ["!=", ["get", "brunnel"], "tunnel"],
|
||||
"paint": {"fill-color": "rgb(158,189,255)"}
|
||||
"paint": {"fill-color": "rgba(34, 54, 98, 1)"}
|
||||
},
|
||||
{
|
||||
"id": "landcover_sand",
|
||||
@@ -255,7 +255,7 @@
|
||||
"source": "openmaptiles",
|
||||
"source-layer": "landcover",
|
||||
"filter": ["==", ["get", "class"], "sand"],
|
||||
"paint": {"fill-color": "rgba(247, 239, 195, 1)"}
|
||||
"paint": {"fill-color": "rgba(148, 146, 138, 1)"}
|
||||
},
|
||||
{
|
||||
"id": "aeroway_fill",
|
||||
@@ -654,7 +654,7 @@
|
||||
],
|
||||
"layout": {"line-join": "round"},
|
||||
"paint": {
|
||||
"line-color": "#fff",
|
||||
"line-color": "rgba(74, 64, 64, 1)",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
["exponential", 1.2],
|
||||
@@ -706,7 +706,7 @@
|
||||
],
|
||||
"layout": {"line-join": "round"},
|
||||
"paint": {
|
||||
"line-color": "#fff4c6",
|
||||
"line-color": "rgba(72, 70, 58, 1)",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
["exponential", 1.2],
|
||||
@@ -1154,7 +1154,7 @@
|
||||
],
|
||||
"layout": {"line-cap": "round", "line-join": "round"},
|
||||
"paint": {
|
||||
"line-color": "#fff",
|
||||
"line-color": "rgba(55, 53, 53, 1)",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
["exponential", 1.2],
|
||||
@@ -1691,7 +1691,7 @@
|
||||
["match", ["get", "class"], ["path", "pedestrian"], true, false]
|
||||
],
|
||||
"paint": {
|
||||
"line-color": "hsl(0,0%,100%)",
|
||||
"line-color": "rgba(107, 102, 102, 1)",
|
||||
"line-dasharray": [1, 0.3],
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
@@ -2208,7 +2208,8 @@
|
||||
"text-font": ["Noto Sans Italic"],
|
||||
"text-max-width": 9,
|
||||
"text-offset": [0, 0.6],
|
||||
"text-size": 12
|
||||
"text-size": 12,
|
||||
"visibility": "none"
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#666",
|
||||
@@ -2247,7 +2248,8 @@
|
||||
"text-font": ["Noto Sans Italic"],
|
||||
"text-max-width": 9,
|
||||
"text-offset": [0, 0.6],
|
||||
"text-size": 12
|
||||
"text-size": 12,
|
||||
"visibility": "none"
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#666",
|
||||
@@ -2286,7 +2288,8 @@
|
||||
"text-font": ["Noto Sans Italic"],
|
||||
"text-max-width": 9,
|
||||
"text-offset": [0, 0.6],
|
||||
"text-size": 12
|
||||
"text-size": 12,
|
||||
"visibility": "none"
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#666",
|
||||
@@ -2320,7 +2323,8 @@
|
||||
"text-font": ["Noto Sans Italic"],
|
||||
"text-max-width": 9,
|
||||
"text-offset": [0.9, 0],
|
||||
"text-size": 12
|
||||
"text-size": 12,
|
||||
"visibility": "none"
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#2e5a80",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.kouros.navigation.data
|
||||
package com.kouros.navigation.data.valhalla
|
||||
|
||||
enum class ManeuverType(val value: Int) {
|
||||
None(0),
|
||||
@@ -8,6 +8,8 @@ import com.kouros.navigation.data.ValhallaLocation
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
private const val routeUrl = "https://kouros-online.de/valhalla/route?json="
|
||||
|
||||
|
||||
class ValhallaRepository : NavigationRepository() {
|
||||
|
||||
override fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String {
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
package com.kouros.navigation.model
|
||||
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import androidx.car.app.navigation.model.Maneuver
|
||||
import androidx.car.app.navigation.model.Step
|
||||
import com.kouros.data.R
|
||||
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.Route
|
||||
import com.kouros.navigation.data.RouteEngine
|
||||
import com.kouros.navigation.data.StepData
|
||||
import com.kouros.navigation.data.route.Leg
|
||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||
import com.kouros.navigation.utils.location
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.invoke
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@@ -43,8 +51,10 @@ open class RouteModel() {
|
||||
val legs: Leg
|
||||
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()
|
||||
.routeEngine(routeEngine)
|
||||
.route(routeString)
|
||||
.build()
|
||||
this.routeState = routeState.copy(
|
||||
@@ -65,15 +75,13 @@ open class RouteModel() {
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun updateLocation(location: Location, viewModel: ViewModel) {
|
||||
findStep(location)
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
updateSpeedLimit(location, viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findStep(location: Location) {
|
||||
var nearestDistance = 100000.0f
|
||||
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()) {
|
||||
if (wayIndex >= step.waypointIndex) {
|
||||
val distance = location.distanceTo(location(waypoint[0], waypoint[1]))
|
||||
@@ -83,12 +91,20 @@ open class RouteModel() {
|
||||
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 {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
// speed limit
|
||||
val distance = routeState.lastSpeedLocation.distanceTo(location)
|
||||
if (distance > 500 || routeState.lastSpeedIndex < route.currentStep) {
|
||||
@@ -103,6 +119,7 @@ open class RouteModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun currentStep(): StepData {
|
||||
val currentStep = route.currentStep()
|
||||
@@ -148,11 +165,9 @@ open class RouteModel() {
|
||||
val maneuverType = step.maneuver.type
|
||||
val distanceLeft = leftStepDistance()
|
||||
var text = ""
|
||||
|
||||
when (distanceLeft) {
|
||||
in 0.0..NEXT_STEP_THRESHOLD -> {
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (step.name.isNotEmpty()) {
|
||||
text = step.name
|
||||
@@ -174,11 +189,13 @@ open class RouteModel() {
|
||||
|
||||
fun travelLeftTime(): Double {
|
||||
var timeLeft = 0.0
|
||||
// time for next step until end step
|
||||
for (i in route.currentStep + 1..<legs.steps.size) {
|
||||
val step = legs.steps[i]
|
||||
timeLeft += step.duration
|
||||
}
|
||||
val step = route.nextStep()
|
||||
// time for current step
|
||||
val step = route.currentStep()
|
||||
val curTime = step.duration
|
||||
val percent =
|
||||
100 * (step.maneuver.waypoints.size - step.waypointIndex) / (step.maneuver.waypoints.size)
|
||||
@@ -220,8 +237,8 @@ open class RouteModel() {
|
||||
val curDistance = step.distance
|
||||
val percent =
|
||||
100 * (step.maneuver.waypoints.size - step.waypointIndex) / (step.maneuver.waypoints.size)
|
||||
val time = curDistance * percent / 100
|
||||
leftDistance += time
|
||||
val distance = curDistance * percent / 100
|
||||
leftDistance += distance
|
||||
return leftDistance
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
repository.getRouteDistance(
|
||||
location,
|
||||
plLocation,
|
||||
getSearchFilter(context)
|
||||
getSearchFilter(context), context
|
||||
)
|
||||
place.distance = distance.toFloat()
|
||||
}
|
||||
@@ -134,7 +134,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
for (place in results) {
|
||||
val plLocation = location(place.longitude, place.latitude)
|
||||
val distance =
|
||||
repository.getRouteDistance(location, plLocation, getSearchFilter(context))
|
||||
repository.getRouteDistance(location, plLocation, getSearchFilter(context), context)
|
||||
place.distance = distance.toFloat()
|
||||
}
|
||||
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) {
|
||||
val amenities = Overpass().getAmenities("highway", "speed_camera", location, 5.0)
|
||||
val amenities = Overpass().getAmenities("highway", "speed_camera", location, radius)
|
||||
val distAmenities = mutableListOf<Elements>()
|
||||
amenities.forEach {
|
||||
val plLocation =
|
||||
@@ -380,7 +380,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
for (place in results) {
|
||||
val plLocation = location(place.longitude, place.latitude)
|
||||
val distance =
|
||||
repository.getRouteDistance(location, plLocation, getSearchFilter(context))
|
||||
repository.getRouteDistance(location, plLocation, getSearchFilter(context), context)
|
||||
place.distance = distance.toFloat()
|
||||
}
|
||||
} 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.buildFeatureCollection
|
||||
import org.maplibre.spatialk.geojson.dsl.buildLineString
|
||||
import org.maplibre.spatialk.geojson.dsl.buildMultiPoint
|
||||
import org.maplibre.spatialk.geojson.toJson
|
||||
import org.maplibre.turf.TurfMeasurement
|
||||
import org.maplibre.turf.TurfMisc
|
||||
@@ -32,8 +33,9 @@ object GeoUtils {
|
||||
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)
|
||||
|
||||
var lat = 0
|
||||
|
||||
@@ -4,7 +4,12 @@ import android.content.Context
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
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.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.ZoneId
|
||||
import java.time.ZoneOffset
|
||||
@@ -20,6 +25,14 @@ import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
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 {
|
||||
return context
|
||||
.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
|
||||
.getSharedPreferences(
|
||||
SHARED_PREF_KEY,
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
.getInt(key, 0)
|
||||
.getInt(key, default)
|
||||
}
|
||||
|
||||
fun setIntKeyValue(context: Context, `val`: Int, key: String) {
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
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>
|
||||
|
||||
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