Packages
localize
0.26.0
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.41.3
0.41.2
0.41.1
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.1
0.30.0
retired
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
0.1.0-alpha.1
Localization (parsing, formatting) of numbers, dates/time/calendar, units of measure, messages and lists. Includes localized collation.
Current section
Files
Jump to
Current section
Files
lib/localize/unit/conversion/beaufort.ex
defmodule Localize.Unit.Conversion.Beaufort do
@moduledoc """
Nonlinear conversion between the Beaufort wind force scale and
meters per second.
Implements the ICU algorithm using interpolated midpoints between
the WMO-defined speed thresholds for each Beaufort number.
Registered as a `:special` custom unit so that the generic
conversion pipeline dispatches through `forward/1` and `inverse/1`
rather than using factor-based arithmetic.
"""
# Minimum m/s threshold for each Beaufort number (0–18).
# Index 18 is an artificial end value to give a reasonable midpoint for B=17.
# Source: ICU4C units_converter.cpp minMetersPerSecForBeaufort[].
@thresholds {0.0, 0.3, 1.6, 3.4, 5.5, 8.0, 10.8, 13.9, 17.2, 20.8, 24.5, 28.5, 32.7, 36.9, 41.4,
46.1, 51.1, 55.8, 61.4}
@max_beaufort tuple_size(@thresholds) - 2
@doc """
Converts a Beaufort scale value to meters per second.
Values are clamped to the range [0, 17]. Fractional Beaufort numbers
are interpolated between adjacent midpoints.
"""
@spec forward(number()) :: float()
def forward(beaufort) do
clamped = min(max(beaufort, 0.0), @max_beaufort * 1.0)
index = trunc(clamped)
fraction = clamped - index
low = elem(@thresholds, index)
high = elem(@thresholds, index + 1)
midpoint_low = (low + high) / 2.0
if fraction == 0.0 do
midpoint_low
else
next_index = min(index + 1, @max_beaufort)
next_low = elem(@thresholds, next_index)
next_high = elem(@thresholds, next_index + 1)
midpoint_high = (next_low + next_high) / 2.0
midpoint_low + fraction * (midpoint_high - midpoint_low)
end
end
@doc """
Converts meters per second to a Beaufort scale value.
Finds the Beaufort band whose midpoint range contains the given
speed and interpolates within it.
"""
@spec inverse(number()) :: float()
def inverse(mps) do
if mps < 0.0 do
0.0
else
find_band(mps, 0)
end
end
defp find_band(_mps, index) when index >= @max_beaufort do
@max_beaufort * 1.0
end
defp find_band(mps, index) do
low = elem(@thresholds, index)
high = elem(@thresholds, index + 1)
midpoint = (low + high) / 2.0
next_low = elem(@thresholds, index + 1)
next_high = elem(@thresholds, min(index + 2, tuple_size(@thresholds) - 1))
next_midpoint = (next_low + next_high) / 2.0
if mps < next_midpoint do
if next_midpoint == midpoint do
index * 1.0
else
index + (mps - midpoint) / (next_midpoint - midpoint)
end
else
find_band(mps, index + 1)
end
end
end