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