Amenities GeoUtils
This commit is contained in:
@@ -67,26 +67,6 @@ data class StepData (
|
||||
)
|
||||
|
||||
|
||||
// GeoJSON data classes
|
||||
|
||||
@Serializable
|
||||
data class GeoJsonType(
|
||||
val type: String,
|
||||
val coordinates: List<List<Double>>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GeoJsonFeature(
|
||||
val type: String,
|
||||
val geometry: GeoJsonType,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GeoJsonFeatureCollection(
|
||||
val type: String,
|
||||
val features: List<GeoJsonFeature>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Locations (
|
||||
var lat : Double,
|
||||
|
||||
@@ -6,14 +6,11 @@ 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.GeoUtils.createCenterLocation
|
||||
import com.kouros.navigation.utils.GeoUtils.createLineStringCollection
|
||||
import com.kouros.navigation.utils.GeoUtils.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(
|
||||
@@ -95,7 +92,7 @@ data class Route(
|
||||
points.add(point)
|
||||
}
|
||||
pointLocations = points
|
||||
routeGeoJson = createGeoJson("LineString", waypoints)
|
||||
routeGeoJson = createLineStringCollection( waypoints)
|
||||
centerLocation = createCenterLocation(routeGeoJson)
|
||||
return Route(
|
||||
maneuvers,
|
||||
|
||||
@@ -2,6 +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 java.io.OutputStreamWriter
|
||||
import java.net.HttpURLConnection
|
||||
@@ -11,8 +13,8 @@ class Overpass {
|
||||
|
||||
val overpassUrl = "https://overpass.kumi.systems/api/interpreter"
|
||||
fun getAmenities(category: String, location: Location) : List<Elements> {
|
||||
val boundingBox = NavigationUtils.getOverpassBbox(location, 2.0)
|
||||
val bb = NavigationUtils.getBoundingBox2(location, 2.0)
|
||||
val boundingBox = getOverpassBbox(location, 2.0)
|
||||
val bb = getBoundingBox2(location, 2.0)
|
||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||
httpURLConnection.requestMethod = "POST"
|
||||
httpURLConnection.setRequestProperty(
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.kouros.navigation.utils
|
||||
|
||||
import android.location.Location
|
||||
import com.kouros.navigation.data.BoundingBox
|
||||
import org.maplibre.geojson.FeatureCollection
|
||||
import org.maplibre.geojson.Point
|
||||
import org.maplibre.spatialk.geojson.Feature
|
||||
import org.maplibre.spatialk.geojson.dsl.addFeature
|
||||
import org.maplibre.spatialk.geojson.dsl.buildFeatureCollection
|
||||
import org.maplibre.spatialk.geojson.dsl.buildLineString
|
||||
import org.maplibre.spatialk.geojson.toJson
|
||||
import org.maplibre.turf.TurfMeasurement
|
||||
import org.maplibre.turf.TurfMisc
|
||||
import java.lang.Math.toDegrees
|
||||
import java.lang.Math.toRadians
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.pow
|
||||
|
||||
object GeoUtils {
|
||||
|
||||
fun snapLocation(location: Location, stepCoordinates: List<Point>) : Location {
|
||||
val newLocation = Location(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
|
||||
newLocation.latitude = point.latitude()
|
||||
newLocation.longitude = point.longitude()
|
||||
}
|
||||
return newLocation
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 createLineStringCollection(lineCoordinates: List<List<Double>>): String {
|
||||
val lineString = buildLineString {
|
||||
lineCoordinates.forEach {
|
||||
add(org.maplibre.spatialk.geojson.Point(
|
||||
it[0],
|
||||
it[1]
|
||||
))
|
||||
}
|
||||
}
|
||||
val feature = Feature(lineString, null)
|
||||
val featureCollection = org.maplibre.spatialk.geojson.FeatureCollection(feature)
|
||||
return featureCollection.toJson()
|
||||
}
|
||||
|
||||
fun createPointCollection(lineCoordinates: List<List<Double>>): String {
|
||||
val featureCollection = buildFeatureCollection {
|
||||
lineCoordinates.forEach {
|
||||
addFeature {
|
||||
geometry = org.maplibre.spatialk.geojson.Point(it[0], it[1])
|
||||
properties = null
|
||||
}
|
||||
}
|
||||
}
|
||||
return featureCollection.toJson()
|
||||
}
|
||||
|
||||
fun getOverpassBbox(location: Location, radius: Double): String {
|
||||
|
||||
val bbox = getBoundingBox(location.longitude, location.latitude, radius)
|
||||
val neLon = bbox["ne"]?.get("lon")
|
||||
val neLat = bbox["ne"]?.get("lat")
|
||||
val swLon = bbox["sw"]?.get("lon")
|
||||
val swLat = bbox["sw"]?.get("lat")
|
||||
return "$swLon,$swLat,$neLon,$neLat"
|
||||
}
|
||||
|
||||
fun getBoundingBox2(location: Location, radius: Double): BoundingBox {
|
||||
val bbox = getBoundingBox(location.longitude, location.latitude, radius)
|
||||
val neLon = bbox["ne"]?.get("lon")
|
||||
val neLat = bbox["ne"]?.get("lat")
|
||||
val swLon = bbox["sw"]?.get("lon")
|
||||
val swLat = bbox["sw"]?.get("lat")
|
||||
return BoundingBox(swLat ?: 0.0 , swLon ?: 0.0, neLat ?: 0.0, neLon ?: 0.0)
|
||||
}
|
||||
fun getBoundingBox(
|
||||
lat: Double,
|
||||
lon: Double,
|
||||
radius: Double
|
||||
): Map<String, Map<String, Double>> {
|
||||
val earthRadius = 6371.0
|
||||
val maxLat = lat + toDegrees(radius / earthRadius)
|
||||
val minLat = lat - toDegrees(radius / earthRadius)
|
||||
val maxLon = lon + toDegrees(radius / earthRadius / cos(toRadians(lat)))
|
||||
val minLon = lon - toDegrees(radius / earthRadius / cos(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)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,13 @@ import android.location.LocationManager
|
||||
import androidx.core.content.edit
|
||||
import com.kouros.navigation.data.BoundingBox
|
||||
import com.kouros.navigation.data.Constants.SHARED_PREF_KEY
|
||||
import com.kouros.navigation.data.GeoJsonFeature
|
||||
import com.kouros.navigation.data.GeoJsonFeatureCollection
|
||||
import com.kouros.navigation.data.GeoJsonType
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.maplibre.geojson.FeatureCollection
|
||||
import org.maplibre.geojson.Point
|
||||
import org.maplibre.spatialk.geojson.Feature
|
||||
import org.maplibre.spatialk.geojson.dsl.addFeature
|
||||
import org.maplibre.spatialk.geojson.dsl.buildFeatureCollection
|
||||
import org.maplibre.spatialk.geojson.dsl.buildLineString
|
||||
import org.maplibre.spatialk.geojson.toJson
|
||||
import org.maplibre.turf.TurfMeasurement
|
||||
import org.maplibre.turf.TurfMisc
|
||||
import java.lang.Math.toDegrees
|
||||
@@ -77,121 +78,6 @@ object NavigationUtils {
|
||||
apply()
|
||||
}
|
||||
}
|
||||
fun snapLocation(location: Location, stepCoordinates: List<Point>) : Location {
|
||||
val newLocation = Location(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
|
||||
newLocation.latitude = point.latitude()
|
||||
newLocation.longitude = point.longitude()
|
||||
}
|
||||
return newLocation
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(type : String, lineCoordinates: List<List<Double>>): String {
|
||||
val lineString = GeoJsonType(type = type, 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 getOverpassBbox(location: Location, radius: Double): String {
|
||||
val bbox = getBoundingBox(location.longitude, location.latitude, radius)
|
||||
val neLon = bbox["ne"]?.get("lon")
|
||||
val neLat = bbox["ne"]?.get("lat")
|
||||
val swLon = bbox["sw"]?.get("lon")
|
||||
val swLat = bbox["sw"]?.get("lat")
|
||||
return "$swLon,$swLat,$neLon,$neLat"
|
||||
}
|
||||
|
||||
fun getBoundingBox2(location: Location, radius: Double): BoundingBox {
|
||||
val bbox = getBoundingBox(location.longitude, location.latitude, radius)
|
||||
val neLon = bbox["ne"]?.get("lon")
|
||||
val neLat = bbox["ne"]?.get("lat")
|
||||
val swLon = bbox["sw"]?.get("lon")
|
||||
val swLat = bbox["sw"]?.get("lat")
|
||||
return BoundingBox(swLat ?: 0.0 , swLon ?: 0.0, neLat ?: 0.0, neLon ?: 0.0)
|
||||
}
|
||||
fun getBoundingBox(
|
||||
lat: Double,
|
||||
lon: Double,
|
||||
radius: Double
|
||||
): Map<String, Map<String, Double>> {
|
||||
val earthRadius = 6371.0
|
||||
val maxLat = lat + toDegrees(radius / earthRadius)
|
||||
val minLat = lat - toDegrees(radius / earthRadius)
|
||||
val maxLon = lon + toDegrees(radius / earthRadius / cos(toRadians(lat)))
|
||||
val minLon = lon - toDegrees(radius / earthRadius / cos(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 calculateZoom(speed: Double?): Double {
|
||||
@@ -224,6 +110,17 @@ fun previewZoom(previewDistance: Double): Double {
|
||||
}
|
||||
return 9.0
|
||||
}
|
||||
|
||||
|
||||
fun calcTilt(newZoom: Double, tilt: Double): Double = if (newZoom < 13) {
|
||||
0.0
|
||||
} else {
|
||||
if (tilt == 0.0) {
|
||||
55.0
|
||||
} else {
|
||||
tilt
|
||||
}
|
||||
}
|
||||
fun bearing(fromLocation: Location, toLocation: Location, oldBearing: Double) : Double {
|
||||
val distance = fromLocation.distanceTo(toLocation)
|
||||
if (distance < 1.0) {
|
||||
|
||||
Reference in New Issue
Block a user