Speed limit
This commit is contained in:
@@ -8,4 +8,6 @@ val RouteColor = Color(0xFF5582D0)
|
||||
|
||||
val SpeedColor = Color(0xFF262525)
|
||||
|
||||
val MaxSpeedColor = Color(0xFF262525)
|
||||
|
||||
val PlaceColor = Color(0xFF868005)
|
||||
@@ -27,34 +27,14 @@ import java.net.URL
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
|
||||
class NavigationRepository {
|
||||
abstract class NavigationRepository {
|
||||
|
||||
private val placesUrl = "https://kouros-online.de/maps/placespwd";
|
||||
|
||||
private val routeUrl = "https://kouros-online.de/valhalla/route?json="
|
||||
|
||||
private val nominatimUrl = "https://nominatim.openstreetmap.org/"
|
||||
|
||||
// Road classes from highest to lowest are:
|
||||
// motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other.
|
||||
|
||||
// exclude_toll
|
||||
fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String {
|
||||
SearchFilter
|
||||
val vLocation = listOf(
|
||||
Locations(lat = currentLocation.latitude, lon = currentLocation.longitude, search_filter = searchFilter),
|
||||
Locations(lat = location.latitude, lon = location.longitude, search_filter = searchFilter)
|
||||
)
|
||||
val valhallaLocation = ValhallaLocation(
|
||||
locations = vLocation,
|
||||
costing = "auto",
|
||||
units = "km",
|
||||
id = "my_work_route",
|
||||
language = "de-DE"
|
||||
)
|
||||
val routeLocation = Json.encodeToString(valhallaLocation)
|
||||
return fetchUrl(routeUrl + routeLocation, true)
|
||||
}
|
||||
abstract fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String
|
||||
|
||||
fun getRouteDistance(currentLocation: Location, location: Location, searchFilter: SearchFilter): Double {
|
||||
val route = getRoute(currentLocation, location, searchFilter)
|
||||
@@ -98,7 +78,7 @@ class NavigationRepository {
|
||||
return places
|
||||
}
|
||||
|
||||
private fun fetchUrl(url: String, authenticator : Boolean): String {
|
||||
fun fetchUrl(url: String, authenticator : Boolean): String {
|
||||
try {
|
||||
if (authenticator) {
|
||||
Authenticator.setDefault(object : Authenticator() {
|
||||
|
||||
@@ -75,8 +75,8 @@ data class Route(
|
||||
fun route(route: String) = apply {
|
||||
if (route.isNotEmpty() && route != "[]") {
|
||||
val gson = GsonBuilder().serializeNulls().create()
|
||||
val valhalla = gson.fromJson(route, ValhallaJson::class.java)
|
||||
trip = valhalla.trip
|
||||
val routeJson = gson.fromJson(route, ValhallaJson::class.java)
|
||||
trip = routeJson.trip
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Intersections(
|
||||
|
||||
@SerializedName("out") var out: Int? = null,
|
||||
@SerializedName("entry") var entry: ArrayList<Boolean> = arrayListOf(),
|
||||
@SerializedName("bearings") var bearings: ArrayList<Int> = arrayListOf(),
|
||||
@SerializedName("location") var location: ArrayList<Double> = arrayListOf(),
|
||||
@SerializedName("lanes") var lanes: ArrayList<Lane> = arrayListOf(),
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class Lane(
|
||||
@SerializedName("valid" ) var valid: Boolean,
|
||||
@SerializedName("indications" ) var indications: List<String>,
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Legs (
|
||||
|
||||
@SerializedName("steps" ) var steps : ArrayList<Steps> = arrayListOf(),
|
||||
@SerializedName("weight" ) var weight : Double? = null,
|
||||
@SerializedName("summary" ) var summary : String? = null,
|
||||
@SerializedName("duration" ) var duration : Double? = null,
|
||||
@SerializedName("distance" ) var distance : Double? = null
|
||||
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Maneuver (
|
||||
|
||||
@SerializedName("bearing_after" ) var bearingAfter : Int? = null,
|
||||
@SerializedName("bearing_before" ) var bearingBefore : Int? = null,
|
||||
@SerializedName("location" ) var location : ArrayList<Double> = arrayListOf(),
|
||||
@SerializedName("modifier" ) var modifier : String? = null,
|
||||
@SerializedName("type" ) var type : String? = null
|
||||
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class OsrmJson (
|
||||
|
||||
@SerializedName("code" ) var code : String? = null,
|
||||
@SerializedName("routes" ) var routes : ArrayList<Routes> = arrayListOf(),
|
||||
@SerializedName("waypoints" ) var waypoints : ArrayList<Waypoints> = arrayListOf()
|
||||
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import android.location.Location
|
||||
import com.kouros.navigation.data.NavigationRepository
|
||||
import com.kouros.navigation.data.SearchFilter
|
||||
|
||||
private const val routeUrl = "https://router.project-osrm.org/route/v1/driving/"
|
||||
|
||||
class OsrmRepository : NavigationRepository() {
|
||||
override fun getRoute(
|
||||
currentLocation: Location,
|
||||
location: Location,
|
||||
searchFilter: SearchFilter
|
||||
): String {
|
||||
val routeLocation = "${currentLocation.latitude},${currentLocation.longitude};${location.latitude},${location.longitude}?steps=true"
|
||||
return fetchUrl(routeUrl + routeLocation, true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Routes (
|
||||
|
||||
@SerializedName("legs" ) var legs : ArrayList<Legs> = arrayListOf(),
|
||||
@SerializedName("weight_name" ) var weightName : String? = null,
|
||||
@SerializedName("geometry" ) var geometry : String? = null,
|
||||
@SerializedName("weight" ) var weight : Double? = null,
|
||||
@SerializedName("duration" ) var duration : Double? = null,
|
||||
@SerializedName("distance" ) var distance : Double? = null
|
||||
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Steps (
|
||||
|
||||
@SerializedName("intersections" ) var intersections : ArrayList<Intersections> = arrayListOf(),
|
||||
@SerializedName("driving_side" ) var drivingSide : String? = null,
|
||||
@SerializedName("geometry" ) var geometry : String? = null,
|
||||
@SerializedName("maneuver" ) var maneuver : Maneuver? = Maneuver(),
|
||||
@SerializedName("name" ) var name : String? = null,
|
||||
@SerializedName("mode" ) var mode : String? = null,
|
||||
@SerializedName("weight" ) var weight : Double? = null,
|
||||
@SerializedName("duration" ) var duration : Double? = null,
|
||||
@SerializedName("distance" ) var distance : Double? = null
|
||||
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.kouros.navigation.data.osrm
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class Waypoints (
|
||||
|
||||
@SerializedName("hint" ) var hint : String? = null,
|
||||
@SerializedName("location" ) var location : ArrayList<Double> = arrayListOf(),
|
||||
@SerializedName("name" ) var name : String? = null,
|
||||
@SerializedName("distance" ) var distance : Double? = null
|
||||
|
||||
)
|
||||
1020
common/data/src/main/java/com/kouros/navigation/data/osrm/osrm.json
Normal file
1020
common/data/src/main/java/com/kouros/navigation/data/osrm/osrm.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,8 @@ package com.kouros.navigation.data.overpass
|
||||
|
||||
import android.location.Location
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.kouros.navigation.utils.GeoUtils.getBoundingBox2
|
||||
import com.kouros.navigation.utils.GeoUtils.getOverpassBbox
|
||||
import com.kouros.navigation.utils.NavigationUtils
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.OutputStreamWriter
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
@@ -12,8 +11,33 @@ import java.net.URL
|
||||
class Overpass {
|
||||
|
||||
val overpassUrl = "https://overpass.kumi.systems/api/interpreter"
|
||||
fun getAmenities(type: String, category: String, location: Location) : List<Elements> {
|
||||
val boundingBox = getOverpassBbox(location, 5.0)
|
||||
fun getAround(radius: Int, linestring: String) : List<Elements> {
|
||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||
httpURLConnection.requestMethod = "POST"
|
||||
httpURLConnection.setRequestProperty(
|
||||
"Accept",
|
||||
"application/json"
|
||||
)
|
||||
httpURLConnection.setDoOutput(true);
|
||||
// define search query
|
||||
val searchQuery = """
|
||||
|[out:json];
|
||||
|(
|
||||
| way[highway](around:$radius,$linestring)
|
||||
| ;
|
||||
|);
|
||||
|out body;
|
||||
""".trimMargin()
|
||||
return overpassApi(httpURLConnection, searchQuery)
|
||||
}
|
||||
|
||||
fun getAmenities(
|
||||
type: String,
|
||||
category: String,
|
||||
location: Location,
|
||||
radius: Double
|
||||
): List<Elements> {
|
||||
val boundingBox = getOverpassBbox(location, radius)
|
||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||
httpURLConnection.requestMethod = "POST"
|
||||
httpURLConnection.setRequestProperty(
|
||||
@@ -32,8 +56,11 @@ class Overpass {
|
||||
|);
|
||||
|out body;
|
||||
""".trimMargin()
|
||||
return overpassApi(httpURLConnection, searchQuery)
|
||||
}
|
||||
|
||||
// Send the JSON we created
|
||||
fun overpassApi(httpURLConnection: HttpURLConnection, searchQuery: String) : List<Elements> {
|
||||
// Send the JSON we created
|
||||
val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream)
|
||||
outputStreamWriter.write(searchQuery)
|
||||
outputStreamWriter.flush()
|
||||
@@ -44,9 +71,9 @@ class Overpass {
|
||||
.use { it.readText() } // defaults to UTF-8
|
||||
val gson = GsonBuilder().serializeNulls().create()
|
||||
val overpass = gson.fromJson(response, Amenity::class.java)
|
||||
println("Overpass: $type $response")
|
||||
//println("Overpass: $response")
|
||||
return overpass.elements
|
||||
}
|
||||
return emptyList()
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.kouros.navigation.data.valhalla
|
||||
|
||||
import android.location.Location
|
||||
import com.kouros.navigation.data.Locations
|
||||
import com.kouros.navigation.data.NavigationRepository
|
||||
import com.kouros.navigation.data.SearchFilter
|
||||
import com.kouros.navigation.data.ValhallaLocation
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
private const val routeUrl = "https://kouros-online.de/valhalla/route?json="
|
||||
class ValhallaRepository : NavigationRepository() {
|
||||
|
||||
override fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String {
|
||||
SearchFilter
|
||||
val vLocation = listOf(
|
||||
Locations(lat = currentLocation.latitude, lon = currentLocation.longitude, search_filter = searchFilter),
|
||||
Locations(lat = location.latitude, lon = location.longitude, search_filter = searchFilter)
|
||||
)
|
||||
val valhallaLocation = ValhallaLocation(
|
||||
locations = vLocation,
|
||||
costing = "auto",
|
||||
units = "km",
|
||||
id = "my_work_route",
|
||||
language = "de-DE"
|
||||
)
|
||||
val routeLocation = Json.encodeToString(valhallaLocation)
|
||||
return fetchUrl(routeUrl + routeLocation, true)
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,16 @@ import android.location.Location
|
||||
import androidx.car.app.navigation.model.Maneuver
|
||||
import androidx.car.app.navigation.model.Step
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
||||
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
||||
import com.kouros.navigation.data.ManeuverType
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.data.Route
|
||||
import com.kouros.navigation.data.StepData
|
||||
import com.kouros.navigation.utils.location
|
||||
import org.maplibre.turf.TurfMeasurement
|
||||
import org.maplibre.turf.TurfMisc
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@@ -21,14 +21,19 @@ open class RouteModel() {
|
||||
data class RouteState(
|
||||
val route: Route? = null,
|
||||
val isNavigating: Boolean = false,
|
||||
var destination: Place = Place(),
|
||||
val destination: Place = Place(),
|
||||
val arrived: Boolean = false,
|
||||
var maneuverType: Int = 0,
|
||||
val maneuverType: Int = 0,
|
||||
var currentShapeIndex: Int = 0,
|
||||
var distanceToStepEnd: Float = 0F,
|
||||
var beginIndex: Int = 0,
|
||||
var endIndex: Int = 0
|
||||
)
|
||||
var endIndex: Int = 0,
|
||||
val travelMessage: String = "",
|
||||
// max speed for street (maneuver)
|
||||
val lastSpeedIndex: Int = 0,
|
||||
val maxSpeed: Int = 0,
|
||||
|
||||
)
|
||||
|
||||
var routeState = RouteState()
|
||||
|
||||
@@ -61,7 +66,8 @@ open class RouteModel() {
|
||||
)
|
||||
}
|
||||
|
||||
fun updateLocation(location: Location) {
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun updateLocation(location: Location, viewModel: ViewModel) {
|
||||
var nearestDistance = 100000.0f
|
||||
var newShapeIndex = -1
|
||||
// find nearest waypoint and current shape index
|
||||
@@ -77,6 +83,24 @@ open class RouteModel() {
|
||||
// find maneuver
|
||||
// calculate distance to step end
|
||||
findManeuver(newShapeIndex)
|
||||
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
updateSpeedLimit(location, viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateSpeedLimit(location: Location, viewModel: ViewModel) {
|
||||
// speed limit for each maneuver index
|
||||
if (routeState.lastSpeedIndex < route.currentManeuverIndex) {
|
||||
routeState = routeState.copy(lastSpeedIndex = route.currentManeuverIndex)
|
||||
val elements = viewModel.getMaxSpeed(location)
|
||||
elements.forEach {
|
||||
if (it.tags.name != null && it.tags.maxspeed != null) {
|
||||
val speed = it.tags.maxspeed!!.toInt()
|
||||
routeState = routeState.copy(maxSpeed = speed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findManeuver(newShapeIndex: Int) {
|
||||
@@ -127,7 +151,7 @@ open class RouteModel() {
|
||||
maneuverType = relevantManeuver.type
|
||||
}
|
||||
val maneuverIconPair = maneuverIcon(maneuverType)
|
||||
routeState.maneuverType = maneuverIconPair.first
|
||||
routeState = routeState.copy(maneuverType = maneuverIconPair.first)
|
||||
// Construct and return the final StepData object
|
||||
return StepData(
|
||||
streetName,
|
||||
@@ -149,6 +173,7 @@ open class RouteModel() {
|
||||
when (distanceLeft) {
|
||||
in 0.0..NEXT_STEP_THRESHOLD -> {
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
|
||||
text = maneuver.streetNames[0]
|
||||
@@ -296,8 +321,8 @@ open class RouteModel() {
|
||||
|
||||
|
||||
fun hasArrived(type: Int): Boolean {
|
||||
// return leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE && type == ManeuverType.DestinationRight.value
|
||||
return type == ManeuverType.DestinationRight.value
|
||||
// return leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE && type == ManeuverType.DestinationRight.value
|
||||
return type == ManeuverType.DestinationRight.value
|
||||
|| type == ManeuverType.Destination.value
|
||||
|| type == ManeuverType.DestinationLeft.value
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
|
||||
fun getAmenities(category: String, location: Location) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val amenities = Overpass().getAmenities("amenity", category, location)
|
||||
val amenities = Overpass().getAmenities("amenity", category, location, 5.0)
|
||||
val distAmenities = mutableListOf<Elements>()
|
||||
amenities.forEach {
|
||||
val plLocation =
|
||||
@@ -267,7 +267,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
|
||||
fun getSpeedCameras(location: Location) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val amenities = Overpass().getAmenities("highway", "speed_camera", location)
|
||||
val amenities = Overpass().getAmenities("highway", "speed_camera", location, 5.0)
|
||||
val distAmenities = mutableListOf<Elements>()
|
||||
amenities.forEach {
|
||||
val plLocation =
|
||||
@@ -281,6 +281,12 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun getMaxSpeed(location: Location) : List<Elements> {
|
||||
val lineString = "${location.latitude},${location.longitude}"
|
||||
val amenities = Overpass().getAround(10, lineString)
|
||||
return amenities
|
||||
}
|
||||
|
||||
fun saveFavorite(place: Place) {
|
||||
place.category = Constants.FAVORITES
|
||||
savePlace(place)
|
||||
|
||||
@@ -74,10 +74,10 @@ fun calculateZoom(speed: Double?): Double {
|
||||
val speedKmh = (speed * 3.6).toInt()
|
||||
val zoom = when (speedKmh) {
|
||||
in 0..10 -> 18.0
|
||||
in 11..30 -> 17.0
|
||||
in 31..50 -> 16.0
|
||||
in 61..70 -> 15.0
|
||||
else -> 14
|
||||
in 11..30 -> 17.5
|
||||
in 31..50 -> 17.0
|
||||
in 61..70 -> 16.5
|
||||
else -> 16.0
|
||||
}
|
||||
return zoom.toDouble()
|
||||
}
|
||||
|
||||
10
common/data/src/main/res/drawable/warning_24px.xml
Normal file
10
common/data/src/main/res/drawable/warning_24px.xml
Normal file
@@ -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="M40,840L480,80L920,840L40,840ZM178,760L782,760L480,240L178,760ZM480,720Q497,720 508.5,708.5Q520,697 520,680Q520,663 508.5,651.5Q497,640 480,640Q463,640 451.5,651.5Q440,663 440,680Q440,697 451.5,708.5Q463,720 480,720ZM440,600L520,600L520,400L440,400L440,600ZM480,500L480,500L480,500Z"/>
|
||||
</vector>
|
||||
Reference in New Issue
Block a user