Traffic transfer line to right,

Fuel prices from tankerkönig
This commit is contained in:
Dimitris
2026-05-04 15:51:03 +02:00
parent 07cc382d3e
commit 202c2dfcea
17 changed files with 246 additions and 56 deletions
+2 -2
View File
@@ -17,8 +17,8 @@ android {
applicationId = "com.kouros.navigation"
minSdk = 33
targetSdk = 37
versionCode = 104
versionName = "0.3.0.104"
versionCode = 107
versionName = "0.3.0.107"
base.archivesName = "navi-$versionName"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -113,7 +113,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
var lastRouteDate: LocalDateTime = LocalDateTime.now()
var lastAlert : Long = 0
var navigationManagerStarted = false
var updateLocationIndex = 0
@@ -516,7 +515,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
navigationScreen.updateTrip(
isNavigating = routeModel.isNavigating(),
isRerouting = false,
hasArrived = routeModel.isArrival(),
hasArrived = routeModel.isManeuverArrival(),
destinationTravelEstimate = routeModel.getTravelEstimateTrip(carContext),
stepTravelEstimate = routeModel.getTravelEstimateStep(carContext),
destinations = mutableListOf(routeModel.getDestination()),
@@ -540,7 +539,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
* Checks for arrival
*/
fun checkArrival() {
if (routeModel.isArrival()
if (routeModel.isManeuverArrival()
&& routeModel.routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
) {
stopNavigation()
@@ -692,9 +691,11 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
cameras.forEach {
coordinates.add(listOf(it.lon, it.lat))
}
synchronized(this) {
val speedData = GeoUtils.createPointCollection(coordinates, "radar")
surfaceRenderer.speedCameraData.value = speedData
}
}
/**
* Handles received maximum speed data and updates the surface renderer.
@@ -762,12 +763,13 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
* Periodically requests speed camera information near the current location.
*/
private fun updateSpeedCamera(location: Location) {
if (lastCameraSearch++ % 100 == 0) {
if (lastCameraSearch % 100 == 0) {
navigationViewModel.getSpeedCameras(location, 5.0)
}
if (speedCameras.isNotEmpty()) {
if (speedCameras.isNotEmpty() && lastCameraSearch % 15 == 0) {
updateDistance(location)
}
lastCameraSearch++
}
/**
@@ -793,10 +795,10 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
} catch (e: Exception) {
0F
}
if (camera.distance < 80 && (System.currentTimeMillis() - lastAlert > 10000)) {
if (camera.distance < 80) {
if ((bearingSpeedCamera - bearingRoute).absoluteValue < 15.0) {
routeModel.showSpeedCamera(carContext, camera.distance, camera.tags.maxspeed)
lastAlert = System.currentTimeMillis()
camera.distance = 1000.0
}
}
}
@@ -257,8 +257,7 @@ class SurfaceRenderer(
updateCameraPosition(
bearing = bearing,
zoom = zoom,
target = pos,
tilt = tilt
target = pos
)
navigationSession.invalidateNavigationScreen()
}
@@ -29,14 +29,13 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.kouros.data.R
import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.Constants.homeVogelhart
import com.kouros.navigation.data.NavigationColorDark
import com.kouros.navigation.data.NavigationColorLight
import com.kouros.navigation.data.RouteColor
import com.kouros.navigation.data.SpeedColor
import com.kouros.navigation.data.ViewStyle
import com.kouros.navigation.utils.GeoUtils.createEndCollection
import com.kouros.navigation.utils.GeoUtils.createLineStringCollection
import com.kouros.navigation.utils.GeoUtils.createPointCollection
import com.kouros.navigation.utils.isMetricSystem
import com.kouros.navigation.utils.location
@@ -51,7 +50,6 @@ import org.maplibre.compose.expressions.dsl.interpolate
import org.maplibre.compose.expressions.dsl.zoom
import org.maplibre.compose.expressions.value.ColorValue
import org.maplibre.compose.expressions.value.DpValue
import org.maplibre.compose.expressions.value.ExpressionValue
import org.maplibre.compose.expressions.value.ImageValue
import org.maplibre.compose.layers.Anchor
import org.maplibre.compose.layers.FillLayer
@@ -124,14 +122,13 @@ fun MapLibre(
Puck(cameraState, lastLocation)
AmenityLayer(route)
} else {
RouteLayer(route)
TrafficLayer(traffic!!)
RouteLayer(route)
StartEndLayer(route)
//RouteLayerPoint(route )
}
SpeedCameraLayer(speedCameras)
}
}
}
@@ -183,17 +180,11 @@ fun RouteLayer(routeData: String?) {
fun TrafficLayer(trafficData: Map<String, String>) {
trafficData.forEach {
val traffic = rememberGeoJsonSource(GeoJsonData.JsonString(it.value))
LineLayer(
id = "traffic-${it.key}-casing",
source = traffic,
color = const(Color.White),
width = routeLineWidth(base = 2.dp, isCasing = true),
)
LineLayer(
id = "traffic-${it.key}",
source = traffic,
color = trafficColor(it.key),
width = routeLineWidth(base = 2.dp, isCasing = false),
width = routeLineWidth(base = 1.dp, isCasing = false),
)
// TrafficIcons(it)
}
@@ -303,7 +294,7 @@ fun trafficColor(key: String): Expression<ColorValue> {
"stationary" -> return const(Color(0xFF910A0A))
"heavy" -> return const(Color(0xFF6B0404))
"roadworks" -> return const(Color(0xFFDAA707))
"closed" -> return const(Color(0xFFDA0727))
"closed" -> return const(Color(0xFF210C0F))
"lane" -> return const(Color(0xFFEFBAC1))
}
return const(Color.Blue)
@@ -675,7 +666,7 @@ fun Puck(cameraState: CameraState, location: Location) {
locationState = location,
cameraState = cameraState,
accuracyThreshold = 10f,
oldLocationThreshold = 2.seconds,
oldLocationThreshold = 1.seconds,
showBearing = false,
sizes = LocationPuckSizes(dotRadius = 10.dp),
colors = LocationPuckColors(
@@ -1,5 +1,6 @@
package com.kouros.navigation.car.screen
import android.util.Log
import androidx.annotation.DrawableRes
import androidx.car.app.CarContext
import androidx.car.app.Screen
@@ -19,6 +20,9 @@ import androidx.car.app.navigation.model.MapWithContentTemplate
import androidx.car.app.versioning.CarAppApiLevels
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import androidx.lifecycle.asLiveData
import androidx.lifecycle.lifecycleScope
import com.kouros.data.R
import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.car.screen.observers.CategoryObserver
@@ -27,12 +31,17 @@ import com.kouros.navigation.data.Constants
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.data.Constants.TAG
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.overpass.Elements
import com.kouros.navigation.model.NavigationViewModel
import com.kouros.navigation.utils.GeoUtils.createPointCollection
import com.kouros.navigation.utils.getSettingsRepository
import com.kouros.navigation.utils.getSettingsViewModel
import com.kouros.navigation.utils.location
import com.kouros.navigation.utils.round
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlin.math.min
class CategoryScreen(
@@ -43,9 +52,15 @@ class CategoryScreen(
) : Screen(carContext), CategoryObserverCallback {
val repository = getSettingsRepository(carContext)
val settingsViewModel = getSettingsViewModel(carContext)
val maxListItems: Int = 30
var elements: List<Elements> = emptyList()
private val categoryObserver = CategoryObserver(this)
private var loading = true
@@ -56,8 +71,13 @@ class CategoryScreen(
navigationViewModel.elements.value = emptyList()
}
})
repository.lastFuelPricesFlow.asLiveData().observe(this, Observer {
navigationViewModel.getAmenities(carContext, category, surfaceRenderer.lastLocation, it)
})
navigationViewModel.elements.observe(this, categoryObserver)
navigationViewModel.getAmenities(category, surfaceRenderer.lastLocation)
//navigationViewModel.getAmenities(carContext, category, surfaceRenderer.lastLocation)
}
override fun onGetTemplate(): Template {
@@ -134,17 +154,17 @@ class CategoryScreen(
}
.setTitle(name)
.setImage(carIcon(carContext, category, index))
if (category == CHARGING_STATION) {
row.addText("${it.tags.socketType2} X Typ 2 ${it.tags.socketType2Output}")
} else {
row.addText(carText(" ${it.e5} / ${it.e10}${it.tags.openingHours}"))
}
if (it.distance < 1000) {
row.addText("${(it.distance).toInt()} m")
} else {
row.addText("${(it.distance / 1000).round(1)} km")
}
if (category == CHARGING_STATION) {
if (it.tags.socketType2 != null)
row.addText("${it.tags.socketType2} X Typ 2 ${it.tags.socketType2Output}")
} else {
row.addText(carText("${it.tags.openingHours}"))
}
row.addAction(
createAction(carContext, R.drawable.navigation_48px, FLAG_DEFAULT, {
navigationViewModel.loadRoute(
@@ -180,7 +180,7 @@ open class NavigationScreen(
carContext = carContext, R.drawable.ic_recenter_24,
0,
onClickAction = {
surfaceRenderer.viewStyle = ViewStyle.VIEW
surfaceRenderer.setStandardView()
invalidate()
})
})
@@ -2,7 +2,6 @@ package com.kouros.navigation.car.screen
import androidx.car.app.testing.ScreenController
import androidx.car.app.testing.TestCarContext
import androidx.car.app.navigation.model.NavigationTemplate
import androidx.lifecycle.MutableLiveData
import androidx.test.core.app.ApplicationProvider
import com.kouros.navigation.car.SurfaceRenderer
@@ -63,7 +62,6 @@ class NavigationScreenTest {
navigationScreen = NavigationScreen(
testCarContext,
mockSurfaceRenderer,
mockRouteModel,
mockListener,
mockViewModel
)
@@ -120,12 +118,12 @@ class NavigationScreenTest {
// Arrange
navigationScreen.navigationType = NavigationType.NAVIGATION
`when`(mockRouteModel.isArrival()).thenReturn(true)
`when`(mockRouteModel.isManeuverArrival()).thenReturn(true)
`when`(mockRouteModel.routeCalculator).thenReturn(mockRouteCalculator)
`when`(mockRouteCalculator.leftStepDistance()).thenReturn(19.0)
`when`(mockRouteModel.navState).thenReturn(NavigationState())
// Act
navigationScreen.checkArrival()
//navigationScreen.checkArrival()
// Assert
assertThat(navigationScreen.navigationType).isEqualTo(NavigationType.ARRIVAL)
@@ -136,9 +136,9 @@ object Constants {
const val MAXIMAL_SNAP_CORRECTION = 50.0
const val MAXIMAL_ROUTE_DEVIATION = 80.0
const val MAXIMAL_ROUTE_DEVIATION = 100.0
const val DESTINATION_ARRIVAL_DISTANCE = 20.0
const val DESTINATION_ARRIVAL_DISTANCE = 10.0
const val NEAREST_LOCATION_DISTANCE = 10F
@@ -159,6 +159,8 @@ object Constants {
const val AUTOMOTIVE_CAR_SPEED_PERMISSION = "android.car.permission.CAR_SPEED"
const val TILT = 60.0
const val TANKER_KOENIG_DELAY = 300000
}
/**
@@ -6,6 +6,7 @@ import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.kouros.navigation.data.EngineType
@@ -59,6 +60,10 @@ class DataStoreManager(private val context: Context) {
val ALTERNATIVE_ROUTES = booleanPreferencesKey("AlternativeRoutes")
val LAST_FUEL_PRICES = longPreferencesKey("LastFuelPrices")
val FUEL_PRICES = stringPreferencesKey("FuelPrices")
}
// Read values
@@ -151,6 +156,18 @@ class DataStoreManager(private val context: Context) {
preferences[ALTERNATIVE_ROUTES] == true
}
val lastFuelPricesFlow: Flow<Long> =
context.dataStore.data.map { preferences ->
preferences[LAST_FUEL_PRICES]
?: 0
}
val fuelPricesFlow: Flow<String> =
context.dataStore.data.map { preferences ->
preferences[FUEL_PRICES]
?: ""
}
// Save values
suspend fun setShow3D(enabled: Boolean) {
context.dataStore.edit { preferences ->
@@ -248,4 +265,16 @@ class DataStoreManager(private val context: Context) {
}
}
suspend fun setLastFuelPrices(lastFuelPrices: Long) {
context.dataStore.edit { prefs ->
prefs[LAST_FUEL_PRICES] = lastFuelPrices
}
}
suspend fun setFuelPrices(fuelPrices: String) {
context.dataStore.edit { prefs ->
prefs[FUEL_PRICES] = fuelPrices
}
}
}
@@ -4,6 +4,7 @@ import android.content.Context
import android.location.Location
import android.util.Log
import com.google.gson.GsonBuilder
import com.kouros.data.BuildConfig
import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.SearchFilter
import com.kouros.navigation.data.overpass.Amenity
@@ -13,6 +14,7 @@ import java.net.HttpURLConnection
import java.net.PasswordAuthentication
import java.net.URL
const val tankerKoenigUrl = "https://creativecommons.tankerkoenig.de/json/list.php?"
private val gson = GsonBuilder().serializeNulls().create()
@@ -20,11 +22,18 @@ private val gson = GsonBuilder().serializeNulls().create()
const val sort = "&sort=dist&type=all"
const val apiKey = "&apikey=fc9900c9-8f02-4b28-990d-dc6067228c59"
val useLocal = BuildConfig.DEBUG
class FuelPrices : NavigationRepository() {
fun getFuelPrices(location: Location, radius: Int) : List<Station> {
val url = "${tankerKoenigUrl}lat=${location.latitude}&lng=${location.longitude}&rad=${radius}$sort$apiKey"
val url = if (useLocal) {
"http://192.168.1.37/fuel.json"
} else {
"${tankerKoenigUrl}lat=${location.latitude}&lng=${location.longitude}&rad=${radius}$sort$apiKey"
}
val prices = fetchUrl(
url,
@@ -1,5 +1,10 @@
package com.kouros.navigation.data.fuel
import com.kouros.navigation.data.Place
data class Stations(
val stations: List<Station>,
)
data class Station(
val brand: String,
val diesel: Double,
@@ -9,6 +9,9 @@ data class Elements(
val lon: Double = 0.0,
val tags: Tags,
val type: String = "",
var distance : Double = 0.0
var distance : Double = 0.0,
var e5 : Double = 0.0,
var e10: Double = 0.0,
var diesel: Double = 0.0
)
@@ -14,22 +14,29 @@ import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.Constants.SPEED_BEARING_DEVIATION
import com.kouros.navigation.data.Constants.SPEED_UPDATE_DISTANCE
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.Constants.TANKER_KOENIG_DELAY
import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.Places
import com.kouros.navigation.data.SearchFilter
import com.kouros.navigation.data.fuel.FuelPrice
import com.kouros.navigation.data.fuel.FuelPrices
import com.kouros.navigation.data.fuel.Station
import com.kouros.navigation.data.fuel.Stations
import com.kouros.navigation.data.nominatim.Search
import com.kouros.navigation.data.nominatim.SearchResult
import com.kouros.navigation.data.overpass.ElementSearch
import com.kouros.navigation.data.overpass.Elements
import com.kouros.navigation.data.overpass.Overpass
import com.kouros.navigation.utils.GeoUtils.snapLocation
import com.kouros.navigation.utils.GeoUtils.transferCoordinates
import com.kouros.navigation.utils.bearingPositive
import com.kouros.navigation.utils.countryCodeSpeedLimit
import com.kouros.navigation.utils.getSettingsRepository
import com.kouros.navigation.utils.getSettingsViewModel
import com.kouros.navigation.utils.isNumeric
import com.kouros.navigation.utils.location
import com.kouros.navigation.utils.settingsViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -273,26 +280,25 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
val incidents = mutableMapOf<String, String>()
val queuing = featureCollection.features()!!
.filter { it.properties()!!.get("events").toString().contains("Queuing traffic") }
incidents["queuing"] = FeatureCollection.fromFeatures(queuing).toJson()
incidents["queuing"] = transferCoordinates(queuing)
val stationary = featureCollection.features()!!
.filter { it.properties()!!.get("events").toString().contains("Stationary traffic") }
incidents["stationary"] = FeatureCollection.fromFeatures(stationary).toJson()
incidents["stationary"] = transferCoordinates(stationary)
val slow = featureCollection.features()!!
.filter { it.properties()!!.get("events").toString().contains("Slow traffic") }
incidents["slow"] = FeatureCollection.fromFeatures(slow).toJson()
incidents["slow"] = transferCoordinates(slow)
val heavy = featureCollection.features()!!
.filter { it.properties()!!.get("events").toString().contains("Heavy traffic") }
incidents["heavy"] = FeatureCollection.fromFeatures(heavy).toJson()
incidents["heavy"] = transferCoordinates(heavy)
val roadworks = featureCollection.features()!!
.filter { it.properties()!!.get("events").toString().contains("Roadworks") }
incidents["roadworks"] = FeatureCollection.fromFeatures(roadworks).toJson()
incidents["roadworks"] = transferCoordinates(roadworks)
val laneClosed = featureCollection.features()!!
.filter { it.properties()!!.get("events").toString().contains("Lane closed") }
incidents["lane"] = FeatureCollection.fromFeatures(laneClosed).toJson()
incidents["lane"] = transferCoordinates(laneClosed)
val closed = featureCollection.features()!!
.filter { it.properties()!!.get("events").toString().contains("Closed") }
incidents["closed"] = FeatureCollection.fromFeatures(closed).toJson()
incidents["closed"] = transferCoordinates(closed)
return incidents
}
@@ -391,15 +397,30 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
* Queries Overpass API for nearby amenities of a specific category.
* Posts sorted results to elements LiveData.
*/
fun getAmenities(category: String, location: Location) {
fun getAmenities(carContext: Context, category: String, location: Location, lastFuelUpdate: Long = 0) {
viewModelScope.launch(Dispatchers.IO) {
val repository = getSettingsRepository(carContext)
var fuelPrices: List<Station>
val amenities = Overpass().getAmenities("amenity", category, location, 5.0)
val now = System.currentTimeMillis()
if (category == Constants.FUEL_STATION && ((now - lastFuelUpdate) > TANKER_KOENIG_DELAY)) {
fuelPrices = FuelPrices().getFuelPrices(location, 5)
repository.setLastFuelPrices(System.currentTimeMillis())
repository.setFuelPrices(gson.toJson(Stations(fuelPrices)))
} else {
val fuels = repository.fuelPricesFlow.first()
fuelPrices =
gson.fromJson(fuels, Stations::class.java).stations
}
val distAmenities = mutableListOf<Elements>()
amenities.forEach {
val plLocation =
location(longitude = it.lon, latitude = it.lat)
val distance = plLocation.distanceTo(location)
it.distance = distance.toDouble()
if (category == Constants.FUEL_STATION) {
addFuelPrice(it, fuelPrices, location)
}
distAmenities.add(it)
}
val sortedList = distAmenities.sortedWith(compareBy { it.distance })
@@ -407,6 +428,21 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
}
}
fun addFuelPrice(fuel: Elements, fuelPrices: List<Station>, location: Location) {
fuelPrices.forEach {
if (fuel.tags.name.equals(it.brand, ignoreCase = true)) {
val plLocation =
location(longitude = it.lng, latitude = it.lat)
val distance = plLocation.distanceTo(location)
if ((distance - fuel.distance).absoluteValue < 30) {
fuel.e10 = it.e10
fuel.e5 = it.e5
fuel.diesel = it.diesel
}
}
}
}
/**
* Queries Overpass API for speed cameras within a radius.
* Posts sorted results to speedCameras LiveData.
@@ -1,12 +1,10 @@
package com.kouros.navigation.model
import android.location.Location
import android.util.Log
import androidx.car.app.connection.CarConnection.CONNECTION_TYPE_NATIVE
import androidx.car.app.connection.CarConnection.CONNECTION_TYPE_PROJECTION
import androidx.car.app.navigation.model.Maneuver
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.NavigationState
import com.kouros.navigation.data.Route
import com.kouros.navigation.data.StepData
@@ -170,7 +168,7 @@ open class RouteModel {
/**
* Checks for arrival
*/
fun isArrival(): Boolean {
fun isManeuverArrival(): Boolean {
return navState.maneuverType == Maneuver.TYPE_DESTINATION
|| navState.maneuverType == Maneuver.TYPE_DESTINATION_LEFT
|| navState.maneuverType == Maneuver.TYPE_DESTINATION_RIGHT
@@ -105,6 +105,18 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel(
false
)
val lastFuelPrices = repository.lastFuelPricesFlow.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000),
0
)
val fuelPrices = repository.fuelPricesFlow.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000),
0
)
fun onShow3DChanged(enabled: Boolean) {
viewModelScope.launch { repository.setShow3D(enabled) }
}
@@ -165,4 +177,12 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel(
fun onAlternativeRoutes(enabled: Boolean) {
viewModelScope.launch { repository.setAlternativeRoutes(enabled) }
}
fun onLastFuelPricesChanged(lastFuelPrices: Long) {
viewModelScope.launch { repository.setLastFuelPrices(lastFuelPrices) }
}
fun onFuelPricesChanged(fuelPrices: String) {
viewModelScope.launch { repository.setFuelPrices(fuelPrices) }
}
}
@@ -2,6 +2,7 @@ package com.kouros.navigation.repository
import android.util.Log
import com.kouros.navigation.data.datastore.DataStoreManager
import com.kouros.navigation.data.fuel.FuelPrices
import kotlinx.coroutines.flow.Flow
class SettingsRepository(
@@ -54,6 +55,13 @@ class SettingsRepository(
val alternativeRoutesFlow: Flow<Boolean> =
dataStoreManager.alternativeRoutesFlow
val lastFuelPricesFlow: Flow<Long> =
dataStoreManager.lastFuelPricesFlow
val fuelPricesFlow: Flow<String> =
dataStoreManager.fuelPricesFlow
suspend fun setShow3D(enabled: Boolean) {
dataStoreManager.setShow3D(enabled)
}
@@ -117,4 +125,12 @@ class SettingsRepository(
suspend fun setAlternativeRoutes(enabled: Boolean) {
dataStoreManager.setAlternativeRoutes(enabled)
}
suspend fun setLastFuelPrices(lastFuelPrices: Long) {
dataStoreManager.setLastFuelPrices(lastFuelPrices)
}
suspend fun setFuelPrices(fuelPrices: String) {
dataStoreManager.setFuelPrices(fuelPrices)
}
}
@@ -2,7 +2,6 @@ package com.kouros.navigation.utils
import android.location.Location
import android.util.Log
import com.kouros.navigation.data.Constants.TAG
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import org.maplibre.geojson.FeatureCollection
@@ -17,8 +16,11 @@ import org.maplibre.turf.TurfMeasurement
import org.maplibre.turf.TurfMisc
import java.lang.Math.toDegrees
import java.lang.Math.toRadians
import kotlin.math.asin
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin
object GeoUtils {
@@ -185,4 +187,64 @@ object GeoUtils {
&& location.longitude <= topRightLon
return isInside
}
fun transferCoordinates(features: List<org.maplibre.geojson.Feature>): String {
return buildFeatureCollection {
features.forEach {
val movedCoordinates = arrayListOf<org.maplibre.spatialk.geojson.Point>()
val coordinates = it.geometry() as LineString
val geo = coordinates.coordinates()
var bearing = 0.0F
for (index in 0..<geo.size) {
val current = location(geo[index].longitude(), geo[index].latitude())
bearing = if (index < geo.size - 1) {
val next =
location(geo[index + 1].longitude(), geo[index + 1].latitude())
current.bearingPositive(next)
} else {
bearing
}
val point = movePoint(
current.latitude,
current.longitude,
bearingDegrees = bearing + 90.0
)
movedCoordinates.add(point)
}
val lineString = buildLineString {
movedCoordinates.forEach { ft ->
add(
ft
)
}
}
addFeature {
geometry = lineString
properties = buildJsonObject { null }
}
}
}.toJson()
}
fun movePoint(
latitude: Double,
longitude: Double,
distanceMeters: Double = 7.0,
bearingDegrees: Double = 0.0
): org.maplibre.spatialk.geojson.Point {
val earthRadius = 6371000.0 // meters
val bearingRadians = toRadians(bearingDegrees)
val lat1 = toRadians(latitude)
val lon1 = toRadians(longitude)
val lat2 = asin(
sin(lat1) * cos(distanceMeters / earthRadius) +
cos(lat1) * sin(distanceMeters / earthRadius) * cos(bearingRadians)
)
val lon2 = lon1 + atan2(
sin(bearingRadians) * sin(distanceMeters / earthRadius) * cos(lat1),
cos(distanceMeters / earthRadius) - sin(lat1) * sin(lat2)
)
return org.maplibre.spatialk.geojson.Point(toDegrees(lon2), toDegrees(lat2))
}
}