summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude <noreply@anthropic.com>2026-04-11 18:35:23 +0000
committerClaude <noreply@anthropic.com>2026-04-11 18:35:23 +0000
commitacb1d24e7b4a4c5c01c8fe5a50d702cbbaed2d5d (patch)
treee3470ca7eb01a2fada73fcbea745d01e19d9a24d
parentec2bc313ba88477aeefcb5c943ed387de2883ee8 (diff)
Throttle conditions API; delay particle view until data loaded
loadConditions now skips the API call if conditions were loaded < 5 min ago and position hasn't moved > 2 nm — prevents 429 from overlapping triggers (startup backstop, GPS first-fix, camera idle). Dev log shows "conditions: skip age=Xs dist=Y.Znm" when throttled. ParticleWindView starts hidden and is revealed only when the first windArrow value arrives, so particles don't flash before the map loads. https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt9
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/ui/MainViewModel.kt27
2 files changed, 31 insertions, 5 deletions
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt b/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt
index 35a0e91..b7db246 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt
@@ -400,10 +400,8 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener {
mapView = findViewById(R.id.mapView)
particleWindView = findViewById(R.id.particle_wind_view)
- // Apply persisted particles preference immediately
- if (!layerManager.particlesEnabled) {
- particleWindView?.visibility = View.GONE
- }
+ // Always start hidden; revealed by the windArrow observer once data arrives
+ particleWindView?.visibility = View.GONE
if (NavApplication.isTesting) return
@@ -503,6 +501,9 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener {
if (arrow != null) {
mapHandler?.updateWindLayer(arrow)
particleWindView?.setWind(arrow.directionDeg, arrow.speedKt)
+ if (layerManager.particlesEnabled) {
+ particleWindView?.visibility = View.VISIBLE
+ }
}
}
}
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/MainViewModel.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/MainViewModel.kt
index e65cbd1..868b828 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/ui/MainViewModel.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/MainViewModel.kt
@@ -170,11 +170,27 @@ class MainViewModel(
.create(AisHubApiService::class.java)
}
+ private var lastConditionsLat = Double.NaN
+ private var lastConditionsLon = Double.NaN
+ private var lastConditionsMs = 0L
+
/**
* Fetches current conditions snapshot for [lat]/[lon] and exposes it via [marineConditions].
- * Silently ignored on network failure — the UI keeps showing dashes.
+ * Throttled: skips if conditions loaded < 5 min ago and position < 2 nm from last load.
*/
fun loadConditions(lat: Double, lon: Double) {
+ val nowMs = System.currentTimeMillis()
+ val ageMs = nowMs - lastConditionsMs
+ val distNm = if (lastConditionsLat.isNaN()) Double.MAX_VALUE
+ else distanceNm(lastConditionsLat, lastConditionsLon, lat, lon)
+ if (ageMs < 5 * 60_000L && distNm < 2.0) {
+ NavLogger.i("conditions", "skip age=${ageMs / 1000}s dist=%.1fnm".format(distNm))
+ return
+ }
+ lastConditionsLat = lat
+ lastConditionsLon = lon
+ lastConditionsMs = nowMs
+
conditionsJob?.cancel()
conditionsJob = viewModelScope.launch {
_conditionsLoading.value = true
@@ -192,6 +208,15 @@ class MainViewModel(
}
}
+ private fun distanceNm(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
+ 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)
+ return 3440.065 * 2 * kotlin.math.asin(kotlin.math.sqrt(a))
+ }
+
/**
* Fetch weather and wind arrow for [lat]/[lon]. Called once on first GPS fix.
*/