prop/lib/microwaveprop/propagation/grid.ex
Graham McIntire 51e390959c Add dialyzer specs and types across the codebase
277 @spec/@type annotations added to 58 files covering all public
APIs: contexts (propagation, radio, weather, terrain, beacons,
commercial), GRIB2 decoders, terrain analysis, duct detection,
rain scatter, CSV/ADIF import, weather clients, and all Ecto
schemas. Dialyzer passes with 0 errors.
2026-04-12 08:55:04 -05:00

54 lines
1.6 KiB
Elixir

defmodule Microwaveprop.Propagation.Grid do
@moduledoc "CONUS grid definition for propagation scoring. 0.125 degree resolution (~14 km)."
@lat_min 25.0
@lat_max 50.0
@lon_min -125.0
@lon_max -66.0
@step 0.125
@doc "Returns all grid points as `{lat, lon}` tuples covering CONUS at 0.125 degree spacing."
@spec conus_points() :: [{float(), float()}]
def conus_points do
for lat <- float_range(@lat_min, @lat_max, @step),
lon <- float_range(@lon_min, @lon_max, @step) do
{Float.round(lat, 3), Float.round(lon, 3)}
end
end
@doc "Returns the grid step size in degrees."
@spec step() :: float()
def step, do: @step
@doc "Returns the CONUS bounding box as a map."
@spec bounds() :: %{lat_min: float(), lat_max: float(), lon_min: float(), lon_max: float()}
def bounds, do: %{lat_min: @lat_min, lat_max: @lat_max, lon_min: @lon_min, lon_max: @lon_max}
@doc "Returns the grid specification for wgrib2 -lola extraction."
@spec wgrib2_grid_spec() :: %{
lon_start: float(),
lon_count: non_neg_integer(),
lon_step: float(),
lat_start: float(),
lat_count: non_neg_integer(),
lat_step: float()
}
def wgrib2_grid_spec do
lon_count = round((@lon_max - @lon_min) / @step) + 1
lat_count = round((@lat_max - @lat_min) / @step) + 1
%{
lon_start: @lon_min,
lon_count: lon_count,
lon_step: @step,
lat_start: @lat_min,
lat_count: lat_count,
lat_step: @step
}
end
defp float_range(start, stop, step) do
count = round((stop - start) / step) + 1
Enum.map(0..(count - 1), fn i -> start + i * step end)
end
end