feat: add DistanceFilterProcessor with Haversine formula
This commit is contained in:
parent
0f969afbd5
commit
c1bd51eb48
1 changed files with 97 additions and 0 deletions
|
|
@ -0,0 +1,97 @@
|
||||||
|
package com.traccar.traccar_client.location
|
||||||
|
|
||||||
|
import android.location.Location
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.atan2
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.sin
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
data class FilterConfig(
|
||||||
|
val distanceFilter: Int = 75,
|
||||||
|
val intervalFilter: Int = 300,
|
||||||
|
val angleFilter: Int = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
class DistanceFilterProcessor {
|
||||||
|
private var lastLatitude: Double? = null
|
||||||
|
private var lastLongitude: Double? = null
|
||||||
|
private var lastTimestamp: Long = 0
|
||||||
|
private var lastHeading: Float? = null
|
||||||
|
|
||||||
|
fun shouldAccept(location: Location, config: FilterConfig): Boolean {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
|
||||||
|
// Distance filter
|
||||||
|
if (config.distanceFilter > 0 && lastLatitude != null && lastLongitude != null) {
|
||||||
|
val distance = haversineDistance(lastLatitude!!, lastLongitude!!, location.latitude, location.longitude)
|
||||||
|
if (distance < config.distanceFilter) {
|
||||||
|
// Check interval as fallback
|
||||||
|
if (config.intervalFilter > 0) {
|
||||||
|
val elapsed = (now - lastTimestamp) / 1000
|
||||||
|
if (elapsed < config.intervalFilter) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interval filter (when distance filter is 0)
|
||||||
|
if (config.distanceFilter == 0 && config.intervalFilter > 0) {
|
||||||
|
val elapsed = (now - lastTimestamp) / 1000
|
||||||
|
if (elapsed < config.intervalFilter && lastTimestamp > 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Angle filter
|
||||||
|
if (config.angleFilter > 0 && lastHeading != null && location.hasBearing()) {
|
||||||
|
val headingDelta = abs(location.bearing - lastHeading!!)
|
||||||
|
// Handle wrap-around at 360 degrees
|
||||||
|
val normalizedDelta = if (headingDelta > 180) 360 - headingDelta else headingDelta
|
||||||
|
if (normalizedDelta < config.angleFilter && lastTimestamp > 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last known values
|
||||||
|
lastLatitude = location.latitude
|
||||||
|
lastLongitude = location.longitude
|
||||||
|
lastTimestamp = now
|
||||||
|
if (location.hasBearing()) {
|
||||||
|
lastHeading = location.bearing
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
lastLatitude = null
|
||||||
|
lastLongitude = null
|
||||||
|
lastTimestamp = 0
|
||||||
|
lastHeading = null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Calculate distance between two coordinates using Haversine formula
|
||||||
|
* @return distance in meters
|
||||||
|
*/
|
||||||
|
fun haversineDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
|
||||||
|
val earthRadius = 6371000.0 // meters
|
||||||
|
|
||||||
|
val dLat = Math.toRadians(lat2 - lat1)
|
||||||
|
val dLon = Math.toRadians(lon2 - lon1)
|
||||||
|
|
||||||
|
val a = sin(dLat / 2) * sin(dLat / 2) +
|
||||||
|
cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) *
|
||||||
|
sin(dLon / 2) * sin(dLon / 2)
|
||||||
|
|
||||||
|
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
|
||||||
|
|
||||||
|
return earthRadius * c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue