feat: add DistanceFilterProcessor with Haversine formula

This commit is contained in:
fiatcode 2026-04-30 11:00:03 +07:00
parent 0f969afbd5
commit c1bd51eb48
No known key found for this signature in database

View file

@ -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
}
}
}