diff --git a/assets/js/rover_map_hook.js b/assets/js/rover_map_hook.js index c31411ba..2f4b000c 100644 --- a/assets/js/rover_map_hook.js +++ b/assets/js/rover_map_hook.js @@ -19,6 +19,9 @@ export const RoverMap = { this.stationMarkers.addTo(map) this.coverageLayer.addTo(map) + this.gridLayer = L.layerGroup().addTo(map) + this.drawGridOverlay() + this.handleEvent("stations_updated", ({ stations }) => { this.stations = stations this.renderStations() @@ -27,6 +30,14 @@ export const RoverMap = { this.handleEvent("coverage_updated", ({ grids }) => { this.renderCoverage(grids) }) + + this.handleEvent("toggle_grids", ({ show }) => { + if (show) { + this.gridLayer.addTo(this.map) + } else { + this.map.removeLayer(this.gridLayer) + } + }) }, renderStations() { @@ -64,10 +75,9 @@ export const RoverMap = { const maxScore = Math.max(...grids.map(g => g.coverage_score)) for (const g of grids) { - // 4-char Maidenhead: 2° lon × 1° lat - // Compute grid square bounds from the grid letters - const bounds = this.gridBounds(g.grid) - if (!bounds) continue + // Render as 0.25° cells centered on the candidate point + const half = 0.125 + const bounds = [[g.lat - half, g.lon - half], [g.lat + half, g.lon + half]] const normalized = maxScore > 0 ? g.coverage_score / maxScore : 0 const color = this.coverageColor(normalized) @@ -149,6 +159,19 @@ export const RoverMap = { return "#ff4f4f" }, + drawGridOverlay() { + for (let lat = 25; lat <= 50; lat += 1) { + L.polyline([[lat, -125], [lat, -66]], { + color: "#888", weight: 0.5, opacity: 0.3 + }).addTo(this.gridLayer) + } + for (let lon = -125; lon <= -66; lon += 2) { + L.polyline([[25, lon], [50, lon]], { + color: "#888", weight: 0.5, opacity: 0.3 + }).addTo(this.gridLayer) + } + }, + fitBounds() { const points = this.stations.map(s => [s.lat, s.lon]) if (points.length >= 2) { diff --git a/lib/microwaveprop/rover/coverage.ex b/lib/microwaveprop/rover/coverage.ex index 6f0c33f6..e44993af 100644 --- a/lib/microwaveprop/rover/coverage.ex +++ b/lib/microwaveprop/rover/coverage.ex @@ -79,20 +79,21 @@ defmodule Microwaveprop.Rover.Coverage do min_lon = max(Enum.min(lons) - range_deg, -125.0) max_lon = min(Enum.max(lons) + range_deg, -66.0) - # Generate at 4-char Maidenhead resolution (1° lat × 2° lon) - lat_range = Enum.to_list(trunc(min_lat)..trunc(max_lat)) - lon_range = Enum.to_list(trunc(min_lon)..trunc(max_lon)//2) + # Generate candidates at 0.25° resolution (between HRRR 0.125° and Maidenhead 1-2°) + # This gives ~16x more candidates than Maidenhead but stays manageable + step = 0.25 - # Use center of grid square - for_result = - for lat <- lat_range, lon <- lon_range do - clat = lat + 0.5 - clon = lon + 1.0 - grid = Geo.latlon_to_grid4(clat, clon) - {grid, clat, clon} - end + for lat <- float_range(min_lat, max_lat, step), + lon <- float_range(min_lon, max_lon, step) do + grid = Geo.latlon_to_grid4(lat, lon) + {grid, Float.round(lat, 3), Float.round(lon, 3)} + end + end - Enum.uniq_by(for_result, fn {grid, _, _} -> grid end) + defp float_range(min, max, step) do + (Float.ceil(min / step) * step) + |> Stream.iterate(&(&1 + step)) + |> Enum.take_while(&(&1 <= max)) end defp fast_score_grid(grid, lat, lon, stations, band_mhz, max_range) do diff --git a/lib/microwaveprop_web/live/rover_live.ex b/lib/microwaveprop_web/live/rover_live.ex index 496f6c32..7bb7cfe9 100644 --- a/lib/microwaveprop_web/live/rover_live.ex +++ b/lib/microwaveprop_web/live/rover_live.ex @@ -32,7 +32,8 @@ defmodule MicrowavepropWeb.RoverLive do selected_grid: nil, resolving: false, computing: false, - url_loaded: false + url_loaded: false, + show_grids: true )} end @@ -80,6 +81,11 @@ defmodule MicrowavepropWeb.RoverLive do {:noreply, push_url(socket)} end + def handle_event("toggle_grids", _params, socket) do + show = !socket.assigns.show_grids + {:noreply, socket |> assign(show_grids: show) |> push_event("toggle_grids", %{show: show})} + end + def handle_event("select_band", %{"band" => band_str}, socket) do socket = assign(socket, band: String.to_integer(band_str), coverage: [], selected_grid: nil) {:noreply, push_url(socket)} @@ -254,11 +260,21 @@ defmodule MicrowavepropWeb.RoverLive do