Traffic transfer line to right,
Fuel prices from tankerkönig
This commit is contained in:
@@ -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,8 +691,10 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
cameras.forEach {
|
||||
coordinates.add(listOf(it.lon, it.lat))
|
||||
}
|
||||
val speedData = GeoUtils.createPointCollection(coordinates, "radar")
|
||||
surfaceRenderer.speedCameraData.value = speedData
|
||||
synchronized(this) {
|
||||
val speedData = GeoUtils.createPointCollection(coordinates, "radar")
|
||||
surfaceRenderer.speedCameraData.value = speedData
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user