Traffic
This commit is contained in:
@@ -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,9 +195,49 @@ 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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user