From 915cd1f5a0b31f631669ceeecb121a2d308e6e4a Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sat, 25 Apr 2026 17:21:52 -0500 Subject: [PATCH] feat(rover): replace max-drive-time slider with max-distance in miles --- assets/js/rover_slider_hook.ts | 11 ++--- lib/microwaveprop/rover/compute.ex | 6 +-- lib/microwaveprop_web/live/rover_live.ex | 40 +++++++++---------- test/microwaveprop/rover/compute_test.exs | 4 +- .../live/rover_live_test.exs | 10 ++--- 5 files changed, 34 insertions(+), 37 deletions(-) diff --git a/assets/js/rover_slider_hook.ts b/assets/js/rover_slider_hook.ts index 1368cb4d..20bae074 100644 --- a/assets/js/rover_slider_hook.ts +++ b/assets/js/rover_slider_hook.ts @@ -7,14 +7,11 @@ interface RoverSliderHook extends ViewHook { inputHandler: () => void } -const AVG_SPEED_KMH = 65.0 +const KM_PER_MI = 1.609344 const formatters: Record string> = { forecast: (v) => `+${v}h`, - drive: (v) => { - const hours = Math.round((v / 60) * 10) / 10 - return `${hours}h` - } + distance: (v) => `${v} mi` } export const RoverSlider: Partial = { @@ -29,8 +26,8 @@ export const RoverSlider: Partial = { const value = Number((this.el as HTMLInputElement).value) if (this.labelEl) this.labelEl.textContent = this.formatter(value) - if (this.format === "drive") { - const km = (value * AVG_SPEED_KMH) / 60.0 + if (this.format === "distance") { + const km = value * KM_PER_MI window.dispatchEvent(new CustomEvent("rover:drive-radius", { detail: { km } })) } } diff --git a/lib/microwaveprop/rover/compute.ex b/lib/microwaveprop/rover/compute.ex index 2d69895f..2e08bb4f 100644 --- a/lib/microwaveprop/rover/compute.ex +++ b/lib/microwaveprop/rover/compute.ex @@ -34,7 +34,7 @@ defmodule Microwaveprop.Rover.Compute do band_mhz: non_neg_integer(), valid_time: DateTime.t(), mode: atom(), - max_drive_min: integer(), + max_distance_km: float(), min_elev_gain: integer() } @@ -53,12 +53,12 @@ defmodule Microwaveprop.Rover.Compute do band_mhz: band_mhz, valid_time: valid_time, mode: mode, - max_drive_min: max_drive_min, + max_distance_km: max_distance_km, min_elev_gain: min_elev_gain } = args selected_stations = Enum.filter(stations, & &1.selected) - radius_km = max_drive_min * @avg_speed_kmh / 60.0 + radius_km = max_distance_km * 1.0 bbox = bbox_around(home, radius_km) raw_cells = scores_at.(band_mhz, valid_time, bbox) diff --git a/lib/microwaveprop_web/live/rover_live.ex b/lib/microwaveprop_web/live/rover_live.ex index 11775859..ad758dc8 100644 --- a/lib/microwaveprop_web/live/rover_live.ex +++ b/lib/microwaveprop_web/live/rover_live.ex @@ -31,7 +31,8 @@ defmodule MicrowavepropWeb.RoverLive do @default_band 10_000 @default_mode :ssb @default_forecast_hour 0 - @default_max_drive_min 120 + @default_max_distance_mi 80 + @km_per_mi 1.609344 @avg_speed_kmh 65.0 # NTMS-area anonymous home: EM13 centroid. @@ -51,8 +52,8 @@ defmodule MicrowavepropWeb.RoverLive do band: @default_band, mode: @default_mode, forecast_hour: @default_forecast_hour, - max_drive_min: @default_max_drive_min, - drive_radius_km: drive_radius_km(@default_max_drive_min), + max_distance_mi: @default_max_distance_mi, + drive_radius_km: drive_radius_km(@default_max_distance_mi), fixed_stations: fixed_stations, persisted?: persisted?, home: home, @@ -163,7 +164,7 @@ defmodule MicrowavepropWeb.RoverLive do defp home_label(lat, lon), do: Maidenhead.from_latlon(lat, lon, 10) - defp drive_radius_km(max_drive_min), do: max_drive_min * @avg_speed_kmh / 60.0 + defp drive_radius_km(max_distance_mi), do: max_distance_mi * @km_per_mi # ── Events ──────────────────────────────────────────────────────────── @@ -178,13 +179,13 @@ defmodule MicrowavepropWeb.RoverLive do {:noreply, assign(socket, forecast_hour: hour)} end - def handle_event("set_max_drive_time", %{"value" => v}, socket) do - minutes = v |> parse_int(@default_max_drive_min) |> clamp(30, 240) - radius_km = drive_radius_km(minutes) + def handle_event("set_max_distance", %{"value" => v}, socket) do + miles = v |> parse_int(@default_max_distance_mi) |> clamp(25, 250) + radius_km = drive_radius_km(miles) {:noreply, socket - |> assign(max_drive_min: minutes, drive_radius_km: radius_km) + |> assign(max_distance_mi: miles, drive_radius_km: radius_km) |> push_event("update_drive_radius", %{km: radius_km})} end @@ -666,7 +667,7 @@ defmodule MicrowavepropWeb.RoverLive do band_mhz: socket.assigns.band, valid_time: socket.assigns.current_valid_time || DateTime.utc_now(), mode: socket.assigns.mode, - max_drive_min: socket.assigns.max_drive_min, + max_distance_km: socket.assigns.drive_radius_km, min_elev_gain: 0 } end @@ -916,26 +917,25 @@ defmodule MicrowavepropWeb.RoverLive do - <.sidebar_section title="MAX DRIVE TIME"> + <.sidebar_section title="MAX DISTANCE">
- {Float.round(@max_drive_min / 60, 1)}h - from {@home.label} + {@max_distance_mi} mi from {@home.label}
diff --git a/test/microwaveprop/rover/compute_test.exs b/test/microwaveprop/rover/compute_test.exs index abf4c521..c594fe7d 100644 --- a/test/microwaveprop/rover/compute_test.exs +++ b/test/microwaveprop/rover/compute_test.exs @@ -28,7 +28,7 @@ defmodule Microwaveprop.Rover.ComputeTest do band_mhz: 10_000, valid_time: ~U[2026-04-25 12:00:00Z], mode: :ssb, - max_drive_min: 60, + max_distance_km: 65.0, min_elev_gain: 0 } @@ -87,7 +87,7 @@ defmodule Microwaveprop.Rover.ComputeTest do band_mhz: 10_000, valid_time: ~U[2026-04-25 12:00:00Z], mode: :ssb, - max_drive_min: 60, + max_distance_km: 65.0, min_elev_gain: 200 }, scores_at: scores_at, diff --git a/test/microwaveprop_web/live/rover_live_test.exs b/test/microwaveprop_web/live/rover_live_test.exs index 732d33a2..79d2be24 100644 --- a/test/microwaveprop_web/live/rover_live_test.exs +++ b/test/microwaveprop_web/live/rover_live_test.exs @@ -20,7 +20,7 @@ defmodule MicrowavepropWeb.RoverLiveTest do assert html =~ "FIXED STATIONS" assert html =~ "FORECAST HOUR" assert html =~ "BAND" - assert html =~ "MAX DRIVE TIME" + assert html =~ "MAX DISTANCE" refute html =~ ">MODE<" refute html =~ "MIN ELEV GAIN" end @@ -63,14 +63,14 @@ defmodule MicrowavepropWeb.RoverLiveTest do refute_push_event(view, "rover_results", %{}) end - test "set_max_drive_time updates the dashed circle radius", %{conn: conn} do + test "set_max_distance updates the dashed circle radius", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/rover") - render_hook(view, "set_max_drive_time", %{"value" => "120"}) + render_hook(view, "set_max_distance", %{"value" => "100"}) - # 120 min @ 65 km/h = 130 km + # 100 mi = 160.93 km assert_push_event(view, "update_drive_radius", %{km: km}) - assert_in_delta km, 130.0, 0.1 + assert_in_delta km, 160.93, 0.1 end test "calculate triggers a rover_results push_event with cells + drive_radius_km", %{conn: conn} do