Speed limit
This commit is contained in:
@@ -14,8 +14,8 @@ android {
|
||||
applicationId = "com.kouros.navigation"
|
||||
minSdk = 33
|
||||
targetSdk = 36
|
||||
versionCode = 11
|
||||
versionName = "0.1.3.11"
|
||||
versionCode = 13
|
||||
versionName = "0.1.3.13"
|
||||
base.archivesName = "navi-$versionName"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@ package com.kouros.navigation
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import com.kouros.navigation.data.NavigationRepository
|
||||
import com.kouros.navigation.data.ObjectBox
|
||||
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 org.koin.android.ext.koin.androidContext
|
||||
import org.koin.android.ext.koin.androidLogger
|
||||
import org.koin.core.context.startKoin
|
||||
@@ -26,5 +30,7 @@ class MainApplication : Application() {
|
||||
private set
|
||||
|
||||
var useContacts = false
|
||||
|
||||
val navigationViewModel = ViewModel(ValhallaRepository())
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.kouros.navigation.di
|
||||
|
||||
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 org.koin.core.module.dsl.singleOf
|
||||
import org.koin.core.module.dsl.viewModelOf
|
||||
@@ -8,5 +10,5 @@ import org.koin.dsl.module
|
||||
|
||||
val appModule = module {
|
||||
viewModelOf(::ViewModel)
|
||||
singleOf(::NavigationRepository)
|
||||
singleOf(::ValhallaRepository)
|
||||
}
|
||||
@@ -49,7 +49,7 @@ class MockLocation (private var locationManager: LocationManager) {
|
||||
this.longitude = longitude
|
||||
this.altitude = 0.0
|
||||
this.accuracy = 1.0f
|
||||
this.speed = 0f
|
||||
this.speed = 10f
|
||||
this.time = System.currentTimeMillis()
|
||||
this.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
|
||||
|
||||
@@ -72,7 +72,7 @@ class MockLocation (private var locationManager: LocationManager) {
|
||||
this.longitude = longitude
|
||||
this.altitude = 0.0
|
||||
this.accuracy = 1.0f
|
||||
this.speed = 0f
|
||||
this.speed = 10f
|
||||
this.time = System.currentTimeMillis()
|
||||
this.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
|
||||
|
||||
|
||||
@@ -15,18 +15,13 @@ import androidx.annotation.RequiresPermission
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.BottomSheetScaffold
|
||||
import androidx.compose.material3.BottomSheetScaffoldState
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberBottomSheetScaffoldState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
@@ -37,19 +32,18 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.gms.location.FusedLocationProviderClient
|
||||
import com.google.android.gms.location.LocationServices
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.MainApplication.Companion.navigationViewModel
|
||||
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
||||
import com.kouros.navigation.data.Constants.homeLocation
|
||||
import com.kouros.navigation.data.NavigationRepository
|
||||
import com.kouros.navigation.data.StepData
|
||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||
import com.kouros.navigation.model.MockLocation
|
||||
import com.kouros.navigation.model.RouteModel
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
@@ -73,7 +67,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
val routeData = MutableLiveData("")
|
||||
val viewModel = ViewModel(NavigationRepository())
|
||||
|
||||
val routeModel = RouteModel()
|
||||
var tilt = 50.0
|
||||
val useMock = true
|
||||
@@ -109,8 +103,7 @@ class MainActivity : ComponentActivity() {
|
||||
private var overpass = false
|
||||
|
||||
init {
|
||||
viewModel.route.observe(this, observer)
|
||||
|
||||
navigationViewModel.route.observe(this, observer)
|
||||
}
|
||||
|
||||
@RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
@@ -221,7 +214,7 @@ class MainActivity : ComponentActivity() {
|
||||
closeSheet: () -> Unit
|
||||
) {
|
||||
if (!routeModel.isNavigating()) {
|
||||
SearchSheet(applicationContext, viewModel, lastLocation) { closeSheet() }
|
||||
SearchSheet(applicationContext, navigationViewModel, lastLocation) { closeSheet() }
|
||||
} else {
|
||||
NavigationSheet(
|
||||
routeModel, step!!, nextStep!!,
|
||||
@@ -241,7 +234,7 @@ class MainActivity : ComponentActivity() {
|
||||
val currentLocation = location(location.position.longitude, location.position.latitude)
|
||||
with(routeModel) {
|
||||
if (isNavigating()) {
|
||||
updateLocation(currentLocation)
|
||||
updateLocation(currentLocation, navigationViewModel)
|
||||
stepData.value = currentStep()
|
||||
if (route.currentManeuverIndex + 1 <= route.maneuvers.size) {
|
||||
nextStepData.value = nextStep()
|
||||
@@ -266,7 +259,7 @@ class MainActivity : ComponentActivity() {
|
||||
)
|
||||
lastLocation = currentLocation
|
||||
if (!loadRecentPlaces) {
|
||||
viewModel.loadRecentPlaces(applicationContext, lastLocation)
|
||||
navigationViewModel.loadRecentPlaces(applicationContext, lastLocation)
|
||||
loadRecentPlaces = true
|
||||
}
|
||||
}
|
||||
@@ -320,7 +313,7 @@ class MainActivity : ComponentActivity() {
|
||||
fun test() {
|
||||
for ((index, loc) in routeModel.route.waypoints.withIndex()) {
|
||||
if (index > 300) {
|
||||
routeModel.updateLocation(location(loc[0], loc[1]))
|
||||
routeModel.updateLocation(location(loc[0], loc[1]), navigationViewModel)
|
||||
routeModel.currentStep()
|
||||
if (routeModel.route.currentManeuverIndex + 1 <= routeModel.route.maneuvers.size) {
|
||||
nextStepData.value = routeModel.nextStep()
|
||||
|
||||
@@ -1,62 +1,28 @@
|
||||
package com.kouros.navigation.ui
|
||||
|
||||
import android.R.attr.x
|
||||
import android.R.attr.y
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.absoluteOffset
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.window.layout.WindowMetricsCalculator
|
||||
import com.kouros.navigation.car.ViewStyle
|
||||
import com.kouros.navigation.car.map.BuildingLayer
|
||||
import com.kouros.navigation.car.map.DarkMode
|
||||
import com.kouros.navigation.car.map.MapLibre
|
||||
import com.kouros.navigation.car.map.NavigationImage
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
|
||||
import com.kouros.navigation.data.RouteColor
|
||||
import com.kouros.navigation.data.StepData
|
||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
||||
import org.maplibre.compose.camera.CameraPosition
|
||||
import org.maplibre.compose.camera.CameraState
|
||||
import org.maplibre.compose.camera.rememberCameraState
|
||||
import org.maplibre.compose.expressions.dsl.const
|
||||
import org.maplibre.compose.expressions.dsl.exponential
|
||||
import org.maplibre.compose.expressions.dsl.interpolate
|
||||
import org.maplibre.compose.expressions.dsl.zoom
|
||||
import org.maplibre.compose.layers.CircleLayer
|
||||
import org.maplibre.compose.layers.LineLayer
|
||||
import org.maplibre.compose.location.LocationTrackingEffect
|
||||
import org.maplibre.compose.location.UserLocationState
|
||||
import org.maplibre.compose.map.MapOptions
|
||||
import org.maplibre.compose.map.MaplibreMap
|
||||
import org.maplibre.compose.map.OrnamentOptions
|
||||
import org.maplibre.compose.sources.GeoJsonData
|
||||
import org.maplibre.compose.sources.GeoJsonSource
|
||||
import org.maplibre.compose.sources.getBaseSource
|
||||
import org.maplibre.compose.sources.rememberGeoJsonSource
|
||||
import org.maplibre.compose.style.BaseStyle
|
||||
import org.maplibre.spatialk.geojson.Position
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@@ -25,7 +25,9 @@ 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.TAG
|
||||
import com.kouros.navigation.data.ObjectBox
|
||||
import com.kouros.navigation.data.NavigationRepository
|
||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
||||
|
||||
class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
@@ -67,6 +69,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
}
|
||||
}
|
||||
|
||||
val navigationViewModel = ViewModel(ValhallaRepository())
|
||||
|
||||
init {
|
||||
val lifecycle: Lifecycle = lifecycle
|
||||
@@ -78,7 +81,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
|
||||
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel)
|
||||
|
||||
navigationScreen = NavigationScreen(carContext, surfaceRenderer, routeModel, this)
|
||||
navigationScreen = NavigationScreen(carContext, surfaceRenderer, routeModel, this, navigationViewModel)
|
||||
|
||||
if (carContext.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
== PackageManager.PERMISSION_GRANTED && !useContacts
|
||||
@@ -116,6 +119,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
carContext,
|
||||
surfaceRenderer,
|
||||
location,
|
||||
navigationViewModel,
|
||||
// TODO: Uri
|
||||
)
|
||||
) { obj: Any? ->
|
||||
|
||||
@@ -28,7 +28,7 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.setViewTreeLifecycleOwner
|
||||
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
||||
import com.kouros.navigation.car.map.DarkMode
|
||||
import com.kouros.navigation.car.map.DrawImage
|
||||
import com.kouros.navigation.car.map.DrawNavigationImages
|
||||
import com.kouros.navigation.car.map.MapLibre
|
||||
import com.kouros.navigation.car.map.cameraState
|
||||
import com.kouros.navigation.car.map.getPaddingValues
|
||||
@@ -191,7 +191,7 @@ class SurfaceRenderer(
|
||||
duration(viewStyle == ViewStyle.PREVIEW, position!!.bearing, lastBearing)
|
||||
val currentSpeed: Float? by speed.observeAsState()
|
||||
if (viewStyle == ViewStyle.VIEW) {
|
||||
DrawImage(paddingValues, currentSpeed, width, height)
|
||||
DrawNavigationImages(paddingValues, currentSpeed, routeModel.routeState.maxSpeed, width, height)
|
||||
}
|
||||
LaunchedEffect(position, viewStyle) {
|
||||
cameraState.animateTo(
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.compose.ui.graphics.drawscope.scale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.drawText
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
@@ -33,13 +34,10 @@ 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 kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import org.maplibre.compose.camera.CameraPosition
|
||||
import org.maplibre.compose.camera.CameraState
|
||||
import org.maplibre.compose.camera.rememberCameraState
|
||||
import org.maplibre.compose.expressions.dsl.const
|
||||
import org.maplibre.compose.expressions.dsl.contains
|
||||
import org.maplibre.compose.expressions.dsl.exponential
|
||||
import org.maplibre.compose.expressions.dsl.image
|
||||
import org.maplibre.compose.expressions.dsl.interpolate
|
||||
@@ -60,15 +58,7 @@ import org.maplibre.compose.sources.Source
|
||||
import org.maplibre.compose.sources.getBaseSource
|
||||
import org.maplibre.compose.sources.rememberGeoJsonSource
|
||||
import org.maplibre.compose.style.BaseStyle
|
||||
import org.maplibre.geojson.FeatureCollection
|
||||
import org.maplibre.spatialk.geojson.BoundingBox.Companion.serializer
|
||||
import org.maplibre.spatialk.geojson.Feature
|
||||
import org.maplibre.spatialk.geojson.GeoJson
|
||||
import org.maplibre.spatialk.geojson.Geometry
|
||||
import org.maplibre.spatialk.geojson.Position
|
||||
import org.maplibre.spatialk.geojson.dsl.FeatureBuilder
|
||||
import org.maplibre.spatialk.geojson.dsl.FeatureCollectionBuilder
|
||||
import org.maplibre.spatialk.geojson.dsl.buildFeatureCollection
|
||||
|
||||
|
||||
@Composable
|
||||
@@ -187,9 +177,12 @@ fun BuildingLayer(tiles: Source) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DrawImage(padding: PaddingValues, speed: Float?, width: Int, height: Int) {
|
||||
fun DrawNavigationImages(padding: PaddingValues, speed: Float?, maxSpeed: Int, width: Int, height: Int) {
|
||||
NavigationImage(padding, width, height)
|
||||
Speed(width, height, speed)
|
||||
CurrentSpeed(width, height, speed)
|
||||
if (speed != null && maxSpeed > 0 && (speed * 3.6) > maxSpeed) {
|
||||
MaxSpeed(width, height, maxSpeed)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -215,7 +208,7 @@ fun NavigationImage(padding: PaddingValues, width: Int, height: Int) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Speed(
|
||||
private fun CurrentSpeed(
|
||||
width: Int,
|
||||
height: Int,
|
||||
speed: Float?
|
||||
@@ -235,6 +228,7 @@ private fun Speed(
|
||||
val kmh = "km/h"
|
||||
val styleSpeed = TextStyle(
|
||||
fontSize = 22.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = Color.White,
|
||||
)
|
||||
val styleKm = TextStyle(
|
||||
@@ -278,6 +272,61 @@ private fun Speed(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MaxSpeed(
|
||||
width: Int,
|
||||
height: Int,
|
||||
maxSpeed: Int,
|
||||
) {
|
||||
val radius = 20
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = width.dp - 350.dp,
|
||||
top = height.dp - 80.dp
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
val textMeasurerSpeed = rememberTextMeasurer()
|
||||
val speed = maxSpeed.toString()
|
||||
val styleSpeed = TextStyle(
|
||||
fontSize = 26.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = Color.Black,
|
||||
)
|
||||
val textLayoutSpeed = remember(speed) {
|
||||
textMeasurerSpeed.measure(speed, styleSpeed)
|
||||
}
|
||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||
drawCircle(
|
||||
center = Offset(
|
||||
x = center.x,
|
||||
y = center.y
|
||||
),
|
||||
radius = radius * 1.3.toFloat(),
|
||||
color = Color.Red,
|
||||
)
|
||||
drawCircle(
|
||||
center = Offset(
|
||||
x = center.x,
|
||||
y = center.y
|
||||
),
|
||||
radius = radius.toFloat(),
|
||||
color = Color.White,
|
||||
)
|
||||
drawText(
|
||||
textMeasurer = textMeasurerSpeed,
|
||||
text = speed,
|
||||
style = styleSpeed,
|
||||
topLeft = Offset(
|
||||
x = center.x - textLayoutSpeed.size.width / 2,
|
||||
y = center.y - textLayoutSpeed.size.height / 2,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DarkMode(context: Context, baseStyle: MutableState<BaseStyle.Uri>) {
|
||||
val darkMode = getIntKeyValue(context, Constants.DARK_MODE_SETTINGS)
|
||||
|
||||
@@ -74,7 +74,7 @@ class RouteCarModel() : RouteModel() {
|
||||
return step
|
||||
}
|
||||
|
||||
fun travelEstimate(): TravelEstimate {
|
||||
fun travelEstimate(carContext: CarContext): TravelEstimate {
|
||||
val timeLeft = travelLeftTime()
|
||||
val timeToDestinationMillis =
|
||||
TimeUnit.SECONDS.toMillis(timeLeft.toLong())
|
||||
@@ -88,7 +88,7 @@ class RouteCarModel() : RouteModel() {
|
||||
arrivalTime(),
|
||||
TimeZone.getTimeZone("Europe/Berlin")
|
||||
)
|
||||
return TravelEstimate.Builder( // The estimated distance to the destination.
|
||||
val travelBuilder = TravelEstimate.Builder( // The estimated distance to the destination.
|
||||
Distance.create(
|
||||
leftDistance,
|
||||
displayUnit
|
||||
@@ -102,9 +102,12 @@ class RouteCarModel() : RouteModel() {
|
||||
)
|
||||
.setRemainingTimeColor(CarColor.YELLOW)
|
||||
.setRemainingDistanceColor(CarColor.RED)
|
||||
//.setTripText(createCarText(carContext,R.string.navigate))
|
||||
//.setTripIcon(createCarIcon(carContext, R.drawable.navigation_48px))
|
||||
.build()
|
||||
|
||||
if (routeState.travelMessage.isNotEmpty()) {
|
||||
travelBuilder.setTripIcon(createCarIcon(carContext, R.drawable.warning_24px))
|
||||
travelBuilder.setTripText(CarText.create(routeState.travelMessage))
|
||||
}
|
||||
return travelBuilder.build()
|
||||
}
|
||||
|
||||
fun createString(
|
||||
@@ -144,6 +147,7 @@ class RouteCarModel() : RouteModel() {
|
||||
.addAction(dismissAction).setCallback(object : AlertCallback {
|
||||
override fun onCancel(reason: Int) {
|
||||
}
|
||||
|
||||
override fun onDismiss() {
|
||||
}
|
||||
}).build()
|
||||
|
||||
@@ -18,11 +18,13 @@ import com.kouros.navigation.data.Category
|
||||
import com.kouros.navigation.data.Constants.CHARGING_STATION
|
||||
import com.kouros.navigation.data.Constants.FUEL_STATION
|
||||
import com.kouros.navigation.data.Constants.PHARMACY
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
|
||||
class CategoriesScreen(
|
||||
private val carContext: CarContext,
|
||||
private val surfaceRenderer: SurfaceRenderer,
|
||||
private val location: Location,
|
||||
private val viewModel: ViewModel
|
||||
) : Screen(carContext) {
|
||||
|
||||
var categories: List<Category> = listOf(
|
||||
@@ -46,7 +48,8 @@ class CategoriesScreen(
|
||||
carContext,
|
||||
surfaceRenderer,
|
||||
location,
|
||||
it.id
|
||||
it.id,
|
||||
viewModel
|
||||
)
|
||||
) { obj: Any? ->
|
||||
if (obj != null) {
|
||||
|
||||
@@ -33,9 +33,9 @@ class CategoryScreen(
|
||||
private val surfaceRenderer: SurfaceRenderer,
|
||||
location: Location,
|
||||
private val category: String,
|
||||
private val viewModel: ViewModel
|
||||
) : Screen(carContext) {
|
||||
|
||||
val viewModel = ViewModel(NavigationRepository())
|
||||
var elements = listOf<Elements>()
|
||||
|
||||
val observer = Observer<List<Elements>> { newElements ->
|
||||
@@ -113,7 +113,6 @@ class CategoryScreen(
|
||||
.setOnClickListener {
|
||||
val location = location(it.lon!!, it.lat!!)
|
||||
surfaceRenderer.setCategoryLocation(location, category)
|
||||
println(it)
|
||||
}
|
||||
.setTitle(name)
|
||||
.setImage(carIcon(carContext, category))
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.car.app.model.Action.FLAG_DEFAULT
|
||||
import androidx.car.app.model.ActionStrip
|
||||
import androidx.car.app.model.CarColor
|
||||
import androidx.car.app.model.CarIcon
|
||||
import androidx.car.app.model.CarText
|
||||
import androidx.car.app.model.Distance
|
||||
import androidx.car.app.model.Header
|
||||
import androidx.car.app.model.MessageTemplate
|
||||
@@ -39,7 +40,8 @@ class NavigationScreen(
|
||||
carContext: CarContext,
|
||||
private var surfaceRenderer: SurfaceRenderer,
|
||||
private var routeModel: RouteCarModel,
|
||||
private var listener: Listener
|
||||
private var listener: Listener,
|
||||
private val viewModel: ViewModel
|
||||
) :
|
||||
Screen(carContext) {
|
||||
|
||||
@@ -52,7 +54,7 @@ class NavigationScreen(
|
||||
var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER)
|
||||
var recentPlace = Place()
|
||||
var navigationType = NavigationType.VIEW
|
||||
val viewModel = ViewModel(NavigationRepository())
|
||||
|
||||
val observer = Observer<String> { route ->
|
||||
if (route.isNotEmpty()) {
|
||||
navigationType = NavigationType.NAVIGATION
|
||||
@@ -88,7 +90,6 @@ class NavigationScreen(
|
||||
var speedCameras = listOf<Elements>()
|
||||
val speedObserver = Observer<List<Elements>> { cameras ->
|
||||
speedCameras = cameras
|
||||
println("Speed cameras ${speedCameras.size}")
|
||||
}
|
||||
|
||||
init {
|
||||
@@ -118,7 +119,7 @@ class NavigationScreen(
|
||||
.setNavigationInfo(
|
||||
getRoutingInfo()
|
||||
)
|
||||
.setDestinationTravelEstimate(routeModel.travelEstimate())
|
||||
.setDestinationTravelEstimate(routeModel.travelEstimate(carContext))
|
||||
.setActionStrip(actionStripBuilder.build())
|
||||
.setMapActionStrip(mapActionStripBuilder().build())
|
||||
.setBackgroundColor(CarColor.GREEN)
|
||||
@@ -301,7 +302,7 @@ class NavigationScreen(
|
||||
.setOnClickListener {
|
||||
val navigateTo = location(recentPlace.longitude, recentPlace.latitude)
|
||||
viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, navigateTo)
|
||||
routeModel.routeState.destination = recentPlace
|
||||
routeModel.routeState = routeModel.routeState.copy(destination = recentPlace)
|
||||
}
|
||||
.build()
|
||||
}
|
||||
@@ -394,7 +395,7 @@ class NavigationScreen(
|
||||
private fun startSearchScreen() {
|
||||
screenManager
|
||||
.pushForResult(
|
||||
SearchScreen(carContext, surfaceRenderer, surfaceRenderer.lastLocation)
|
||||
SearchScreen(carContext, surfaceRenderer, surfaceRenderer.lastLocation, viewModel)
|
||||
) { obj: Any? ->
|
||||
if (obj != null) {
|
||||
val place = obj as Place
|
||||
@@ -417,7 +418,7 @@ class NavigationScreen(
|
||||
viewModel.saveRecent(place)
|
||||
currentNavigationLocation = location
|
||||
viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, location)
|
||||
routeModel.routeState.destination = place
|
||||
routeModel.routeState = routeModel.routeState.copy(destination = place)
|
||||
invalidate()
|
||||
}
|
||||
|
||||
@@ -430,8 +431,8 @@ class NavigationScreen(
|
||||
}
|
||||
|
||||
fun calculateNewRoute(destination: Place) {
|
||||
navigationType = NavigationType.REROUTE
|
||||
stopNavigation()
|
||||
navigationType = NavigationType.REROUTE
|
||||
invalidate()
|
||||
val mainThreadHandler = Handler(carContext.mainLooper)
|
||||
mainThreadHandler.post {
|
||||
@@ -451,14 +452,9 @@ class NavigationScreen(
|
||||
}
|
||||
|
||||
fun updateTrip(location: Location) {
|
||||
if (lastCameraSearch++ % 100 == 0) {
|
||||
viewModel.getSpeedCameras(location)
|
||||
}
|
||||
if (speedCameras.isNotEmpty()) {
|
||||
updateDistance(location)
|
||||
}
|
||||
updateSpeedCamera(location)
|
||||
with(routeModel) {
|
||||
updateLocation(location)
|
||||
updateLocation(location, viewModel)
|
||||
if (routeState.maneuverType == Maneuver.TYPE_DESTINATION
|
||||
&& leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
||||
) {
|
||||
@@ -472,6 +468,15 @@ class NavigationScreen(
|
||||
invalidate()
|
||||
}
|
||||
|
||||
private fun updateSpeedCamera(location: Location) {
|
||||
if (lastCameraSearch++ % 100 == 0) {
|
||||
viewModel.getSpeedCameras(location)
|
||||
}
|
||||
if (speedCameras.isNotEmpty()) {
|
||||
updateDistance(location)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDistance(
|
||||
location: Location,
|
||||
) {
|
||||
@@ -485,10 +490,9 @@ class NavigationScreen(
|
||||
}
|
||||
val sortedList = updatedCameras.sortedWith(compareBy { it.distance })
|
||||
val camera = sortedList.first()
|
||||
if (camera.distance < 100) {
|
||||
if (camera.distance < 80) {
|
||||
routeModel.showSpeedCamera(carContext, camera.distance, camera.tags.maxspeed)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@ class PlaceListScreen(
|
||||
private val carContext: CarContext,
|
||||
private val surfaceRenderer: SurfaceRenderer,
|
||||
private val location: Location,
|
||||
private val category: String
|
||||
private val category: String,
|
||||
private val viewModel: ViewModel
|
||||
) : Screen(carContext) {
|
||||
|
||||
val viewModel = ViewModel(NavigationRepository())
|
||||
var places = listOf<Place>()
|
||||
|
||||
val observer = Observer<List<Place>> { newPlaces ->
|
||||
@@ -102,7 +102,8 @@ class PlaceListScreen(
|
||||
RoutePreviewScreen(
|
||||
carContext,
|
||||
surfaceRenderer,
|
||||
place
|
||||
place,
|
||||
viewModel
|
||||
)
|
||||
) { obj: Any? ->
|
||||
if (obj != null) {
|
||||
|
||||
@@ -41,13 +41,12 @@ import java.math.RoundingMode
|
||||
class RoutePreviewScreen(
|
||||
carContext: CarContext,
|
||||
private var surfaceRenderer: SurfaceRenderer,
|
||||
private var destination: Place
|
||||
private var destination: Place,
|
||||
private val viewModel: ViewModel
|
||||
) :
|
||||
Screen(carContext) {
|
||||
private var isFavorite = false
|
||||
|
||||
val vieModel = ViewModel(NavigationRepository())
|
||||
|
||||
val routeModel = RouteCarModel()
|
||||
|
||||
val navigationMessage = NavigationMessage(carContext)
|
||||
@@ -60,9 +59,9 @@ class RoutePreviewScreen(
|
||||
}
|
||||
|
||||
init {
|
||||
vieModel.previewRoute.observe(this, observer)
|
||||
viewModel.previewRoute.observe(this, observer)
|
||||
val location = location(destination.longitude, destination.latitude)
|
||||
vieModel.loadPreviewRoute(carContext, surfaceRenderer.lastLocation, location)
|
||||
viewModel.loadPreviewRoute(carContext, surfaceRenderer.lastLocation, location)
|
||||
}
|
||||
|
||||
override fun onGetTemplate(): Template {
|
||||
@@ -149,7 +148,7 @@ class RoutePreviewScreen(
|
||||
CarToast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
vieModel.saveFavorite(destination)
|
||||
viewModel.saveFavorite(destination)
|
||||
invalidate()
|
||||
}
|
||||
.build()
|
||||
@@ -157,7 +156,7 @@ class RoutePreviewScreen(
|
||||
private fun deleteFavoriteAction(): Action = Action.Builder()
|
||||
.setOnClickListener {
|
||||
if (isFavorite) {
|
||||
vieModel.deleteFavorite(destination)
|
||||
viewModel.deleteFavorite(destination)
|
||||
}
|
||||
isFavorite = !isFavorite
|
||||
finish()
|
||||
|
||||
@@ -28,7 +28,8 @@ import com.kouros.navigation.model.ViewModel
|
||||
class SearchScreen(
|
||||
carContext: CarContext,
|
||||
private var surfaceRenderer: SurfaceRenderer,
|
||||
private var location: Location
|
||||
private var location: Location,
|
||||
private val viewModel: ViewModel
|
||||
) : Screen(carContext) {
|
||||
|
||||
var isSearchComplete: Boolean = false
|
||||
@@ -47,8 +48,6 @@ class SearchScreen(
|
||||
invalidate()
|
||||
}
|
||||
|
||||
val viewModel = ViewModel(NavigationRepository())
|
||||
|
||||
init {
|
||||
viewModel.searchPlaces.observe(this, observer)
|
||||
}
|
||||
@@ -75,6 +74,7 @@ class SearchScreen(
|
||||
carContext,
|
||||
surfaceRenderer,
|
||||
location,
|
||||
viewModel
|
||||
)
|
||||
) { obj: Any? ->
|
||||
surfaceRenderer.viewStyle = ViewStyle.VIEW
|
||||
@@ -90,7 +90,8 @@ class SearchScreen(
|
||||
carContext,
|
||||
surfaceRenderer,
|
||||
location,
|
||||
it.id
|
||||
it.id,
|
||||
viewModel
|
||||
)
|
||||
) { obj: Any? ->
|
||||
if (obj != null) {
|
||||
|
||||
@@ -34,6 +34,5 @@ class ViewModelTest {
|
||||
|
||||
val route = repo.getRoute(fromLocation, toLocation, SearchFilter())
|
||||
model.startNavigation(route)
|
||||
println(route)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,6 @@ val RouteColor = Color(0xFF5582D0)
|
||||
|
||||
val SpeedColor = Color(0xFF262525)
|
||||
|
||||
val MaxSpeedColor = Color(0xFF262525)
|
||||
|
||||
val PlaceColor = Color(0xFF868005)
|
||||
@@ -27,34 +27,14 @@ import java.net.URL
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
|
||||
class NavigationRepository {
|
||||
abstract class NavigationRepository {
|
||||
|
||||
private val placesUrl = "https://kouros-online.de/maps/placespwd";
|
||||
|
||||
private val routeUrl = "https://kouros-online.de/valhalla/route?json="
|
||||
|
||||
private val nominatimUrl = "https://nominatim.openstreetmap.org/"
|
||||
|
||||
// Road classes from highest to lowest are:
|
||||
// motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other.
|
||||
|
||||
// exclude_toll
|
||||
fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String {
|
||||
SearchFilter
|
||||
val vLocation = listOf(
|
||||
Locations(lat = currentLocation.latitude, lon = currentLocation.longitude, search_filter = searchFilter),
|
||||
Locations(lat = location.latitude, lon = location.longitude, search_filter = searchFilter)
|
||||
)
|
||||
val valhallaLocation = ValhallaLocation(
|
||||
locations = vLocation,
|
||||
costing = "auto",
|
||||
units = "km",
|
||||
id = "my_work_route",
|
||||
language = "de-DE"
|
||||
)
|
||||
val routeLocation = Json.encodeToString(valhallaLocation)
|
||||
return fetchUrl(routeUrl + routeLocation, true)
|
||||
}
|
||||
abstract fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String
|
||||
|
||||
fun getRouteDistance(currentLocation: Location, location: Location, searchFilter: SearchFilter): Double {
|
||||
val route = getRoute(currentLocation, location, searchFilter)
|
||||
@@ -98,7 +78,7 @@ class NavigationRepository {
|
||||
return places
|
||||
}
|
||||
|
||||
private fun fetchUrl(url: String, authenticator : Boolean): String {
|
||||
fun fetchUrl(url: String, authenticator : Boolean): String {
|
||||
try {
|
||||
if (authenticator) {
|
||||
Authenticator.setDefault(object : Authenticator() {
|
||||
|
||||
@@ -75,8 +75,8 @@ data class Route(
|
||||
fun route(route: String) = apply {
|
||||
if (route.isNotEmpty() && route != "[]") {
|
||||
val gson = GsonBuilder().serializeNulls().create()
|
||||
val valhalla = gson.fromJson(route, ValhallaJson::class.java)
|
||||
trip = valhalla.trip
|
||||
val routeJson = gson.fromJson(route, ValhallaJson::class.java)
|
||||
trip = routeJson.trip
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Intersections(
|
||||
|
||||
@SerializedName("out") var out: Int? = null,
|
||||
@SerializedName("entry") var entry: ArrayList<Boolean> = arrayListOf(),
|
||||
@SerializedName("bearings") var bearings: ArrayList<Int> = arrayListOf(),
|
||||
@SerializedName("location") var location: ArrayList<Double> = arrayListOf(),
|
||||
@SerializedName("lanes") var lanes: ArrayList<Lane> = arrayListOf(),
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class Lane(
|
||||
@SerializedName("valid" ) var valid: Boolean,
|
||||
@SerializedName("indications" ) var indications: List<String>,
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Legs (
|
||||
|
||||
@SerializedName("steps" ) var steps : ArrayList<Steps> = arrayListOf(),
|
||||
@SerializedName("weight" ) var weight : Double? = null,
|
||||
@SerializedName("summary" ) var summary : String? = null,
|
||||
@SerializedName("duration" ) var duration : Double? = null,
|
||||
@SerializedName("distance" ) var distance : Double? = null
|
||||
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Maneuver (
|
||||
|
||||
@SerializedName("bearing_after" ) var bearingAfter : Int? = null,
|
||||
@SerializedName("bearing_before" ) var bearingBefore : Int? = null,
|
||||
@SerializedName("location" ) var location : ArrayList<Double> = arrayListOf(),
|
||||
@SerializedName("modifier" ) var modifier : String? = null,
|
||||
@SerializedName("type" ) var type : String? = null
|
||||
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class OsrmJson (
|
||||
|
||||
@SerializedName("code" ) var code : String? = null,
|
||||
@SerializedName("routes" ) var routes : ArrayList<Routes> = arrayListOf(),
|
||||
@SerializedName("waypoints" ) var waypoints : ArrayList<Waypoints> = arrayListOf()
|
||||
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
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/"
|
||||
|
||||
class OsrmRepository : NavigationRepository() {
|
||||
override fun getRoute(
|
||||
currentLocation: Location,
|
||||
location: Location,
|
||||
searchFilter: SearchFilter
|
||||
): String {
|
||||
val routeLocation = "${currentLocation.latitude},${currentLocation.longitude};${location.latitude},${location.longitude}?steps=true"
|
||||
return fetchUrl(routeUrl + routeLocation, true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Routes (
|
||||
|
||||
@SerializedName("legs" ) var legs : ArrayList<Legs> = arrayListOf(),
|
||||
@SerializedName("weight_name" ) var weightName : String? = null,
|
||||
@SerializedName("geometry" ) var geometry : String? = null,
|
||||
@SerializedName("weight" ) var weight : Double? = null,
|
||||
@SerializedName("duration" ) var duration : Double? = null,
|
||||
@SerializedName("distance" ) var distance : Double? = null
|
||||
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Steps (
|
||||
|
||||
@SerializedName("intersections" ) var intersections : ArrayList<Intersections> = arrayListOf(),
|
||||
@SerializedName("driving_side" ) var drivingSide : String? = null,
|
||||
@SerializedName("geometry" ) var geometry : String? = null,
|
||||
@SerializedName("maneuver" ) var maneuver : Maneuver? = Maneuver(),
|
||||
@SerializedName("name" ) var name : String? = null,
|
||||
@SerializedName("mode" ) var mode : String? = null,
|
||||
@SerializedName("weight" ) var weight : Double? = null,
|
||||
@SerializedName("duration" ) var duration : Double? = null,
|
||||
@SerializedName("distance" ) var distance : Double? = null
|
||||
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Waypoints (
|
||||
|
||||
@SerializedName("hint" ) var hint : String? = null,
|
||||
@SerializedName("location" ) var location : ArrayList<Double> = arrayListOf(),
|
||||
@SerializedName("name" ) var name : String? = null,
|
||||
@SerializedName("distance" ) var distance : Double? = null
|
||||
|
||||
)
|
||||
1020
common/data/src/main/java/com/kouros/navigation/data/osrm/osrm.json
Normal file
1020
common/data/src/main/java/com/kouros/navigation/data/osrm/osrm.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,8 @@ package com.kouros.navigation.data.overpass
|
||||
|
||||
import android.location.Location
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.kouros.navigation.utils.GeoUtils.getBoundingBox2
|
||||
import com.kouros.navigation.utils.GeoUtils.getOverpassBbox
|
||||
import com.kouros.navigation.utils.NavigationUtils
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.OutputStreamWriter
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
@@ -12,8 +11,33 @@ import java.net.URL
|
||||
class Overpass {
|
||||
|
||||
val overpassUrl = "https://overpass.kumi.systems/api/interpreter"
|
||||
fun getAmenities(type: String, category: String, location: Location) : List<Elements> {
|
||||
val boundingBox = getOverpassBbox(location, 5.0)
|
||||
fun getAround(radius: Int, linestring: String) : List<Elements> {
|
||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||
httpURLConnection.requestMethod = "POST"
|
||||
httpURLConnection.setRequestProperty(
|
||||
"Accept",
|
||||
"application/json"
|
||||
)
|
||||
httpURLConnection.setDoOutput(true);
|
||||
// define search query
|
||||
val searchQuery = """
|
||||
|[out:json];
|
||||
|(
|
||||
| way[highway](around:$radius,$linestring)
|
||||
| ;
|
||||
|);
|
||||
|out body;
|
||||
""".trimMargin()
|
||||
return overpassApi(httpURLConnection, searchQuery)
|
||||
}
|
||||
|
||||
fun getAmenities(
|
||||
type: String,
|
||||
category: String,
|
||||
location: Location,
|
||||
radius: Double
|
||||
): List<Elements> {
|
||||
val boundingBox = getOverpassBbox(location, radius)
|
||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||
httpURLConnection.requestMethod = "POST"
|
||||
httpURLConnection.setRequestProperty(
|
||||
@@ -32,7 +56,10 @@ class Overpass {
|
||||
|);
|
||||
|out body;
|
||||
""".trimMargin()
|
||||
return overpassApi(httpURLConnection, searchQuery)
|
||||
}
|
||||
|
||||
fun overpassApi(httpURLConnection: HttpURLConnection, searchQuery: String) : List<Elements> {
|
||||
// Send the JSON we created
|
||||
val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream)
|
||||
outputStreamWriter.write(searchQuery)
|
||||
@@ -44,7 +71,7 @@ class Overpass {
|
||||
.use { it.readText() } // defaults to UTF-8
|
||||
val gson = GsonBuilder().serializeNulls().create()
|
||||
val overpass = gson.fromJson(response, Amenity::class.java)
|
||||
println("Overpass: $type $response")
|
||||
//println("Overpass: $response")
|
||||
return overpass.elements
|
||||
}
|
||||
return emptyList()
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.kouros.navigation.data.valhalla
|
||||
|
||||
import android.location.Location
|
||||
import com.kouros.navigation.data.Locations
|
||||
import com.kouros.navigation.data.NavigationRepository
|
||||
import com.kouros.navigation.data.SearchFilter
|
||||
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 {
|
||||
SearchFilter
|
||||
val vLocation = listOf(
|
||||
Locations(lat = currentLocation.latitude, lon = currentLocation.longitude, search_filter = searchFilter),
|
||||
Locations(lat = location.latitude, lon = location.longitude, search_filter = searchFilter)
|
||||
)
|
||||
val valhallaLocation = ValhallaLocation(
|
||||
locations = vLocation,
|
||||
costing = "auto",
|
||||
units = "km",
|
||||
id = "my_work_route",
|
||||
language = "de-DE"
|
||||
)
|
||||
val routeLocation = Json.encodeToString(valhallaLocation)
|
||||
return fetchUrl(routeUrl + routeLocation, true)
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,16 @@ 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
|
||||
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
||||
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
||||
import com.kouros.navigation.data.ManeuverType
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.data.Route
|
||||
import com.kouros.navigation.data.StepData
|
||||
import com.kouros.navigation.utils.location
|
||||
import org.maplibre.turf.TurfMeasurement
|
||||
import org.maplibre.turf.TurfMisc
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@@ -21,13 +21,18 @@ open class RouteModel() {
|
||||
data class RouteState(
|
||||
val route: Route? = null,
|
||||
val isNavigating: Boolean = false,
|
||||
var destination: Place = Place(),
|
||||
val destination: Place = Place(),
|
||||
val arrived: Boolean = false,
|
||||
var maneuverType: Int = 0,
|
||||
val maneuverType: Int = 0,
|
||||
var currentShapeIndex: Int = 0,
|
||||
var distanceToStepEnd: Float = 0F,
|
||||
var beginIndex: Int = 0,
|
||||
var endIndex: Int = 0
|
||||
var endIndex: Int = 0,
|
||||
val travelMessage: String = "",
|
||||
// max speed for street (maneuver)
|
||||
val lastSpeedIndex: Int = 0,
|
||||
val maxSpeed: Int = 0,
|
||||
|
||||
)
|
||||
|
||||
var routeState = RouteState()
|
||||
@@ -61,7 +66,8 @@ open class RouteModel() {
|
||||
)
|
||||
}
|
||||
|
||||
fun updateLocation(location: Location) {
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun updateLocation(location: Location, viewModel: ViewModel) {
|
||||
var nearestDistance = 100000.0f
|
||||
var newShapeIndex = -1
|
||||
// find nearest waypoint and current shape index
|
||||
@@ -77,6 +83,24 @@ open class RouteModel() {
|
||||
// find maneuver
|
||||
// calculate distance to step end
|
||||
findManeuver(newShapeIndex)
|
||||
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
updateSpeedLimit(location, viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateSpeedLimit(location: Location, viewModel: ViewModel) {
|
||||
// speed limit for each maneuver index
|
||||
if (routeState.lastSpeedIndex < route.currentManeuverIndex) {
|
||||
routeState = routeState.copy(lastSpeedIndex = route.currentManeuverIndex)
|
||||
val elements = viewModel.getMaxSpeed(location)
|
||||
elements.forEach {
|
||||
if (it.tags.name != null && it.tags.maxspeed != null) {
|
||||
val speed = it.tags.maxspeed!!.toInt()
|
||||
routeState = routeState.copy(maxSpeed = speed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findManeuver(newShapeIndex: Int) {
|
||||
@@ -127,7 +151,7 @@ open class RouteModel() {
|
||||
maneuverType = relevantManeuver.type
|
||||
}
|
||||
val maneuverIconPair = maneuverIcon(maneuverType)
|
||||
routeState.maneuverType = maneuverIconPair.first
|
||||
routeState = routeState.copy(maneuverType = maneuverIconPair.first)
|
||||
// Construct and return the final StepData object
|
||||
return StepData(
|
||||
streetName,
|
||||
@@ -149,6 +173,7 @@ open class RouteModel() {
|
||||
when (distanceLeft) {
|
||||
in 0.0..NEXT_STEP_THRESHOLD -> {
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
|
||||
text = maneuver.streetNames[0]
|
||||
|
||||
@@ -251,7 +251,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
|
||||
fun getAmenities(category: String, location: Location) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val amenities = Overpass().getAmenities("amenity", category, location)
|
||||
val amenities = Overpass().getAmenities("amenity", category, location, 5.0)
|
||||
val distAmenities = mutableListOf<Elements>()
|
||||
amenities.forEach {
|
||||
val plLocation =
|
||||
@@ -267,7 +267,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
|
||||
fun getSpeedCameras(location: Location) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val amenities = Overpass().getAmenities("highway", "speed_camera", location)
|
||||
val amenities = Overpass().getAmenities("highway", "speed_camera", location, 5.0)
|
||||
val distAmenities = mutableListOf<Elements>()
|
||||
amenities.forEach {
|
||||
val plLocation =
|
||||
@@ -281,6 +281,12 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun getMaxSpeed(location: Location) : List<Elements> {
|
||||
val lineString = "${location.latitude},${location.longitude}"
|
||||
val amenities = Overpass().getAround(10, lineString)
|
||||
return amenities
|
||||
}
|
||||
|
||||
fun saveFavorite(place: Place) {
|
||||
place.category = Constants.FAVORITES
|
||||
savePlace(place)
|
||||
|
||||
@@ -74,10 +74,10 @@ fun calculateZoom(speed: Double?): Double {
|
||||
val speedKmh = (speed * 3.6).toInt()
|
||||
val zoom = when (speedKmh) {
|
||||
in 0..10 -> 18.0
|
||||
in 11..30 -> 17.0
|
||||
in 31..50 -> 16.0
|
||||
in 61..70 -> 15.0
|
||||
else -> 14
|
||||
in 11..30 -> 17.5
|
||||
in 31..50 -> 17.0
|
||||
in 61..70 -> 16.5
|
||||
else -> 16.0
|
||||
}
|
||||
return zoom.toDouble()
|
||||
}
|
||||
|
||||
10
common/data/src/main/res/drawable/warning_24px.xml
Normal file
10
common/data/src/main/res/drawable/warning_24px.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M40,840L480,80L920,840L40,840ZM178,760L782,760L480,240L178,760ZM480,720Q497,720 508.5,708.5Q520,697 520,680Q520,663 508.5,651.5Q497,640 480,640Q463,640 451.5,651.5Q440,663 440,680Q440,697 451.5,708.5Q463,720 480,720ZM440,600L520,600L520,400L440,400L440,600ZM480,500L480,500L480,500Z"/>
|
||||
</vector>
|
||||
Reference in New Issue
Block a user