Overpass Performance, ApplicationConfig

This commit is contained in:
Dimitris
2026-05-09 09:33:50 +02:00
parent 29e58f6a24
commit ffa0d47e7b
9 changed files with 256 additions and 140 deletions
+1
View File
@@ -29,6 +29,7 @@ android {
buildConfigField("String", "USER", "\"${properties.getProperty("USER") ?: ""}\"")
buildConfigField("String", "PASSWORD", "\"${properties.getProperty("PASSWORD") ?: ""}\"")
buildConfigField("String", "TANKER_KOENIG_API_KEY", "\"${properties.getProperty("TANKER_KOENIG_API_KEY") ?: ""}\"")
}
buildFeatures {
@@ -4,14 +4,16 @@ import com.kouros.data.BuildConfig
data class ApplicationConfig(
val user: String,
val password: String
val password: String,
val tankerKoenigApiKey: String
) {
companion object {
fun load(): ApplicationConfig {
return ApplicationConfig(
user = BuildConfig.USER,
password = BuildConfig.PASSWORD
password = BuildConfig.PASSWORD,
tankerKoenigApiKey = BuildConfig.TANKER_KOENIG_API_KEY
)
}
}
@@ -21,18 +21,19 @@ private val gson = GsonBuilder().serializeNulls().create()
const val sort = "&sort=dist&type=all"
const val apiKey = "&apikey=fc9900c9-8f02-4b28-990d-dc6067228c59"
val useLocal = BuildConfig.DEBUG
class FuelPrices : NavigationRepository() {
private val config by lazy { com.kouros.navigation.data.ApplicationConfig.load() }
fun getFuelPrices(location: Location, radius: Int) : List<Station> {
val url = if (useLocal) {
"http://192.168.1.37/fuel.json"
} else {
"${tankerKoenigUrl}lat=${location.latitude}&lng=${location.longitude}&rad=${radius}$sort$apiKey"
"${tankerKoenigUrl}lat=${location.latitude}&lng=${location.longitude}&rad=${radius}$sort&apikey=${config.tankerKoenigApiKey}"
}
val prices = fetchUrl(
@@ -49,7 +50,7 @@ class FuelPrices : NavigationRepository() {
carOrientation: Float,
searchFilter: SearchFilter
): String {
TODO("Not yet implemented")
return ""
}
override fun getTraffic(
@@ -51,7 +51,7 @@ class Overpass {
}
val searchQuery = """
|[out:json];
|[out:json][timeout:10];
|(
| ${searchClauses.joinToString(";")};
|);
@@ -75,7 +75,7 @@ class Overpass {
val searchLocation ="way[\"highway\"~\"^(primary|secondary|tertiary|residential|motorway)$\"][name](around:50, $lineString)";
val searchQuery = """
|[out:json];
|[out:json][timeout:10];
|(
| ${searchLocation};
|);
@@ -51,6 +51,8 @@ import kotlin.collections.first
import kotlin.collections.forEach
import kotlin.comparisons.compareBy
import kotlin.math.absoluteValue
import kotlin.math.cos
import kotlin.math.sqrt
/**
* ViewModel for navigation-related data operations.
@@ -58,6 +60,8 @@ import kotlin.math.absoluteValue
*/
class NavigationViewModel(private val repository: NavigationRepository) : ViewModel() {
private val overpass = Overpass()
/** LiveData containing the calculated route JSON string */
val route: MutableLiveData<String> by lazy {
MutableLiveData()
@@ -411,7 +415,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
) {
viewModelScope.launch(Dispatchers.IO) {
val repository = getSettingsRepository(carContext)
val amenities = Overpass().getAmenities("amenity", category, location, 5.0)
val amenities = overpass.getAmenities("amenity", category, location, 5.0)
val fuelPrices = fuelStations(category, lastFuelUpdate, location, repository)
val distAmenities = mutableListOf<Elements>()
amenities.forEach {
@@ -473,7 +477,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
fun getSpeedCameras(location: Location, radius: Double) {
synchronized(this) {
viewModelScope.launch(Dispatchers.IO) {
val amenities = Overpass().getAmenities("highway", "speed_camera", location, radius)
val amenities = overpass.getAmenities("highway", "speed_camera", location, radius)
val distAmenities = mutableListOf<Elements>()
amenities.forEach {
val plLocation =
@@ -509,43 +513,46 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
*/
fun calculateSpeedLimit(location: Location, routeBearing: Float, countryCode: String): Int {
var speed = 0
var element: Elements?
val search = mutableListOf<ElementSearch>()
synchronized(this) {
speedElements.filter { it.type == "way" }.forEach {
var distance: Float
var maxDistance = 1000F
var geometryFirstLocation = location(0.0, 0.0)
var geometryLastLocation = location(0.0, 0.0)
for ((geoIndex, geo) in it.geometry.withIndex()) {
if (geoIndex == 0) {
geometryFirstLocation = location(geo.lon, geo.lat)
}
if (geoIndex == it.geometry.size - 1) {
geometryLastLocation = location(geo.lon, geo.lat)
}
val geometryLocation = location(geo.lon, geo.lat)
distance = geometryLocation.distanceTo(location)
if (distance < maxDistance) {
maxDistance = distance
}
// Equirectangular projection at the user's latitude. Closest-point ranking
// doesn't need geodesic accuracy, so we skip Location.distanceTo (Vincenty)
// and avoid allocating a Location per geometry vertex.
val userLat = location.latitude
val userLon = location.longitude
val metersPerDegLat = 111_320.0
val metersPerDegLon = metersPerDegLat * cos(Math.toRadians(userLat))
for (element in speedElements) {
if (element.type != "way") continue
val geometry = element.geometry
if (geometry.isEmpty()) continue
var minDistanceSq = Double.MAX_VALUE
for (geo in geometry) {
val dx = (geo.lon - userLon) * metersPerDegLon
val dy = (geo.lat - userLat) * metersPerDegLat
val sq = dx * dx + dy * dy
if (sq < minDistanceSq) minDistanceSq = sq
}
val streetBearing = geometryFirstLocation.bearingPositive(geometryLastLocation)
if (isBearingValid(it, streetBearing, routeBearing)) {
val minDistance = sqrt(minDistanceSq)
val first = geometry.first()
val last = geometry.last()
val streetBearing = location(first.lon, first.lat)
.bearingPositive(location(last.lon, last.lat))
if (isBearingValid(element, streetBearing, routeBearing)) {
search.add(
ElementSearch(
it,
maxDistance.toDouble(),
streetBearing.absoluteValue
)
ElementSearch(element, minDistance, streetBearing.absoluteValue)
)
}
}
val result =
search.sortedWith(compareBy<ElementSearch> { it.distance }.thenByDescending { it.bearing })
if (result.isNotEmpty()) {
element = result.first().element
val element = result.first().element
speed = if (element.tags.maxspeed == "none" && element.tags.highway == "motorway") {
countryCodeSpeedLimit(countryCode)
} else {
@@ -585,7 +592,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
synchronized(this) {
val lineString = "${location.latitude},${location.longitude}"
val elements =
Overpass().getSpeedLimit(SPEED_UPDATE_DISTANCE, lineString, street, roadNumbers)
overpass.getSpeedLimit(SPEED_UPDATE_DISTANCE, lineString, street, roadNumbers)
speedElements.clear()
speedElements.addAll(elements)
}
@@ -740,7 +747,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
fun loadCurrentLocation(location: Location) {
viewModelScope.launch(Dispatchers.IO) {
synchronized(this) {
val elements = Overpass().getStreet(location)
val elements = overpass.getStreet(location)
if (elements.isNotEmpty()) {
val points = mutableListOf<Point>()
elements.first().geometry.forEach {
@@ -17,6 +17,7 @@ class RouteCalculator(var routeModel: RouteModel) {
var bestMatch: StepMatch? = null
var lastSpeedLocation: Location = location(0.0, 0.0)
var lastLocalSpeedLocation: Location = location(0.0, 0.0)
var lastSpeedIndex: Int = 0
@@ -157,13 +158,16 @@ class RouteCalculator(var routeModel: RouteModel) {
if ((distance > SPEED_UPDATE_DISTANCE * 2) || lastSpeedIndex < routeModel.route.currentStepIndex) {
lastSpeedIndex = routeModel.route.currentStepIndex
lastSpeedLocation = location
// Force the local re-match on the next GPS fix once new elements arrive.
lastLocalSpeedLocation = location(0.0, 0.0)
viewModel.updateSpeedLimit(
location,
routeModel.route.currentStep().street,
routeModel.currentStep().roadNumbers
)
} else {
} else if (lastLocalSpeedLocation.distanceTo(location) >= NEAREST_LOCATION_DISTANCE) {
lastLocalSpeedLocation = location
viewModel.getSpeedLimit(
location,
routeModel.navState.routeBearing,
File diff suppressed because one or more lines are too long