Module:Sandbox/Erutuon/Climate
Appearance
Usage
Exports a function that calculates the Köppen climate classification of monthly average temperature and precipitation data in degrees Celsius and millimeters, based on the formulas described in the Wikipedia article.
Function
|Koeppen_template=
- Parameters:
|1=
,|2=
- Temperatures and precipitation figures (12, one per month), separated by any characters that are not digits (0-9), hyphen-minuses (-), or periods (.).
|3=yes
- Indicates that the data is from a location in the Southern Hemisphere. The default is the Northern Hemisphere. This is used to determine the high-sun and low-sun halves of the year.
|alt_CD=
- Use -3 °C as the dividing line between C and D climates. The default is 0 °C.
|alt_hk=
- Cold semi-arid and arid climates (last letter k) have mean annual temperatures below 18 °C. The default is that they have at least one month below 0 °C.
|alt_w=
- Dry-winter climates (last letter w) have more than 70% of precipitation in high-sun half of year. Default is that wettest summer month has ten times as much precipitation as driest winter month.
Examples
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
- Lua error in package.lua at line 80: module 'Module:Utility' not found.
local p = {}
local map = require "Module:Utility".map
local function errorf(level, ...)
if type(level) == number then
return error(string.format(...), level + 1)
else -- level is actually the format string.
return error(string.format(level, ...), 2)
end
end
local function count(func, t)
local count = 0
for _, v in ipairs(t) do
if func(v) then
count = count + 1
end
end
return count
end
local function make_arr_and_varargs_func(varargs_func)
local function arr_func (arr, i, j)
i = i or 1
j = j or #arr
assert(i > 0 and j > 0)
if i == j then
return arr[i]
elseif i < j then -- |---i+++j---|
return varargs_func(unpack(arr, i, j))
else -- |+++j---i+++|
return varargs_func(arr_func(arr, 1, j), arr_func(arr, i))
end
end
return function (...)
if type(...) == 'table' then -- if first argument is table
if select(2, ...) then -- if second argument
return arr_func(...)
else
return varargs_func(unpack((...)))
end
else
return varargs_func(...)
end
end
end
local function sum_varargs (...)
local n = select('#', ...)
local result = 0
for i = 1, n do
result = result + select(i, ...)
end
return result
end
local mean = make_arr_and_varargs_func(
function (...)
return sum_varargs(...) / select('#', ...)
end)
local sum = make_arr_and_varargs_func(sum_varargs)
local min_and_max = make_arr_and_varargs_func(
function (...)
local min, max = math.huge, -math.huge
for i = 1, select('#', ...) do
local val = select(i, ...)
if val < min then
min = val
end
if val > max then
max = val
end
end
return min, max
end)
local function min_index(t)
local index
local min = math.huge
for i, v in ipairs(t) do
if v < min then
min = v
index = i
end
end
return index
end
local function max_index(t)
local index
local max = -math.huge
for i, v in ipairs(t) do
if v > max then
max = v
index = i
end
end
return index
end
local function in_range(val, low, high)
if low < high then -- |---i+++j---|
return low <= val and val <= high
else -- |+++j---i+++|
return val < high or low < val
end
end
local function mean_of_highs_and_lows(highs, lows)
local high_count, low_count = #highs, #lows
-- for now, no annual average accepted
if not (high_count == 12 and low_count == 12) then
errorf("Wrong number of highs or lows (%d, %d): expected 12 each",
high_count, low_count)
elseif high_count ~= low_count then
errorf("Number of highs (%d) is not equal to number of lows (%d)",
high_count, low_count)
end
local temperatures = {}
for i = 1, high_count do
if highs[i] <= lows[i] then
errorf("High #%d (%d) is not greater than low #%d (%d)",
i, highs[i], i, lows[i])
end
temperatures[i] = mean(highs[i], lows[i])
end
return temperatures
end
-- Temperatures and precipitation are tables of mean monthly temperature and
-- precipitation. Or temperatures can be a table containing a table of monthly
-- mean of daily highs and monthly mean of daily lows.
-- Units: °C, mm.
function p.Koeppen(temperatures, precipitation, Southern_Hemisphere)
local temperature_count = #temperatures
if temperature_count == 2 then
local highs_and_lows = temperatures
temperatures = mean_of_highs_and_lows(unpack(temperatures))
elseif temperature_count ~= 12 then
errorf("Wrong number of temperatures (expected 12, got %d)",
temperature_count)
elseif #precipitation ~= 12 then
errorf("Wrong number of precipitation stats (expected 12, got %d)",
#precipitation)
end
local min_month_temp, max_month_temp = min_and_max(temperatures)
local first_summer_month, last_summer_month
if Southern_Hemisphere then
first_summer_month, last_summer_month = 10, 3 -- October to March
else
first_summer_month, last_summer_month = 4, 9 -- April to September
end
local mean_temp = mean(temperatures)
local total_precip = sum(precipitation)
local total_summer_precip = sum(precipitation, first_summer_month, last_summer_month)
local summer_precip_fraction = total_summer_precip / total_precip
local threshold = mean_temp * 20
+ (summer_precip_fraction >= 0.7 and 280
or summer_precip_fraction >= 0.3 and 140
or 0)
if total_precip <= threshold then
return "B"
.. (total_precip < threshold / 2 and "W" or "S") -- arid, semi-arid
.. (mean_temp < 0 and "k" or "h")
end
local first_letter =
max_month_temp < 10 and "E"
or min_month_temp < 0 and "D"
or min_month_temp >= 18 and "A"
or "C"
if first_letter == "E" then
return first_letter .. (max_month_temp < 0 and "F" or "T")
elseif first_letter == "A" then
local min_precip_index = min_index(precipitation)
local min_month_precip = precipitation[min_precip_index]
return first_letter
.. (min_month_precip >= 60 and "f"
or min_month_precip / total_precip > 0.04 and "m"
or in_range(min_precip_index, first_summer_month, last_summer_month) and "s"
or "w")
else
local min_summer_precip, max_summer_precip =
min_and_max(precipitation, first_summer_month, last_summer_month)
local min_winter_precip, max_winter_precip =
min_and_max(precipitation, last_summer_month + 1, first_summer_month - 1)
local second_letter =
-- Alternative: total_precip / total_summer_precip >= 0.7
max_summer_precip > max_winter_precip * 10 and "w"
or min_summer_precip < 30 and max_winter_precip > min_summer_precip * 3
and "s"
or "f"
local months_above_10 = count(
function (temperature)
return temperature > 10
end,
temperatures)
local third_letter
if months_above_10 <= 3 then
if min_month_temp < -38 then
third_letter = "d"
else
third_letter = "c"
end
elseif max_month_temp < 22 then
third_letter = "b"
else
third_letter = "a"
end
return first_letter .. second_letter .. third_letter
end
end
local function gather_numbers(str)
local arr = {}
local i = 0
for number in str:gmatch('%-?%d+%.?%d*') do
i = i + 1
arr[i] = tonumber(number)
end
return arr
end
function p.Koeppen_template(frame)
local temperature_string = frame.args[1]
local precipitation_string = frame.args[2]
local temperatures, precipitation =
gather_numbers(temperature_string), gather_numbers(precipitation_string)
local Southern_Hemisphere = require 'Module:yesno' (frame.args[3])
return p.Koeppen(temperatures, precipitation, Southern_Hemisphere)
end
return p