Serialize Json

This commit is contained in:
Dimitris
2025-11-20 10:27:33 +01:00
parent 3f3bdeb96d
commit 33f5ef4f34
24 changed files with 919 additions and 391 deletions

View File

@@ -57,9 +57,8 @@ data class StepData (
var bearing: Double
)
//val places = mutableListOf<Place>()
/* Place(
val dataPlaces = listOf(
Place(
id = 0,
name = "Vogelhartstr. 17",
category = "Favorites",
@@ -80,7 +79,7 @@ data class StepData (
city = "München",
street = "Hohenwaldeckstr. 27",
)
) */
)
// GeoJSON data classes
@Serializable
@@ -106,7 +105,6 @@ data class Locations (
var lat : Double,
var lon : Double,
var street : String = ""
)
@Serializable
@@ -120,6 +118,8 @@ data class ValhallaLocation (
object Constants {
const val STYLE: String = "https://kouros-online.de/liberty2"
//baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"),
const val TAG: String = "Navigation"
const val CONTACTS: String = "Contacts"

View File

@@ -52,7 +52,7 @@ class NavigationRepository {
val route = getRoute(currentLocation, location)
val routeModel = RouteModel()
routeModel.startNavigation(route)
return routeModel.routeDistance
return routeModel.route.distance
}
fun getPlaces(): List<Place> {
@@ -87,7 +87,6 @@ class NavigationRepository {
)
}
})
println(url)
val httpURLConnection = URL(url).openConnection() as HttpURLConnection
httpURLConnection.setRequestProperty(

View File

@@ -0,0 +1,115 @@
package com.kouros.navigation.data
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.createGeoJson
import com.kouros.navigation.utils.NavigationUtils.decodePolyline
import org.maplibre.geojson.Point
data class Route (
/**
* A Leg is a route between only two waypoints.
*
* @since 1.0.0
*/
val maneuvers: List<Maneuvers>,
/**
* The distance traveled from origin to destination.
*
* @return a double number with unit meters
* @since 1.0.0
*/
val distance: Double,
/**
* List of [List<Double>] objects. Each `waypoint` 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
*/
var waypoints: List<List<Double>>,
val pointLocations : List<Point>,
val summary: Summary,
val trip: Trip,
val time: Double,
var routingManeuvers : List<Maneuvers>,
var routeGeoJson : String,
var currentIndex: Int
) {
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 routingManeuvers: List<Maneuvers>
private var routeGeoJson = ""
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
}
}
fun build(): Route {
maneuvers = trip.legs[0].maneuvers
summary = trip.summary
distance = summary.length
time = summary.time
waypoints = decodePolyline(trip.legs[0].shape)
val points = mutableListOf<Point>()
for (loc in waypoints) {
val point = Point.fromLngLat(loc[0], loc[1])
points.add(point)
}
pointLocations = points
val routings = mutableListOf<Maneuvers>()
for (maneuver in maneuvers) {
routings.add(maneuver)
}
this.routingManeuvers = routings
this.routeGeoJson = createGeoJson(waypoints)
return Route(
maneuvers, distance, waypoints, pointLocations, summary, trip, time, routingManeuvers, routeGeoJson, 0
)
}
}
fun clear() {
waypoints = mutableListOf()
routingManeuvers = mutableListOf()
routeGeoJson = ""
}
fun currentManeuver() : Maneuvers {
return maneuvers[currentIndex]
}
fun nextManeuver() : Maneuvers {
return maneuvers[currentIndex+1]
}
}

View File

@@ -0,0 +1,17 @@
package com.kouros.navigation.data.valhalla
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
@OptIn(ExperimentalSerializationApi::class)
@JsonIgnoreUnknownKeys
data class Legs (
@SerializedName("maneuvers" ) var maneuvers : ArrayList<Maneuvers> = arrayListOf(),
@SerializedName("summary" ) var summary : Summary = Summary(),
@SerializedName("shape" ) var shape : String = ""
)

View File

@@ -0,0 +1,19 @@
package com.kouros.navigation.data.valhalla
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
@OptIn(ExperimentalSerializationApi::class)
@JsonIgnoreUnknownKeys
data class Locations (
@SerializedName("type" ) var type : String = "",
@SerializedName("lat" ) var lat : Double = 0.0,
@SerializedName("lon" ) var lon : Double = 0.0,
@SerializedName("side_of_street" ) var sideOfStreet : String = "",
@SerializedName("original_index" ) var originalIndex : Int = 0
)

View File

@@ -0,0 +1,29 @@
package com.kouros.navigation.data.valhalla
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
@OptIn(ExperimentalSerializationApi::class)
@JsonIgnoreUnknownKeys
data class Maneuvers(
@SerializedName("begin_shape_index") var beginShapeIndex: Int,
@SerializedName("end_shape_index") var endShapeIndex: Int,
@SerializedName("type") var type: Int = 0,
@SerializedName("instruction") var instruction: String = "",
@SerializedName("verbal_succinct_transition_instruction") var verbalSuccinctTransitionInstruction: String = "",
@SerializedName("verbal_pre_transition_instruction") var verbalPreTransitionInstruction: String = "",
@SerializedName("verbal_post_transition_instruction") var verbalPostTransitionInstruction: String = "",
@SerializedName("street_names") val streetNames: List<String>? = arrayListOf(),
@SerializedName("bearing_after") var bearingAfter: Int = 0,
@SerializedName("time") var time: Double = 0.0,
@SerializedName("length") var length: Double = 0.0,
@SerializedName("cost") var cost: Double = 0.0,
@SerializedName("verbal_multi_cue") var verbalMultiCue: Boolean = false,
@SerializedName("travel_mode") var travelMode: String = "",
@SerializedName("travel_type") var travelType: String = "",
)

View File

@@ -0,0 +1,25 @@
package com.kouros.navigation.data.valhalla
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
@OptIn(ExperimentalSerializationApi::class)
@JsonIgnoreUnknownKeys
data class Summary (
@SerializedName("has_time_restrictions" ) var hasTimeRestrictions : Boolean = false,
@SerializedName("has_toll" ) var hasToll : Boolean = false,
@SerializedName("has_highway" ) var hasHighway : Boolean = false,
@SerializedName("has_ferry" ) var hasFerry : Boolean = false,
@SerializedName("min_lat" ) var minLat : Double = 0.0,
@SerializedName("min_lon" ) var minLon : Double = 0.0,
@SerializedName("max_lat" ) var maxLat : Double = 0.0,
@SerializedName("max_lon" ) var maxLon : Double = 0.0,
@SerializedName("time" ) var time : Double = 0.0,
@SerializedName("length" ) var length : Double = 0.0,
@SerializedName("cost" ) var cost : Double = 0.0
)

View File

@@ -0,0 +1,21 @@
package com.kouros.navigation.data.valhalla
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
@OptIn(ExperimentalSerializationApi::class)
@JsonIgnoreUnknownKeys
data class Trip (
@SerializedName("locations" ) var locations : ArrayList<Locations> = arrayListOf(),
@SerializedName("legs" ) var legs : ArrayList<Legs> = arrayListOf(),
@SerializedName("summary" ) var summary : Summary = Summary(),
@SerializedName("status_message" ) var statusMessage : String = "",
@SerializedName("status" ) var status : Int = 0,
@SerializedName("units" ) var units : String = "",
@SerializedName("language" ) var language : String = "",
)

View File

@@ -0,0 +1,16 @@
package com.kouros.navigation.data.valhalla
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
@OptIn(ExperimentalSerializationApi::class)
@JsonIgnoreUnknownKeys
data class ValhallaJson (
@SerializedName("trip" ) var trip : Trip = Trip(),
@SerializedName("id" ) var id : String = ""
)

View File

@@ -2,32 +2,29 @@ package com.kouros.navigation.model
import android.location.Location
import android.location.LocationManager
import com.kouros.navigation.data.Constants.homeLocation
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.Route
import com.kouros.navigation.data.StepData
import com.kouros.navigation.utils.NavigationUtils
import com.kouros.navigation.utils.NavigationUtils.Utils.createGeoJson
import com.kouros.navigation.utils.NavigationUtils.Utils.decodePolyline
import org.json.JSONArray
import org.json.JSONObject
import com.kouros.navigation.utils.location
import org.maplibre.geojson.Point
import kotlin.math.absoluteValue
import kotlin.math.roundToInt
open class RouteModel () {
var polylineLocations: List<List<Double>> = emptyList()
lateinit var maneuvers: JSONArray
lateinit var locations: JSONArray
private lateinit var summary: JSONObject
var routeDistance = 0.0
var routeTime = 0.0
open class RouteModel() {
lateinit var centerLocation: Location
lateinit var destination: Place
var navigating = false
var arrived = false
var maneuverIndex = 0
var maneuverType = 0
/*
Index in a maneuver
*/
var currentIndex = 0
var distanceToStepEnd = 0F
@@ -38,53 +35,27 @@ open class RouteModel () {
var endIndex = 0
var routingManeuvers = mutableListOf<JSONObject>()
var route = ""
data class Builder(
var route: String? = null,
var fromLocation: Location? = null,
var toLocation: Location? = null) {
fun route(route: String) = apply { this.route = route }
fun fromLocation(fromLocation: Location) = apply { this.fromLocation = fromLocation }
fun toLocation(toLocation: Location) = apply { this.toLocation = toLocation }
//fun build() = RouteModel(route!!, fromLocation!!, toLocation!!)
}
private fun decodeValhallaRoute(valhallaRoute: String) {
if (valhallaRoute.isEmpty() || valhallaRoute == "[]") {
return;
}
val jObject = JSONObject(valhallaRoute)
val trip = jObject.getJSONObject("trip")
locations = trip.getJSONArray("locations")
val legs = trip.getJSONArray("legs")
summary = trip.getJSONObject("summary")
routeTime = summary.getDouble("time")
routeDistance = summary.getDouble("length")
centerLocation = createCenterLocation()
maneuvers = legs.getJSONObject(0).getJSONArray("maneuvers")
val shape = legs.getJSONObject(0).getString("shape")
polylineLocations = decodePolyline(shape)
}
private fun createCenterLocation() : Location {
val latitude = summary.getDouble("max_lat") - (summary.getDouble("max_lat") - summary.getDouble("min_lat"))
val longitude = summary.getDouble("max_lon") - (summary.getDouble("max_lon") - summary.getDouble("min_lon"))
return NavigationUtils().location(latitude, longitude)
}
lateinit var route: Route
fun startNavigation(valhallaRoute: String) {
decodeValhallaRoute(valhallaRoute)
for (i in 0..<maneuvers.length()) {
val maneuver = (maneuvers[i] as JSONObject)
routingManeuvers.add(maneuver)
}
route = createGeoJson(polylineLocations)
route = Route.Builder()
.route(valhallaRoute)
.build()
centerLocation = createCenterLocation()
navigating = true
}
private fun createCenterLocation(): Location {
if (route.summary.maxLat == 0.0) {
return location(homeLocation.latitude, homeLocation.longitude)
}
val latitude =
route.summary.maxLat - (route.summary.maxLat - route.summary.minLat)
val longitude =
route.summary.maxLon - (route.summary.maxLon - route.summary.minLon)
return location(latitude, longitude)
}
val currentDistance: Double
/** Returns the current [Step] with information such as the cue text and images. */
get() {
@@ -93,45 +64,42 @@ open class RouteModel () {
fun updateLocation(location: Location) {
var nearestDistance = 100000.0f
maneuverIndex = -1
route.currentIndex = -1
// find maneuver
for (i in 0..<maneuvers.length()) {
val maneuver = (maneuvers[i] as JSONObject)
val beginShapeIndex = maneuver.getString("begin_shape_index").toInt()
val endShapeIndex = maneuver.getString("end_shape_index").toInt()
for ((i, maneuver) in route.maneuvers.withIndex()) {
val beginShapeIndex = maneuver.beginShapeIndex
val endShapeIndex = maneuver.endShapeIndex
val distance = calculateDistance(beginShapeIndex, endShapeIndex, location)
if (distance < nearestDistance) {
nearestDistance = distance
maneuverIndex = i
route.currentIndex = i
calculateCurrentIndex(beginShapeIndex, endShapeIndex, location)
}
}
}
fun currentStep(): StepData {
val maneuver = (maneuvers[maneuverIndex] as JSONObject)
val maneuver = route.currentManeuver()
var text = ""
if (maneuver.optJSONArray("street_names") != null) {
text = maneuver.getJSONArray("street_names").get(0) as String
println("Maneuver $maneuver")
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
text = maneuver.streetNames[0]
}
if (bearing == 0F) {
if (maneuver.has("bearing_after")) {
bearing = maneuver.getInt("bearing_after").toFloat()
}
bearing = maneuver.bearingAfter.toFloat()
}
val distanceStepLeft = leftStepDistance() * 1000
when (distanceStepLeft) {
in 0.0..100.0 -> {
if (maneuverIndex < maneuvers.length()) {
val maneuver = (maneuvers[maneuverIndex + 1] as JSONObject)
if (maneuver.optJSONArray("street_names") != null) {
text = maneuver.getJSONArray("street_names").get(0) as String
if (route.currentIndex < route.maneuvers.size) {
val maneuver = route.nextManeuver()
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
text = maneuver.streetNames[0]
}
}
}
}
return StepData(text, distanceStepLeft, bearing.toDouble())
}
/** Calculates the index in a maneuver. */
@@ -143,8 +111,8 @@ open class RouteModel () {
var nearestLocation = 100000.0f
for (i in beginShapeIndex..endShapeIndex) {
val polylineLocation = Location(LocationManager.GPS_PROVIDER)
polylineLocation.longitude = polylineLocations[i][0]
polylineLocation.latitude = polylineLocations[i][1]
polylineLocation.longitude = route.waypoints[i][0]
polylineLocation.latitude = route.waypoints[i][1]
val distance: Float = location.distanceTo(polylineLocation)
if (distance < nearestLocation) {
nearestLocation = distance
@@ -154,16 +122,16 @@ open class RouteModel () {
distanceToStepEnd = 0F
val loc1 = Location(LocationManager.GPS_PROVIDER)
val loc2 = Location(LocationManager.GPS_PROVIDER)
loc1.longitude = polylineLocations[i][0]
loc1.latitude = polylineLocations[i][1]
loc2.longitude = polylineLocations[i+1][0]
loc2.latitude = polylineLocations[i+1][1]
loc1.longitude = route.waypoints[i][0]
loc1.latitude = route.waypoints[i][1]
loc2.longitude = route.waypoints[i + 1][0]
loc2.latitude = route.waypoints[i + 1][1]
bearing = loc1.bearingTo(loc2).absoluteValue
for (j in i + 1..endShapeIndex) {
loc1.longitude = polylineLocations[j - 1][0]
loc1.latitude = polylineLocations[j - 1][1]
loc2.longitude = polylineLocations[j][0]
loc2.latitude = polylineLocations[j][1]
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]
distanceToStepEnd += loc1.distanceTo(loc2)
}
}
@@ -178,8 +146,8 @@ open class RouteModel () {
var nearestLocation = 100000.0f
for (i in beginShapeIndex..endShapeIndex) {
val polylineLocation = Location(LocationManager.GPS_PROVIDER)
polylineLocation.longitude = polylineLocations[i][0]
polylineLocation.latitude = polylineLocations[i][1]
polylineLocation.longitude = route.waypoints[i][0]
polylineLocation.latitude = route.waypoints[i][1]
val distance: Float = location.distanceTo(polylineLocation)
if (distance < nearestLocation) {
nearestLocation = distance
@@ -188,15 +156,21 @@ open class RouteModel () {
return nearestLocation
}
fun maneuverLocations(): List<Point> {
val beginShapeIndex = route.currentManeuver().beginShapeIndex
val endShapeIndex = route.currentManeuver().endShapeIndex
return route.pointLocations.subList(beginShapeIndex, endShapeIndex)
}
fun travelLeftTime(): Double {
var timeLeft = 0.0
for (i in maneuverIndex + 1..<routingManeuvers.size) {
val maneuver = routingManeuvers[i]
timeLeft += maneuver.getDouble("time")
for (i in route.currentIndex + 1..<route.routingManeuvers.size) {
val maneuver = route.routingManeuvers[i]
timeLeft += maneuver.time
}
if (endIndex > 0) {
val maneuver = routingManeuvers[maneuverIndex]
val curTime = maneuver.getDouble("time")
val maneuver = route.currentManeuver()
val curTime = maneuver.time
val percent = 100 * (endIndex - currentIndex) / (endIndex - beginIndex)
val time = curTime * percent / 100
timeLeft += time
@@ -206,8 +180,8 @@ open class RouteModel () {
/** Returns the current [Step] left distance in km. */
fun leftStepDistance(): Double {
val maneuver = routingManeuvers[maneuverIndex]
var leftDistance = maneuver.getDouble("length")
val maneuver = route.routingManeuvers[route.currentIndex]
var leftDistance = maneuver.length
if (endIndex > 0) {
leftDistance = (distanceToStepEnd / 1000).toDouble()
}
@@ -216,13 +190,13 @@ open class RouteModel () {
fun travelLeftDistance(): Double {
var leftDistance = 0.0
for (i in maneuverIndex + 1..<routingManeuvers.size) {
val maneuver = routingManeuvers[i]
leftDistance += maneuver.getDouble("length")
for (i in route.currentIndex + 1..<route.routingManeuvers.size) {
val maneuver = route.routingManeuvers[i]
leftDistance += maneuver.length
}
if (endIndex > 0) {
val maneuver = routingManeuvers[maneuverIndex]
val curDistance = maneuver.getDouble("length")
val maneuver = route.routingManeuvers[route.currentIndex]
val curDistance = maneuver.length
val percent = 100 * (endIndex - currentIndex) / (endIndex - beginIndex)
val time = curDistance * percent / 100
leftDistance += time
@@ -239,11 +213,9 @@ open class RouteModel () {
}
fun stopNavigation() {
route.clear()
navigating = false
polylineLocations = mutableListOf()
routingManeuvers = mutableListOf()
route = ""
maneuverIndex = 0
//maneuverIndex = 0
currentIndex = 0
distanceToStepEnd = 0F
beginIndex = 0

View File

@@ -13,6 +13,7 @@ import com.kouros.navigation.data.ObjectBox.boxStore
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.Place_
import com.kouros.navigation.utils.NavigationUtils
import com.kouros.navigation.utils.location
import io.objectbox.kotlin.boxFor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -43,7 +44,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
val placeBox = boxStore.boxFor(Place::class)
pl.addAll(placeBox.all)
for (place in pl) {
val plLocation = NavigationUtils().location(place.latitude, place.longitude)
val plLocation = location(place.latitude, place.longitude)
val distance = repository.getRouteDistance(location, plLocation)
place.distance = distance.toFloat()
}
@@ -87,7 +88,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
address.address, 5) {
for (adr in it) {
if (addressLines.size > 1) {
val plLocation = NavigationUtils().location(adr.latitude, adr.longitude)
val plLocation = location(adr.latitude, adr.longitude)
val distance = repository.getRouteDistance(currentLocation, plLocation)
contactList.add(
Place(

View File

@@ -6,6 +6,13 @@ 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.Point
import org.maplibre.turf.TurfClassification
import org.maplibre.turf.TurfConversion
import org.maplibre.turf.TurfJoins
import org.maplibre.turf.TurfMeta
import org.maplibre.turf.TurfMisc
import org.maplibre.turf.TurfTransformation
import java.lang.Math.toDegrees
import java.lang.Math.toRadians
import kotlin.math.asin
@@ -14,118 +21,130 @@ import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin
class NavigationUtils() {
object Utils {
fun decodePolyline(encoded: String, vararg precisionOptional: Int): List<List<Double>> {
val precision = if (precisionOptional.isNotEmpty()) precisionOptional[0] else 6
val factor = 10.0.pow(precision)
var lat = 0
var lng = 0
val coordinates = mutableListOf<List<Double>>()
var index = 0
while (index < encoded.length) {
var byte = 0x20
var shift = 0
var result = 0
while (byte >= 0x20) {
byte = encoded[index].code - 63
result = result or ((byte and 0x1f) shl shift)
shift += 5
index++
}
lat += if ((result and 1) > 0) (result shr 1).inv() else (result shr 1)
object NavigationUtils {
byte = 0x20
shift = 0
result = 0
while (byte >= 0x20) {
byte = encoded[index].code - 63
result = result or ((byte and 0x1f) shl shift)
shift += 5
index++
}
lng += if ((result and 1) > 0) (result shr 1).inv() else (result shr 1)
coordinates.add(listOf(lng.toDouble() / factor, lat.toDouble() / factor))
}
return coordinates
fun snapLocation(location: Location, stepCoordinates: List<Point>): Location {
val oldPoint = Point.fromLngLat(location.longitude, location.latitude)
if (stepCoordinates.size > 1) {
val pointFeature = TurfMisc.nearestPointOnLine(oldPoint, stepCoordinates)
val point = pointFeature.geometry() as Point
location.latitude = point.latitude()
location.longitude = point.longitude()
}
fun createGeoJson(lineCoordinates: List<List<Double>>): String {
val lineString = GeoJsonLineString(type = "LineString", coordinates = lineCoordinates)
val feature = GeoJsonFeature(type = "Feature", geometry = lineString)
val featureCollection =
GeoJsonFeatureCollection(type = "FeatureCollection", features = listOf(feature))
val jsonString = Json.Default.encodeToString(featureCollection)
return jsonString
}
fun getBoundingBox(
lat: Double,
lon: Double,
radius: Double
): Map<String, Map<String, Double>> {
val earthRadius = 6371.0
val maxLat = lat + Math.toDegrees(radius / earthRadius)
val minLat = lat - Math.toDegrees(radius / earthRadius)
val maxLon = lon + Math.toDegrees(radius / earthRadius / cos(Math.toRadians(lat)))
val minLon = lon - Math.toDegrees(radius / earthRadius / cos(Math.toRadians(lat)))
return mapOf(
"nw" to mapOf("lat" to maxLat, "lon" to minLon),
"ne" to mapOf("lat" to maxLat, "lon" to maxLon),
"sw" to mapOf("lat" to minLat, "lon" to minLon),
"se" to mapOf("lat" to minLat, "lon" to maxLon)
)
}
fun computeOffset(from: Location, distance: Double, heading: Double): Location {
val earthRadius = 6371009.0
var distance = distance
var heading = heading
distance /= earthRadius
heading = toRadians(heading)
val fromLat: Double = toRadians(from.latitude)
val fromLng: Double = toRadians(from.longitude)
val cosDistance: Double = cos(distance)
val sinDistance = sin(distance)
val sinFromLat = sin(fromLat)
val cosFromLat: Double = cos(fromLat)
val sinLat: Double = cosDistance * sinFromLat + sinDistance * cosFromLat * cos(heading)
val dLng: Double = atan2(
sinDistance * cosFromLat * sin(heading),
cosDistance - sinFromLat * sinLat
)
val snap = Location(LocationManager.GPS_PROVIDER)
snap.latitude = toDegrees(asin(sinLat))
snap.longitude = toDegrees(fromLng + dLng)
return snap
//return LatLng(toDegrees(asin(sinLat)), toDegrees(fromLng + dLng))
}
}
fun calculateZoom(speed: Double?): Double {
if (speed == null) {
return 18.0
}
val zoom = when (speed.toInt()) {
in 0..10 -> 17.0
in 11..20 -> 17.0
in 21..30 -> 17.0
in 31..40 -> 16.0
in 41..50 -> 15.0
in 51..60 -> 14.0
else -> 11
}
return zoom.toDouble()
}
fun location(latitude: Double, longitude: Double): Location {
val location = Location(LocationManager.GPS_PROVIDER)
location.longitude = longitude
location.latitude = latitude
return location
}
fun decodePolyline(encoded: String, vararg precisionOptional: Int): List<List<Double>> {
val precision = if (precisionOptional.isNotEmpty()) precisionOptional[0] else 6
val factor = 10.0.pow(precision)
var lat = 0
var lng = 0
val coordinates = mutableListOf<List<Double>>()
var index = 0
while (index < encoded.length) {
var byte = 0x20
var shift = 0
var result = 0
while (byte >= 0x20) {
byte = encoded[index].code - 63
result = result or ((byte and 0x1f) shl shift)
shift += 5
index++
}
lat += if ((result and 1) > 0) (result shr 1).inv() else (result shr 1)
byte = 0x20
shift = 0
result = 0
while (byte >= 0x20) {
byte = encoded[index].code - 63
result = result or ((byte and 0x1f) shl shift)
shift += 5
index++
}
lng += if ((result and 1) > 0) (result shr 1).inv() else (result shr 1)
coordinates.add(listOf(lng.toDouble() / factor, lat.toDouble() / factor))
}
return coordinates
}
fun createGeoJson(lineCoordinates: List<List<Double>>): String {
val lineString = GeoJsonLineString(type = "LineString", coordinates = lineCoordinates)
val feature = GeoJsonFeature(type = "Feature", geometry = lineString)
val featureCollection =
GeoJsonFeatureCollection(type = "FeatureCollection", features = listOf(feature))
val jsonString = Json.encodeToString(featureCollection)
return jsonString
}
fun getBoundingBox(
lat: Double,
lon: Double,
radius: Double
): Map<String, Map<String, Double>> {
val earthRadius = 6371.0
val maxLat = lat + Math.toDegrees(radius / earthRadius)
val minLat = lat - Math.toDegrees(radius / earthRadius)
val maxLon = lon + Math.toDegrees(radius / earthRadius / cos(Math.toRadians(lat)))
val minLon = lon - Math.toDegrees(radius / earthRadius / cos(Math.toRadians(lat)))
return mapOf(
"nw" to mapOf("lat" to maxLat, "lon" to minLon),
"ne" to mapOf("lat" to maxLat, "lon" to maxLon),
"sw" to mapOf("lat" to minLat, "lon" to minLon),
"se" to mapOf("lat" to minLat, "lon" to maxLon)
)
}
fun computeOffset(from: Location, distance: Double, heading: Double): Location {
val earthRadius = 6371009.0
var distance = distance
var heading = heading
distance /= earthRadius
heading = toRadians(heading)
val fromLat: Double = toRadians(from.latitude)
val fromLng: Double = toRadians(from.longitude)
val cosDistance: Double = cos(distance)
val sinDistance = sin(distance)
val sinFromLat = sin(fromLat)
val cosFromLat: Double = cos(fromLat)
val sinLat: Double = cosDistance * sinFromLat + sinDistance * cosFromLat * cos(heading)
val dLng: Double = atan2(
sinDistance * cosFromLat * sin(heading),
cosDistance - sinFromLat * sinLat
)
val snap = Location(LocationManager.GPS_PROVIDER)
snap.latitude = toDegrees(asin(sinLat))
snap.longitude = toDegrees(fromLng + dLng)
return snap
//return LatLng(toDegrees(asin(sinLat)), toDegrees(fromLng + dLng))
}
}
fun calculateZoom(speed: Double?): Double {
if (speed == null) {
return 18.0
}
val zoom = when (speed.toInt()) {
in 0..10 -> 17.0
in 11..20 -> 17.0
in 21..30 -> 17.0
in 31..40 -> 16.0
in 41..50 -> 15.0
in 51..60 -> 14.0
else -> 11
}
return zoom.toDouble()
}
fun location(latitude: Double, longitude: Double): Location {
val location = Location(LocationManager.GPS_PROVIDER)
location.longitude = longitude
location.latitude = latitude
return location
}