Traffic
This commit is contained in:
@@ -17,8 +17,8 @@ android {
|
||||
applicationId = "com.kouros.navigation"
|
||||
minSdk = 33
|
||||
targetSdk = 37
|
||||
versionCode = 103
|
||||
versionName = "0.3.0.103"
|
||||
versionCode = 104
|
||||
versionName = "0.3.0.104"
|
||||
base.archivesName = "navi-$versionName"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import androidx.car.app.navigation.model.Trip
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelStore
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
@@ -60,6 +61,7 @@ import com.kouros.navigation.utils.getSettingsViewModel
|
||||
import com.kouros.navigation.utils.location
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
import kotlinx.coroutines.launch
|
||||
import org.maplibre.geojson.FeatureCollection
|
||||
import java.time.Duration
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
@@ -116,6 +118,8 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
var lastAlert : Long = 0
|
||||
var navigationManagerStarted = false
|
||||
|
||||
var updateLocationIndex = 0
|
||||
|
||||
/**
|
||||
* Lifecycle observer for managing session lifecycle events.
|
||||
* Cleans up resources when the session is destroyed.
|
||||
@@ -438,6 +442,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
checkTraffic(LocalDateTime.now(ZoneOffset.UTC), location)
|
||||
surfaceRenderer.updateLocation(location, streetName)
|
||||
}
|
||||
updateLocationIndex++
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -471,7 +476,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
checkArrival()
|
||||
}
|
||||
val endTime = System.currentTimeMillis() - startTime
|
||||
//Log.d(TAG, "handleNavigationLocation: $endTime")
|
||||
Log.d(TAG, "handleNavigationLocation: $endTime")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -507,14 +512,14 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
return
|
||||
}
|
||||
|
||||
val stepData = routeModel.currentStep() // 121 ms
|
||||
val stepData = routeModel.currentStep()
|
||||
|
||||
navigationScreen.updateTrip( // 277 ms
|
||||
navigationScreen.updateTrip(
|
||||
isNavigating = routeModel.isNavigating(),
|
||||
isRerouting = false,
|
||||
hasArrived = routeModel.isArrival(),
|
||||
destinationTravelEstimate = routeModel.getTravelEstimateTrip(carContext), // 60 ms
|
||||
stepTravelEstimate = routeModel.getTravelEstimateStep(carContext), // 60 ms
|
||||
destinationTravelEstimate = routeModel.getTravelEstimateTrip(carContext),
|
||||
stepTravelEstimate = routeModel.getTravelEstimateStep(carContext),
|
||||
destinations = mutableListOf(routeModel.getDestination()),
|
||||
steps = routeModel.getSteps(carContext),
|
||||
stepRemainingDistance = routeModel.getDistance(),
|
||||
@@ -529,7 +534,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
* Updates the trip information and notifies the listener with a new Trip object.
|
||||
* This includes destination name, address, travel estimate, and loading status.
|
||||
*/
|
||||
updateTrip(routeModel.getTrip(carContext)) // 230 ms
|
||||
updateTrip(routeModel.getTrip(carContext))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -703,6 +708,10 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
navigationScreen.invalidate()
|
||||
}
|
||||
|
||||
override fun onTrafficMessageReceived(trafficMessage: String) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a route to the specified place and sets it as the destination.
|
||||
*/
|
||||
@@ -748,6 +757,12 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
lastTrafficDate = current
|
||||
navigationViewModel.loadTraffic(carContext, location, surfaceRenderer.carOrientation)
|
||||
}
|
||||
if (updateLocationIndex % 4 == 0) {
|
||||
// navigationViewModel.updateTrafficMessages(
|
||||
// surfaceRenderer.lastLocation,
|
||||
// surfaceRenderer.trafficData.value!!
|
||||
// )
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.kouros.navigation.car.map
|
||||
|
||||
import android.location.Location
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
@@ -28,12 +29,15 @@ 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.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
|
||||
import org.maplibre.compose.camera.CameraPosition
|
||||
@@ -47,6 +51,8 @@ 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
|
||||
import org.maplibre.compose.layers.LineLayer
|
||||
@@ -61,6 +67,8 @@ 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.geojson.LineString
|
||||
import org.maplibre.spatialk.geojson.Position
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@@ -131,6 +139,7 @@ fun MapLibre(
|
||||
fun StartEndLayer(routeData: String?) {
|
||||
if (!routeData.isNullOrEmpty()) {
|
||||
val end = createEndCollection(routeData)
|
||||
Log.d("Map End", " $end")
|
||||
val routes = rememberGeoJsonSource(GeoJsonData.JsonString(end))
|
||||
val img = image(painterResource(R.drawable.sports_score_48px), drawAsSdf = true)
|
||||
SymbolLayer(
|
||||
@@ -186,8 +195,48 @@ fun TrafficLayer(trafficData: Map<String, String>) {
|
||||
color = trafficColor(it.key),
|
||||
width = routeLineWidth(base = 2.dp, isCasing = false),
|
||||
)
|
||||
val featureCollection = FeatureCollection.fromJson(it.value)
|
||||
if (featureCollection.features()!!.isNotEmpty()) {
|
||||
val points = mutableListOf<List<Double>>()
|
||||
featureCollection.features()!!.forEach { geo ->
|
||||
val coordinates = (geo.geometry() as LineString).coordinates()
|
||||
if (coordinates.size > 5) {
|
||||
val first = coordinates.first()
|
||||
val second = coordinates[1]
|
||||
points.add(listOf(first.coordinates()[0], second.coordinates()[1]))
|
||||
}
|
||||
}
|
||||
val collection = createPointCollection(
|
||||
points, it.key
|
||||
)
|
||||
TrafficPoint(it.key, "${it.key}-point-layer", collection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TrafficPoint(id: String, layer: String, routeData: String?) {
|
||||
if (!routeData.isNullOrEmpty()) {
|
||||
val routes = rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
|
||||
val img = trafficImage(id)
|
||||
SymbolLayer(
|
||||
id = layer,
|
||||
source = routes,
|
||||
iconColor = const(Color.Red),
|
||||
iconImage = img,
|
||||
iconSize =
|
||||
interpolate(
|
||||
type = exponential(2f),
|
||||
input = zoom(),
|
||||
5 to const(0.4f),
|
||||
6 to const(1.6f),
|
||||
7 to const(2.0f),
|
||||
20 to const(5.0f),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reusable helper for consistent line widths across all route layers.
|
||||
@@ -198,12 +247,12 @@ private fun routeLineWidth(base: Dp, isCasing: Boolean = false): Expression<DpVa
|
||||
val width = base + extra
|
||||
|
||||
return interpolate(
|
||||
type = exponential(1.1f),
|
||||
type = exponential(2f),
|
||||
input = zoom(),
|
||||
5 to const(width + 3.dp),
|
||||
10 to const(width + 8.dp),
|
||||
1 to const(width + 3.dp),
|
||||
15 to const(width + 13.dp),
|
||||
20 to const(width + 18.dp),
|
||||
18 to const(width + 15.dp),
|
||||
24 to const(width + 24.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -232,6 +281,16 @@ fun RouteLayerPoint(routeData: String?) {
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun trafficImage(key: String): Expression<ImageValue> {
|
||||
when (key) {
|
||||
"heavy", "queuing", "slow", "stationary" -> return image(painterResource(R.drawable.traffic_jam_24px), drawAsSdf = true)
|
||||
"roadworks" -> return image(painterResource(R.drawable.construction_24px), drawAsSdf = true)
|
||||
"closed", "lane" -> return image(painterResource(R.drawable.remove_road_24px), drawAsSdf = true)
|
||||
}
|
||||
return image(painterResource(R.drawable.traffic_jam_24px), drawAsSdf = true)
|
||||
}
|
||||
|
||||
fun trafficColor(key: String): Expression<ColorValue> {
|
||||
when (key) {
|
||||
"queuing" -> return const(Color(0xFFC46E53))
|
||||
@@ -239,6 +298,8 @@ 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))
|
||||
"lane" -> return const(Color(0xFFEFBAC1))
|
||||
}
|
||||
return const(Color.Blue)
|
||||
}
|
||||
|
||||
+2
@@ -29,4 +29,6 @@ interface NavigationObserverCallback {
|
||||
/** Called to request UI invalidation/refresh */
|
||||
fun invalidateScreen()
|
||||
|
||||
fun onTrafficMessageReceived(trafficMessage: String)
|
||||
|
||||
}
|
||||
|
||||
+3
@@ -14,6 +14,8 @@ class NavigationObserverManager(
|
||||
|
||||
val routeObserver = RouteObserver(callback)
|
||||
val trafficObserver = TrafficObserver(callback)
|
||||
|
||||
val trafficMessageObserver = TrafficMessageObserver(callback)
|
||||
val placeSearchObserver = PlaceSearchObserver(callback)
|
||||
val speedCameraObserver = SpeedCameraObserver(callback)
|
||||
val maxSpeedObserver = MaxSpeedObserver(callback)
|
||||
@@ -21,6 +23,7 @@ class NavigationObserverManager(
|
||||
fun attachAllObservers(session: NavigationSession) {
|
||||
viewModel.route.observe(session, routeObserver)
|
||||
viewModel.traffic.observe(session, trafficObserver)
|
||||
viewModel.trafficMessage.observe(session, trafficMessageObserver)
|
||||
viewModel.placeLocation.observe(session, placeSearchObserver)
|
||||
viewModel.speedCameras.observe(session, speedCameraObserver)
|
||||
viewModel.maxSpeed.observe(session, maxSpeedObserver)
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.kouros.navigation.car.screen.observers
|
||||
|
||||
import androidx.lifecycle.Observer
|
||||
|
||||
/**
|
||||
* Observer for traffic data updates.
|
||||
*/
|
||||
class TrafficMessageObserver(
|
||||
private val callback: NavigationObserverCallback
|
||||
) : Observer<String> {
|
||||
|
||||
override fun onChanged(value: String) {
|
||||
callback.onTrafficMessageReceived(value)
|
||||
callback.invalidateScreen()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.kouros.navigation.data
|
||||
|
||||
import android.util.Log
|
||||
import java.io.File
|
||||
|
||||
data class ApplicationConfig(
|
||||
val user: String,
|
||||
val password: String
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun load(): ApplicationConfig {
|
||||
|
||||
fun Map<String, String>.envOrLookup(key: String): String {
|
||||
return System.getenv(key) ?: this[key]!!
|
||||
}
|
||||
|
||||
val envVars: Map<String, String> = envFile().let { envFile ->
|
||||
if (envFile.exists()) {
|
||||
envFile.readLines()
|
||||
.map { it.split("=") }
|
||||
.filter { it.size == 2 }
|
||||
.associate { it.first().trim() to it.last().trim() }
|
||||
} else emptyMap()
|
||||
}
|
||||
|
||||
return ApplicationConfig(
|
||||
user = envVars.envOrLookup("USER"),
|
||||
password = envVars.envOrLookup("PASSWORD"),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun envFile(): File {
|
||||
|
||||
//val path = context.filesDir.absolutePath
|
||||
//val config = ApplicationConfig.load(context)
|
||||
|
||||
return listOf(".env").map {
|
||||
Log.d("Overpass", it)
|
||||
File(it)
|
||||
}.first { it.exists() }
|
||||
}
|
||||
@@ -3,8 +3,7 @@ package com.kouros.navigation.data
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val NavigationColorLight = Color(0xFF17A119)
|
||||
|
||||
val NavigationColorDark = Color(0xFF07568C)
|
||||
val NavigationColorDark = Color(0xFF413EAD)
|
||||
|
||||
val RouteColor = Color(0xFF5201B4)
|
||||
|
||||
|
||||
@@ -146,6 +146,8 @@ object Constants {
|
||||
|
||||
const val TRAFFIC_UPDATE = 300
|
||||
|
||||
const val TRAFFIC_MESSAGE_UPDATE = 10
|
||||
|
||||
const val SPEED_UPDATE_DISTANCE = 600F
|
||||
|
||||
const val INSTRUCTION_DISTANCE = 50
|
||||
|
||||
@@ -4,9 +4,12 @@ import android.location.Location
|
||||
import android.util.Log
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.kouros.data.BuildConfig
|
||||
import com.kouros.navigation.data.ApplicationConfig
|
||||
import com.kouros.navigation.utils.GeoUtils.getBoundingBox
|
||||
import java.io.OutputStreamWriter
|
||||
import java.net.Authenticator
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.PasswordAuthentication
|
||||
import java.net.URL
|
||||
|
||||
class Overpass {
|
||||
@@ -94,7 +97,17 @@ class Overpass {
|
||||
}
|
||||
|
||||
private fun overpassApi(connection: HttpURLConnection, searchQuery: String): List<Elements> {
|
||||
|
||||
return try {
|
||||
Authenticator.setDefault(object : Authenticator() {
|
||||
override fun getPasswordAuthentication(): PasswordAuthentication {
|
||||
return PasswordAuthentication(
|
||||
"kouros",
|
||||
"eo7sbjyWpmjSVFyELgbfrryqJ6ddNeq9".toCharArray()
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
connection.outputStream.use { os ->
|
||||
OutputStreamWriter(os).use { writer ->
|
||||
writer.write(searchQuery)
|
||||
|
||||
@@ -26,6 +26,7 @@ val useLocal = BuildConfig.DEBUG
|
||||
val useLocalTraffic = BuildConfig.DEBUG
|
||||
|
||||
class TomTomRepository : NavigationRepository() {
|
||||
|
||||
override fun getRoute(
|
||||
context: Context,
|
||||
currentLocation: Location,
|
||||
@@ -102,7 +103,7 @@ class TomTomRepository : NavigationRepository() {
|
||||
if (!showTraffic) {
|
||||
return ""
|
||||
}
|
||||
val bbox = calculateSquareRadius(location.latitude, location.longitude, 15.0)
|
||||
val bbox = calculateSquareRadius(location.latitude, location.longitude, 10.0)
|
||||
return if (useLocalTraffic) {
|
||||
fetchUrl(
|
||||
"http://192.168.1.37/tomtom_traffic.json",
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.kouros.navigation.model
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import android.util.Log
|
||||
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
@@ -11,9 +10,11 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.kouros.navigation.data.ApplicationConfig
|
||||
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.NavigationRepository
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.data.Places
|
||||
@@ -36,6 +37,7 @@ import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.maplibre.geojson.FeatureCollection
|
||||
import org.maplibre.geojson.LineString
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import kotlin.collections.first
|
||||
@@ -59,6 +61,11 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
|
||||
MutableLiveData()
|
||||
}
|
||||
|
||||
/** LiveData containing categorized traffic messages map */
|
||||
val trafficMessage: MutableLiveData<String> by lazy {
|
||||
MutableLiveData()
|
||||
}
|
||||
|
||||
/** LiveData containing a preview route JSON string for route preview screens */
|
||||
val previewRoute: MutableLiveData<String> by lazy {
|
||||
MutableLiveData()
|
||||
@@ -223,33 +230,6 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorizes traffic incidents by type (queuing, stationary, slow, heavy, roadworks).
|
||||
* @param data Raw traffic GeoJSON string
|
||||
* @return Map of incident type to GeoJSON FeatureCollection
|
||||
*/
|
||||
private fun rebuildTraffic(data: String): Map<String, String> {
|
||||
val featureCollection = FeatureCollection.fromJson(data)
|
||||
val incidents = mutableMapOf<String, String>()
|
||||
val queuing = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Queuing traffic") }
|
||||
incidents["queuing"] = FeatureCollection.fromFeatures(queuing).toJson()
|
||||
val stationary = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Stationary traffic") }
|
||||
incidents["stationary"] = FeatureCollection.fromFeatures(stationary).toJson()
|
||||
val slow = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Slow traffic") }
|
||||
incidents["slow"] = FeatureCollection.fromFeatures(slow).toJson()
|
||||
val heavy = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Heavy traffic") }
|
||||
incidents["heavy"] = FeatureCollection.fromFeatures(heavy).toJson()
|
||||
val roadworks = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Roadworks") }
|
||||
incidents["roadworks"] = FeatureCollection.fromFeatures(roadworks).toJson()
|
||||
|
||||
return incidents
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a preview route for route preview screen.
|
||||
* Posts the route JSON to previewRoute LiveData.
|
||||
@@ -278,6 +258,40 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
|
||||
return previewRoute.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorizes traffic incidents by type (queuing, stationary, slow, heavy, roadworks).
|
||||
* @param data Raw traffic GeoJSON string
|
||||
* @return Map of incident type to GeoJSON FeatureCollection
|
||||
*/
|
||||
private fun rebuildTraffic(data: String): Map<String, String> {
|
||||
val featureCollection = FeatureCollection.fromJson(data)
|
||||
val incidents = mutableMapOf<String, String>()
|
||||
val queuing = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Queuing traffic") }
|
||||
incidents["queuing"] = FeatureCollection.fromFeatures(queuing).toJson()
|
||||
val stationary = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Stationary traffic") }
|
||||
incidents["stationary"] = FeatureCollection.fromFeatures(stationary).toJson()
|
||||
val slow = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Slow traffic") }
|
||||
incidents["slow"] = FeatureCollection.fromFeatures(slow).toJson()
|
||||
val heavy = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Heavy traffic") }
|
||||
incidents["heavy"] = FeatureCollection.fromFeatures(heavy).toJson()
|
||||
val roadworks = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Roadworks") }
|
||||
incidents["roadworks"] = FeatureCollection.fromFeatures(roadworks).toJson()
|
||||
val laneClosed = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Lane closed") }
|
||||
incidents["lane"] = FeatureCollection.fromFeatures(laneClosed).toJson()
|
||||
val closed = featureCollection.features()!!
|
||||
.filter { it.properties()!!.get("events").toString().contains("Closed") }
|
||||
incidents["closed"] = FeatureCollection.fromFeatures(closed).toJson()
|
||||
|
||||
|
||||
return incidents
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads device contacts with addresses and converts to Place objects.
|
||||
@@ -463,7 +477,8 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
|
||||
)
|
||||
}
|
||||
}
|
||||
val result = search.sortedWith(compareBy<ElementSearch> { it.distance }.thenByDescending { it.bearing })
|
||||
val result =
|
||||
search.sortedWith(compareBy<ElementSearch> { it.distance }.thenByDescending { it.bearing })
|
||||
if (result.isNotEmpty()) {
|
||||
element = result.first().element
|
||||
speed = if (element.tags.maxspeed == "none" && element.tags.highway == "motorway") {
|
||||
@@ -479,7 +494,11 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
|
||||
return speed
|
||||
}
|
||||
|
||||
private fun isBearingValid(element: Elements, streetBearing : Float, routeBearing: Float) : Boolean {
|
||||
private fun isBearingValid(
|
||||
element: Elements,
|
||||
streetBearing: Float,
|
||||
routeBearing: Float
|
||||
): Boolean {
|
||||
return if (element.tags.oneway.isNotEmpty()) {
|
||||
(streetBearing - routeBearing.absoluteValue) < SPEED_BEARING_DEVIATION
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.kouros.navigation.model
|
||||
|
||||
import android.location.Location
|
||||
import android.util.Log
|
||||
import androidx.car.app.navigation.model.Step
|
||||
import com.kouros.navigation.data.ApplicationConfig
|
||||
import com.kouros.navigation.data.Constants.MAXIMUM_LOCATION_DISTANCE
|
||||
import com.kouros.navigation.data.Constants.NEAREST_LOCATION_DISTANCE
|
||||
import com.kouros.navigation.data.Constants.SPEED_UPDATE_DISTANCE
|
||||
@@ -117,6 +119,7 @@ class RouteCalculator(var routeModel: RouteModel) {
|
||||
if ((distance > SPEED_UPDATE_DISTANCE * 2) || lastSpeedIndex < routeModel.route.currentStepIndex) {
|
||||
lastSpeedIndex = routeModel.route.currentStepIndex
|
||||
lastSpeedLocation = location
|
||||
|
||||
viewModel.updateSpeedLimit(
|
||||
location,
|
||||
routeModel.route.currentStep().street,
|
||||
|
||||
@@ -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="M620,680Q645,680 662.5,662.5Q680,645 680,620Q680,595 662.5,577.5Q645,560 620,560Q595,560 577.5,577.5Q560,595 560,620Q560,645 577.5,662.5Q595,680 620,680ZM260,680Q285,680 302.5,662.5Q320,645 320,620Q320,595 302.5,577.5Q285,560 260,560Q235,560 217.5,577.5Q200,595 200,620Q200,645 217.5,662.5Q235,680 260,680ZM680,480Q597,480 538.5,421.5Q480,363 480,280Q480,198 538,139Q596,80 680,80Q763,80 821.5,138.5Q880,197 880,280Q880,363 821.5,421.5Q763,480 680,480ZM660,320L700,320L700,160L660,160L660,320ZM680,400Q688,400 694,394Q700,388 700,380Q700,372 694,366Q688,360 680,360Q672,360 666,366Q660,372 660,380Q660,388 666,394Q672,400 680,400ZM120,880Q103,880 91.5,868.5Q80,857 80,840L80,520L164,280Q170,262 185.5,251Q201,240 220,240L403,240Q400,260 400,280Q400,300 403,320L234,320L192,440L451,440Q491,497 551,528.5Q611,560 680,560Q711,560 741.5,553.5Q772,547 800,533L800,840Q800,857 788.5,868.5Q777,880 760,880L720,880Q703,880 691.5,868.5Q680,857 680,840L680,800L200,800L200,840Q200,857 188.5,868.5Q177,880 160,880L120,880Z"/>
|
||||
</vector>
|
||||
@@ -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="M756,840L537,621L621,537L840,756L756,840ZM204,840L120,756L396,480L328,412L300,440L249,389L249,471L221,499L100,378L128,350L210,350L160,300L302,158Q322,138 345,129Q368,120 392,120Q416,120 439,129Q462,138 482,158L390,250L440,300L412,328L480,396L570,306Q566,295 563.5,283Q561,271 561,259Q561,200 601.5,159.5Q642,119 701,119Q716,119 729.5,122Q743,125 757,131L658,230L730,302L829,203Q836,217 838.5,230.5Q841,244 841,259Q841,318 800.5,358.5Q760,399 701,399Q689,399 677,397Q665,395 654,390L204,840Z"/>
|
||||
</vector>
|
||||
@@ -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="M720,520L720,160L800,160L800,520L720,520ZM160,800L160,160L240,160L240,800L160,800ZM440,320L440,160L520,160L520,320L440,320ZM440,560L440,400L520,400L520,560L440,560ZM440,800L440,640L520,640L520,800L440,800ZM617,823L702,738L617,654L674,597L759,682L844,597L900,654L815,739L899,824L844,880L758,795L673,880L617,823Z"/>
|
||||
</vector>
|
||||
@@ -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="M80,880Q63,880 51.5,868.5Q40,857 40,840L40,520L125,317Q132,300 147,290Q162,280 180,280L540,280Q558,280 573,290Q588,300 595,317L680,520L680,840Q680,857 668.5,868.5Q657,880 640,880L600,880Q583,880 571.5,868.5Q560,857 560,840L560,800L160,800L160,840Q160,857 148.5,868.5Q137,880 120,880L80,880ZM152,440L567,440L534,360L186,360L152,440ZM262.5,662.5Q280,645 280,620Q280,595 262.5,577.5Q245,560 220,560Q195,560 177.5,577.5Q160,595 160,620Q160,645 177.5,662.5Q195,680 220,680Q245,680 262.5,662.5ZM542.5,662.5Q560,645 560,620Q560,595 542.5,577.5Q525,560 500,560Q475,560 457.5,577.5Q440,595 440,620Q440,645 457.5,662.5Q475,680 500,680Q525,680 542.5,662.5ZM720,760L720,416L647,240L227,240L245,197Q252,180 267,170Q282,160 300,160L660,160Q678,160 693,170Q708,180 715,197L800,400L800,720Q800,737 788.5,748.5Q777,760 760,760L720,760ZM840,640L840,296L767,120L347,120L365,77Q372,60 387,50Q402,40 420,40L780,40Q798,40 813,50Q828,60 835,77L920,280L920,600Q920,617 908.5,628.5Q897,640 880,640L840,640Z"/>
|
||||
</vector>
|
||||
Reference in New Issue
Block a user