Traffic transfer line to right,
Fuel prices from tankerkönig
This commit is contained in:
@@ -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))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user