Refactoring
This commit is contained in:
@@ -2,7 +2,7 @@ package com.kouros.navigation.data
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val NavigationColor = Color(0xFF052186)
|
||||
val NavigationColor = Color(0xFF0730B2)
|
||||
|
||||
val RouteColor = Color(0xFF5582D0)
|
||||
|
||||
|
||||
@@ -164,6 +164,8 @@ object Constants {
|
||||
|
||||
const val SHOW_THREED_BUILDING = "Show3D"
|
||||
|
||||
const val DARK_MODE_SETTINGS = "DarkMode"
|
||||
|
||||
const val AVOID_MOTORWAY = "AvoidMotorway"
|
||||
|
||||
const val AVOID_TOLLWAY = "AvoidTollway"
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
package com.kouros.navigation.data
|
||||
|
||||
import android.location.Location
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.kouros.navigation.data.valhalla.Maneuvers
|
||||
import com.kouros.navigation.data.valhalla.Summary
|
||||
import com.kouros.navigation.data.valhalla.Trip
|
||||
import com.kouros.navigation.data.valhalla.ValhallaJson
|
||||
import com.kouros.navigation.utils.NavigationUtils.createCenterLocation
|
||||
import com.kouros.navigation.utils.NavigationUtils.createGeoJson
|
||||
import com.kouros.navigation.utils.NavigationUtils.decodePolyline
|
||||
import com.kouros.navigation.utils.location
|
||||
import org.maplibre.geojson.FeatureCollection
|
||||
import org.maplibre.geojson.Point
|
||||
import org.maplibre.turf.TurfMeasurement
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
data class Route (
|
||||
data class Route(
|
||||
/**
|
||||
* A Leg is a route between only two waypoints.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
var maneuvers: List<Maneuvers>,
|
||||
val maneuvers: List<Maneuvers>,
|
||||
|
||||
/**
|
||||
* The distance traveled from origin to destination.
|
||||
@@ -33,9 +39,16 @@ data class Route (
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
var waypoints: List<List<Double>>,
|
||||
val waypoints: List<List<Double>>,
|
||||
|
||||
val pointLocations : List<Point>,
|
||||
/**
|
||||
* List of [List<Point>] objects. Each `Point` is an input coordinate
|
||||
* snapped to the road and path network. The `waypoint` appear in the list in the order of
|
||||
* the input coordinates.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
val pointLocations: List<Point>,
|
||||
|
||||
val summary: Summary,
|
||||
|
||||
@@ -43,27 +56,26 @@ data class Route (
|
||||
|
||||
val time: Double,
|
||||
|
||||
var routeGeoJson : String,
|
||||
val routeGeoJson: String,
|
||||
|
||||
var currentManeuverIndex: Int
|
||||
val currentManeuverIndex : Int,
|
||||
|
||||
val centerLocation: Location
|
||||
|
||||
) {
|
||||
|
||||
class Builder {
|
||||
private lateinit var maneuvers: List<Maneuvers>
|
||||
private var distance: Double = 0.0
|
||||
|
||||
private var time: Double = 0.0
|
||||
private lateinit var waypoints: List<List<Double>>
|
||||
private lateinit var pointLocations: List<Point>
|
||||
|
||||
private lateinit var summary : Summary
|
||||
|
||||
private lateinit var trip : Trip
|
||||
|
||||
private lateinit var summary: Summary
|
||||
private lateinit var trip: Trip
|
||||
private var routeGeoJson = ""
|
||||
private var centerLocation = location(0.0, 0.0)
|
||||
|
||||
fun route (route: String ) = apply {
|
||||
fun route(route: String) = apply {
|
||||
if (route.isNotEmpty() && route != "[]") {
|
||||
val gson = GsonBuilder().serializeNulls().create()
|
||||
val valhalla = gson.fromJson(route, ValhallaJson::class.java)
|
||||
@@ -83,35 +95,37 @@ data class Route (
|
||||
points.add(point)
|
||||
}
|
||||
pointLocations = points
|
||||
this.routeGeoJson = createGeoJson(waypoints)
|
||||
routeGeoJson = createGeoJson(waypoints)
|
||||
centerLocation = createCenterLocation(routeGeoJson)
|
||||
return Route(
|
||||
maneuvers, distance, waypoints, pointLocations, summary, trip, time, routeGeoJson, 0
|
||||
maneuvers,
|
||||
distance,
|
||||
waypoints,
|
||||
pointLocations,
|
||||
summary,
|
||||
trip,
|
||||
time,
|
||||
routeGeoJson,
|
||||
0,
|
||||
centerLocation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun maneuverLocations(): List<Point> {
|
||||
val beginShapeIndex = currentManeuver().beginShapeIndex
|
||||
val endShapeIndex = if (currentManeuver().endShapeIndex >= waypoints.size) {
|
||||
waypoints.size
|
||||
} else {
|
||||
currentManeuver().endShapeIndex + 1
|
||||
}
|
||||
//return pointLocations.subList(beginShapeIndex, endShapeIndex)
|
||||
return pointLocations
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
waypoints = mutableListOf()
|
||||
maneuvers = mutableListOf()
|
||||
routeGeoJson = ""
|
||||
}
|
||||
|
||||
fun currentManeuver() : Maneuvers {
|
||||
fun currentManeuver(): Maneuvers {
|
||||
return maneuvers[currentManeuverIndex]
|
||||
}
|
||||
|
||||
fun nextManeuver() : Maneuvers {
|
||||
return maneuvers[currentManeuverIndex+1]
|
||||
fun nextManeuver(): Maneuvers {
|
||||
val nextIndex = currentManeuverIndex + 1
|
||||
return if (nextIndex < maneuvers.size) {
|
||||
maneuvers[nextIndex]
|
||||
} else {
|
||||
throw IndexOutOfBoundsException("No next maneuver available.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
"id": "background",
|
||||
"type": "background",
|
||||
"layout": {"visibility": "visible"},
|
||||
"paint": {"background-color": "rgba(146, 146, 142, 1)"}
|
||||
"paint": {"background-color": "rgba(28, 28, 35, 1)"}
|
||||
},
|
||||
{
|
||||
"id": "natural_earth",
|
||||
|
||||
@@ -1,139 +1,99 @@
|
||||
package com.kouros.navigation.model
|
||||
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
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.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.geojson.FeatureCollection
|
||||
import org.maplibre.geojson.Point
|
||||
import org.maplibre.turf.TurfMeasurement
|
||||
import org.maplibre.turf.TurfMisc
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
open class RouteModel() {
|
||||
lateinit var centerLocation: Location
|
||||
data class RouteState(
|
||||
val route: Route? = null,
|
||||
val isNavigating: Boolean = false,
|
||||
var destination: Place = Place(),
|
||||
val arrived: Boolean = false,
|
||||
var maneuverType: Int = 0,
|
||||
var currentShapeIndex: Int = 0,
|
||||
var distanceToStepEnd: Float = 0F,
|
||||
var beginIndex: Int = 0,
|
||||
var endIndex: Int = 0
|
||||
)
|
||||
|
||||
lateinit var destination: Place
|
||||
var routeState = RouteState()
|
||||
|
||||
var navigating = false
|
||||
|
||||
var arrived = false
|
||||
|
||||
var maneuverType = 0
|
||||
|
||||
/*
|
||||
current shapeIndex
|
||||
*/
|
||||
var currentShapeIndex = 0
|
||||
|
||||
var distanceToStepEnd = 0F
|
||||
|
||||
var beginIndex = 0
|
||||
|
||||
var endIndex = 0
|
||||
|
||||
lateinit var route: Route
|
||||
|
||||
fun startNavigation(valhallaRoute: String) {
|
||||
route = Route.Builder()
|
||||
.route(valhallaRoute)
|
||||
var route: Route
|
||||
get() = routeState.route!!
|
||||
set(value) {
|
||||
routeState = routeState.copy(route = value)
|
||||
}
|
||||
fun startNavigation(routeString: String) {
|
||||
val newRoute = Route.Builder()
|
||||
.route(routeString)
|
||||
.build()
|
||||
centerLocation = createCenterLocation()
|
||||
navigating = true
|
||||
this.routeState = routeState.copy(
|
||||
route = newRoute,
|
||||
isNavigating = true
|
||||
)
|
||||
}
|
||||
|
||||
fun stopNavigation() {
|
||||
route.clear()
|
||||
navigating = false
|
||||
currentShapeIndex = 0
|
||||
distanceToStepEnd = 0F
|
||||
beginIndex = 0
|
||||
endIndex = 0
|
||||
this.routeState = routeState.copy(
|
||||
route = null,
|
||||
isNavigating = false,
|
||||
// destination = Place(),
|
||||
arrived = false,
|
||||
maneuverType = 0,
|
||||
currentShapeIndex = 0,
|
||||
distanceToStepEnd = 0F,
|
||||
beginIndex = 0,
|
||||
endIndex = 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the geographic center of the route's GeoJSON data.
|
||||
*
|
||||
* @return A [Location] object representing the center point.
|
||||
* @throws IllegalStateException if the calculated center does not have valid Point geometry.
|
||||
*/
|
||||
private fun createCenterLocation(): Location {
|
||||
// 1. Create a FeatureCollection from the raw GeoJSON string.
|
||||
val featureCollection = FeatureCollection.fromJson(route.routeGeoJson)
|
||||
|
||||
// 2. Calculate the center feature of the collection.
|
||||
val centerFeature = TurfMeasurement.center(featureCollection)
|
||||
|
||||
// 3. Safely access and cast the geometry, throwing an informative error if it fails.
|
||||
val centerPoint = centerFeature.geometry() as? Point
|
||||
?: throw IllegalStateException("Center of GeoJSON is not a valid Point.")
|
||||
|
||||
// 4. Create and return the Location object.
|
||||
return location(centerPoint.longitude(), centerPoint.latitude())
|
||||
}
|
||||
|
||||
/**
|
||||
* The remaining distance to the step, rounded to the nearest 10 units.
|
||||
*/
|
||||
val currentDistance: Double
|
||||
get() {
|
||||
// This is a more direct way to round to the nearest multiple of 10.
|
||||
return (leftStepDistance() / 10.0).roundToInt() * 10.0
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun updateLocation(location: Location) {
|
||||
var nearestDistance = 100000.0f
|
||||
for (i in route.currentManeuverIndex..<route.maneuvers.size) {
|
||||
val maneuver = route.maneuvers[i]
|
||||
val beginShapeIndex = maneuver.beginShapeIndex
|
||||
val endShapeIndex = maneuver.endShapeIndex
|
||||
val distance = calculateDistance(beginShapeIndex, endShapeIndex, location)
|
||||
var newShapeIndex = -1
|
||||
// find nearest waypoint and current shape index
|
||||
// start search at last shape index
|
||||
for (i in routeState.currentShapeIndex..<route.waypoints.size) {
|
||||
val waypoint = route.waypoints[i]
|
||||
val distance = location.distanceTo(location(waypoint[0], waypoint[1]))
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance
|
||||
route.currentManeuverIndex = i
|
||||
calculateCurrentShapeIndex(beginShapeIndex, endShapeIndex, location)
|
||||
newShapeIndex = i
|
||||
}
|
||||
}
|
||||
// find maneuver
|
||||
// calculate distance to step end
|
||||
findManeuver(newShapeIndex)
|
||||
}
|
||||
|
||||
/** Calculates the index in a maneuver. */
|
||||
private fun calculateCurrentShapeIndex(
|
||||
beginShapeIndex: Int,
|
||||
endShapeIndex: Int,
|
||||
location: Location
|
||||
) {
|
||||
var nearestLocation = 100000.0f
|
||||
for (i in currentShapeIndex..endShapeIndex) {
|
||||
val waypoint = Location(LocationManager.GPS_PROVIDER)
|
||||
waypoint.longitude = route.waypoints[i][0]
|
||||
waypoint.latitude = route.waypoints[i][1]
|
||||
val distance: Float = location.distanceTo(waypoint)
|
||||
if (distance < nearestLocation) {
|
||||
nearestLocation = distance
|
||||
currentShapeIndex = i
|
||||
beginIndex = beginShapeIndex
|
||||
endIndex = endShapeIndex
|
||||
distanceToStepEnd = 0F
|
||||
val loc1 = Location(LocationManager.GPS_PROVIDER)
|
||||
val loc2 = Location(LocationManager.GPS_PROVIDER)
|
||||
if (i + 1 < route.waypoints.size) {
|
||||
for (j in i + 1..endShapeIndex) {
|
||||
loc1.longitude = route.waypoints[j - 1][0]
|
||||
loc1.latitude = route.waypoints[j - 1][1]
|
||||
loc2.longitude = route.waypoints[j][0]
|
||||
loc2.latitude = route.waypoints[j][1]
|
||||
private fun findManeuver(newShapeIndex: Int) {
|
||||
for (i in route.currentManeuverIndex..<route.maneuvers.size) {
|
||||
val maneuver = route.maneuvers[i]
|
||||
if (maneuver.beginShapeIndex <= newShapeIndex && maneuver.endShapeIndex >= newShapeIndex) {
|
||||
route = route.copy(currentManeuverIndex = i)
|
||||
routeState.apply {
|
||||
currentShapeIndex = newShapeIndex
|
||||
beginIndex = maneuver.beginShapeIndex
|
||||
endIndex = maneuver.endShapeIndex
|
||||
distanceToStepEnd = 0F
|
||||
// calculate shape distance to step end
|
||||
for (j in newShapeIndex + 1..maneuver.endShapeIndex) {
|
||||
val loc1 = location(route!!.waypoints[j - 1][0], route.waypoints[j - 1][1])
|
||||
val loc2 = location(route.waypoints[j][0], route.waypoints[j][1])
|
||||
distanceToStepEnd += loc1.distanceTo(loc2)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,42 +135,6 @@ open class RouteModel() {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fun currentStepx(): StepData {
|
||||
val maneuver = route.currentManeuver()
|
||||
var text = ""
|
||||
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
|
||||
text = maneuver.streetNames[0]
|
||||
}
|
||||
val distanceStepLeft = leftStepDistance()
|
||||
when (distanceStepLeft) {
|
||||
in 0.0..Constants.NEXT_STEP_THRESHOLD -> {
|
||||
if (route.currentManeuverIndex < route.maneuvers.size) {
|
||||
val maneuver = route.nextManeuver()
|
||||
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
|
||||
text = maneuver.streetNames[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val type = if (hasArrived(maneuverType)) {
|
||||
maneuver.type
|
||||
} else {
|
||||
ManeuverType.None.value
|
||||
}
|
||||
var routing: (Pair<Int, Int>) = maneuverIcon(type)
|
||||
when (distanceStepLeft) {
|
||||
in 0.0..NEXT_STEP_THRESHOLD -> {
|
||||
if (route.currentManeuverIndex < route.maneuvers.size) {
|
||||
val maneuver = route.nextManeuver()
|
||||
val maneuverType = maneuver.type
|
||||
routing = maneuverIcon(maneuverType)
|
||||
}
|
||||
}
|
||||
}
|
||||
return StepData(text, distanceStepLeft, routing.first, routing.second, arrivalTime(), travelLeftDistance())
|
||||
}
|
||||
|
||||
fun nextStep(): StepData {
|
||||
val maneuver = route.nextManeuver()
|
||||
val maneuverType = maneuver.type
|
||||
@@ -220,30 +144,24 @@ open class RouteModel() {
|
||||
when (distanceLeft) {
|
||||
in 0.0..NEXT_STEP_THRESHOLD -> {
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (maneuver.streetNames != null && maneuver.streetNames!!.isNotEmpty()) {
|
||||
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
|
||||
text = maneuver.streetNames[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
val routing: (Pair<Int, Int>) = maneuverIcon(maneuverType)
|
||||
return StepData(text, distanceLeft, routing.first, routing.second, arrivalTime(), travelLeftDistance())
|
||||
}
|
||||
|
||||
private fun calculateDistance(
|
||||
beginShapeIndex: Int,
|
||||
endShapeIndex: Int,
|
||||
location: Location
|
||||
): Float {
|
||||
var nearestLocation = 100000.0f
|
||||
for (i in beginShapeIndex..endShapeIndex) {
|
||||
val polylineLocation = location(route.waypoints[i][0], route.waypoints[i][1])
|
||||
val distance: Float = location.distanceTo(polylineLocation)
|
||||
if (distance < nearestLocation) {
|
||||
nearestLocation = distance
|
||||
}
|
||||
}
|
||||
return nearestLocation
|
||||
val routing: (Pair<Int, Int>) = maneuverIcon(maneuverType)
|
||||
// Construct and return the final StepData object
|
||||
return StepData(
|
||||
text,
|
||||
distanceLeft,
|
||||
routing.first,
|
||||
routing.second,
|
||||
arrivalTime(),
|
||||
travelLeftDistance()
|
||||
)
|
||||
}
|
||||
|
||||
fun travelLeftTime(): Double {
|
||||
@@ -252,10 +170,11 @@ open class RouteModel() {
|
||||
val maneuver = route.maneuvers[i]
|
||||
timeLeft += maneuver.time
|
||||
}
|
||||
if (endIndex > 0) {
|
||||
if (routeState.endIndex > 0) {
|
||||
val maneuver = route.currentManeuver()
|
||||
val curTime = maneuver.time
|
||||
val percent = 100 * (endIndex - currentShapeIndex) / (endIndex - beginIndex)
|
||||
val percent =
|
||||
100 * (routeState.endIndex - routeState.currentShapeIndex) / (routeState.endIndex - routeState.beginIndex)
|
||||
val time = curTime * percent / 100
|
||||
timeLeft += time
|
||||
}
|
||||
@@ -275,10 +194,12 @@ open class RouteModel() {
|
||||
fun leftStepDistance(): Double {
|
||||
val maneuver = route.currentManeuver()
|
||||
var leftDistance = maneuver.length
|
||||
if (endIndex > 0) {
|
||||
leftDistance = (distanceToStepEnd / 1000).toDouble()
|
||||
if (routeState.endIndex > 0) {
|
||||
leftDistance = (routeState.distanceToStepEnd / 1000).toDouble()
|
||||
}
|
||||
return leftDistance * 1000
|
||||
// The remaining distance to the step, rounded to the nearest 10 units.
|
||||
return (leftDistance * 1000 / 10.0).roundToInt() * 10.0
|
||||
|
||||
}
|
||||
|
||||
/** Returns the left distance in km. */
|
||||
@@ -288,10 +209,11 @@ open class RouteModel() {
|
||||
val maneuver = route.maneuvers[i]
|
||||
leftDistance += maneuver.length
|
||||
}
|
||||
if (endIndex > 0) {
|
||||
if (routeState.endIndex > 0) {
|
||||
val maneuver = route.currentManeuver()
|
||||
val curDistance = maneuver.length
|
||||
val percent = 100 * (endIndex - currentShapeIndex) / (endIndex - beginIndex)
|
||||
val percent =
|
||||
100 * (routeState.endIndex - routeState.currentShapeIndex) / (routeState.endIndex - routeState.beginIndex)
|
||||
val time = curDistance * percent / 100
|
||||
leftDistance += time
|
||||
}
|
||||
@@ -360,21 +282,21 @@ open class RouteModel() {
|
||||
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
||||
}
|
||||
}
|
||||
maneuverType = type
|
||||
routeState.maneuverType = type
|
||||
return Pair(type, currentTurnIcon)
|
||||
}
|
||||
|
||||
fun isNavigating(): Boolean {
|
||||
return navigating
|
||||
return routeState.isNavigating
|
||||
}
|
||||
|
||||
fun isArrived(): Boolean {
|
||||
return arrived
|
||||
return routeState.arrived
|
||||
}
|
||||
|
||||
fun hasArrived(type: Int): Boolean {
|
||||
return type == ManeuverType.DestinationRight.value
|
||||
|| maneuverType == ManeuverType.Destination.value
|
||||
|| maneuverType == ManeuverType.DestinationLeft.value
|
||||
|| routeState.maneuverType == ManeuverType.Destination.value
|
||||
|| routeState.maneuverType == ManeuverType.DestinationLeft.value
|
||||
}
|
||||
}
|
||||
@@ -241,7 +241,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
savePlace(place)
|
||||
}
|
||||
|
||||
fun savePlace(place: Place) {
|
||||
private fun savePlace(place: Place) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val placeBox = boxStore.boxFor(Place::class)
|
||||
@@ -258,6 +258,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
val current = LocalDateTime.now(ZoneOffset.UTC)
|
||||
place.lastDate = current.atZone(ZoneOffset.UTC).toEpochSecond()
|
||||
placeBox.put(place)
|
||||
println("Save Recent $place")
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
@@ -333,4 +334,21 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
return results.toMutableStateList()
|
||||
}
|
||||
|
||||
fun loadRecentPlace(): SnapshotStateList<Place?> {
|
||||
val results = listOf<Place>()
|
||||
try {
|
||||
val placeBox = boxStore.boxFor(Place::class)
|
||||
val query = placeBox
|
||||
.query(Place_.name.notEqual("").and(Place_.category.equal(Constants.RECENT)))
|
||||
.orderDesc(Place_.lastDate)
|
||||
.build()
|
||||
val results = query.find()
|
||||
query.close()
|
||||
return results.toMutableStateList()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return results.toMutableStateList()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,7 +9,9 @@ import com.kouros.navigation.data.GeoJsonFeature
|
||||
import com.kouros.navigation.data.GeoJsonFeatureCollection
|
||||
import com.kouros.navigation.data.GeoJsonLineString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.maplibre.geojson.FeatureCollection
|
||||
import org.maplibre.geojson.Point
|
||||
import org.maplibre.turf.TurfMeasurement
|
||||
import org.maplibre.turf.TurfMisc
|
||||
import java.lang.Math.toDegrees
|
||||
import java.lang.Math.toRadians
|
||||
@@ -19,13 +21,15 @@ import java.time.ZoneOffset
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.asin
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sin
|
||||
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
|
||||
object NavigationUtils {
|
||||
@@ -52,6 +56,29 @@ object NavigationUtils {
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getIntKeyValue(context: Context, key: String) : Int {
|
||||
return context
|
||||
.getSharedPreferences(
|
||||
SHARED_PREF_KEY,
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
.getInt(key, 0)
|
||||
}
|
||||
|
||||
fun setIntKeyValue(context: Context, `val`: Int, key: String) {
|
||||
context
|
||||
.getSharedPreferences(
|
||||
SHARED_PREF_KEY,
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
.edit {
|
||||
putInt(
|
||||
key, `val`
|
||||
)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
fun snapLocation(location: Location, stepCoordinates: List<Point>) : Location {
|
||||
val newLocation = Location(location)
|
||||
val oldPoint = Point.fromLngLat(location.longitude, location.latitude)
|
||||
@@ -101,6 +128,26 @@ object NavigationUtils {
|
||||
return coordinates
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the geographic center of the route's GeoJSON data.
|
||||
*
|
||||
* @return A [Location] object representing the center point.
|
||||
* @throws IllegalStateException if the calculated center does not have valid Point geometry.
|
||||
*/
|
||||
fun createCenterLocation(routeGeoJson: String): Location {
|
||||
// 1. Create a FeatureCollection from the raw GeoJSON string.
|
||||
val featureCollection = FeatureCollection.fromJson(routeGeoJson)
|
||||
|
||||
// 2. Calculate the center feature of the collection.
|
||||
val centerFeature = TurfMeasurement.center(featureCollection)
|
||||
|
||||
// 3. Safely access and cast the geometry, throwing an informative error if it fails.
|
||||
val centerPoint = centerFeature.geometry() as? Point
|
||||
?: throw IllegalStateException("Center of GeoJSON is not a valid Point.")
|
||||
|
||||
// 4. Create and return the Location object.
|
||||
return location(centerPoint.longitude(), centerPoint.latitude())
|
||||
}
|
||||
fun createGeoJson(lineCoordinates: List<List<Double>>): String {
|
||||
|
||||
val lineString = GeoJsonLineString(type = "LineString", coordinates = lineCoordinates)
|
||||
@@ -210,4 +257,16 @@ fun formatDateTime(time: Long): String {
|
||||
fun Double.round(numFractionDigits: Int): Double {
|
||||
val factor = 10.0.pow(numFractionDigits.toDouble())
|
||||
return (this * factor).roundToInt() / factor
|
||||
}
|
||||
|
||||
fun duration(preview: Boolean, bearing: Double, lastBearing: Double): Duration {
|
||||
if (preview) {
|
||||
return 3.seconds
|
||||
}
|
||||
val cameraDuration = if ((lastBearing - bearing).absoluteValue > 20.0) {
|
||||
2.seconds
|
||||
} else {
|
||||
1.seconds
|
||||
}
|
||||
return cameraDuration
|
||||
}
|
||||
Reference in New Issue
Block a user