From b01a638674a313355664cc387e7da0d77a9c32d0 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 10 Apr 2026 22:26:02 +0000 Subject: Suppress map label flicker from GPS jitter GPS settling produces many tiny position updates that each triggered a 1-second animateCamera(), forcing continuous symbol-collision recalculation and label fade cycles. Fix: skip animation entirely (moveCamera) when the new position is < 3 m from the last one; use a shorter easeCamera(400 ms) for real movement so the animation window is smaller. https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX --- .../src/main/kotlin/org/terst/nav/ui/MapHandler.kt | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/MapHandler.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/MapHandler.kt index ca146b1..a7d746f 100644 --- a/android-app/app/src/main/kotlin/org/terst/nav/ui/MapHandler.kt +++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/MapHandler.kt @@ -26,6 +26,7 @@ import org.terst.nav.data.model.WindArrow import org.terst.nav.track.TrackPoint import kotlin.math.cos import kotlin.math.sin +import kotlin.math.sqrt import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -225,14 +226,32 @@ class MapHandler(private val maplibreMap: MapLibreMap) { * Centers the map on the specified location. */ fun centerOnLocation(lat: Double, lon: Double, zoom: Double = 14.0) { + val prevLat = lastLat + val prevLon = lastLon lastLat = lat lastLon = lon if (!_isFollowing.value) return + val position = CameraPosition.Builder() .target(LatLng(lat, lon)) .zoom(zoom) .build() - maplibreMap.animateCamera(CameraUpdateFactory.newCameraPosition(position), 1000) + + // Skip animation for tiny GPS jitter (< 3 m) to avoid repeated symbol + // collision recalculations that cause labels to flash on/off. + val deltaMeters = approximateMeters(prevLat, prevLon, lat, lon) + if (deltaMeters < 3.0) { + maplibreMap.moveCamera(CameraUpdateFactory.newCameraPosition(position)) + } else { + maplibreMap.easeCamera(CameraUpdateFactory.newCameraPosition(position), 400) + } + } + + /** Fast flat-earth distance approximation (accurate to ~0.1% within a few km). */ + private fun approximateMeters(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double { + val dLat = (lat2 - lat1) * 111_320.0 + val dLon = (lon2 - lon1) * 111_320.0 * cos(Math.toRadians((lat1 + lat2) / 2.0)) + return sqrt(dLat * dLat + dLon * dLon) } /** -- cgit v1.2.3