fix(weather): drop valid_times older than one hour from the timeline
The HRRR scalar cache can hold a previous run's forecast hours past their useful life — the timeline was offering "current conditions" points that were 2-3 hours old, which is more misleading than helpful on a map labeled "Now". Filter Weather.available_weather_valid_times() through a `now - 1h` cutoff in mount, :weather_updated, and :propagation_pipeline_progress so the timeline only ever shows the most recent analysis hour plus forecast hours.
This commit is contained in:
parent
b66024e0c3
commit
9e4ca154ff
2 changed files with 36 additions and 3 deletions
|
|
@ -27,7 +27,7 @@ defmodule MicrowavepropWeb.WeatherMapLive do
|
||||||
# the LiveView socket join timeout. The client now paints weather
|
# the LiveView socket join timeout. The client now paints weather
|
||||||
# through a tile endpoint, so mount only needs the selected layer
|
# through a tile endpoint, so mount only needs the selected layer
|
||||||
# and valid_time metadata.
|
# and valid_time metadata.
|
||||||
valid_times = Weather.available_weather_valid_times()
|
valid_times = recent_valid_times(Weather.available_weather_valid_times())
|
||||||
initial_vt = pick_initial_valid_time(valid_times)
|
initial_vt = pick_initial_valid_time(valid_times)
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
|
|
@ -65,6 +65,15 @@ defmodule MicrowavepropWeb.WeatherMapLive do
|
||||||
Enum.min_by(valid_times, fn vt -> abs(DateTime.diff(vt, now, :second)) end)
|
Enum.min_by(valid_times, fn vt -> abs(DateTime.diff(vt, now, :second)) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Drop valid_times older than one hour ago. The HRRR scalar cache can
|
||||||
|
# hold the previous run's forecast hours past their useful life;
|
||||||
|
# showing 3-hour-old "current conditions" on the map is more
|
||||||
|
# misleading than helpful.
|
||||||
|
defp recent_valid_times(valid_times) do
|
||||||
|
cutoff = DateTime.add(DateTime.utc_now(), -3600, :second)
|
||||||
|
Enum.filter(valid_times, fn vt -> DateTime.compare(vt, cutoff) != :lt end)
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("select_layer", %{"layer" => layer_id}, socket) do
|
def handle_event("select_layer", %{"layer" => layer_id}, socket) do
|
||||||
if MapLayers.valid_id?(layer_id) do
|
if MapLayers.valid_id?(layer_id) do
|
||||||
|
|
@ -122,7 +131,7 @@ defmodule MicrowavepropWeb.WeatherMapLive do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info({:weather_updated, _valid_time}, socket) do
|
def handle_info({:weather_updated, _valid_time}, socket) do
|
||||||
valid_times = Weather.available_weather_valid_times()
|
valid_times = recent_valid_times(Weather.available_weather_valid_times())
|
||||||
|
|
||||||
# Keep whatever the user has scrubbed to if it's still available,
|
# Keep whatever the user has scrubbed to if it's still available,
|
||||||
# otherwise fall back to the latest on-disk time (GridCache-backed
|
# otherwise fall back to the latest on-disk time (GridCache-backed
|
||||||
|
|
@ -152,7 +161,7 @@ defmodule MicrowavepropWeb.WeatherMapLive do
|
||||||
# so the timeline picks up the extra hour, but keep the currently
|
# so the timeline picks up the extra hour, but keep the currently
|
||||||
# selected time where it is.
|
# selected time where it is.
|
||||||
def handle_info({:propagation_pipeline_progress, _progress}, socket) do
|
def handle_info({:propagation_pipeline_progress, _progress}, socket) do
|
||||||
valid_times = Weather.available_weather_valid_times()
|
valid_times = recent_valid_times(Weather.available_weather_valid_times())
|
||||||
|
|
||||||
if valid_times == socket.assigns.valid_times do
|
if valid_times == socket.assigns.valid_times do
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,30 @@ defmodule MicrowavepropWeb.WeatherMapLiveTest do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "mount drops valid_times older than one hour ago", %{conn: conn} do
|
||||||
|
now =
|
||||||
|
DateTime.utc_now()
|
||||||
|
|> DateTime.truncate(:second)
|
||||||
|
|> Map.put(:minute, 0)
|
||||||
|
|> Map.put(:second, 0)
|
||||||
|
|
||||||
|
stale_t = DateTime.add(now, -2 * 3600, :second)
|
||||||
|
recent_t = DateTime.add(now, -30 * 60, :second)
|
||||||
|
future_t = DateTime.add(now, 3 * 3600, :second)
|
||||||
|
|
||||||
|
Enum.each([stale_t, recent_t, future_t], &write_profile(&1, 33.0, -97.0))
|
||||||
|
|
||||||
|
{:ok, _lv, html} = live(conn, ~p"/weather")
|
||||||
|
|
||||||
|
stale_iso = DateTime.to_iso8601(stale_t)
|
||||||
|
recent_iso = DateTime.to_iso8601(recent_t)
|
||||||
|
future_iso = DateTime.to_iso8601(future_t)
|
||||||
|
|
||||||
|
refute html =~ stale_iso, "stale valid_time #{stale_iso} should be filtered out"
|
||||||
|
assert html =~ recent_iso, "recent valid_time #{recent_iso} should be present"
|
||||||
|
assert html =~ future_iso, "future valid_time #{future_iso} should be present"
|
||||||
|
end
|
||||||
|
|
||||||
test "mount defaults to the valid_time closest to now, not the latest forecast hour",
|
test "mount defaults to the valid_time closest to now, not the latest forecast hour",
|
||||||
%{conn: conn} do
|
%{conn: conn} do
|
||||||
base =
|
base =
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue