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:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
- Lua error in package.lua at line 80: module 'Module:Climate/stats' not found.
local p = {}
local Stats = require "Module:Climate/stats"
local use_alt_CD_isotherm = false
local use_alt_hk_isotherm = false
local use_alt_w_isotherm = false
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 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 get_aridity_threshold(mean_temp, total_precip, total_summer_precip)
local summer_precip_fraction = total_summer_precip / total_precip
return mean_temp * 20
+ (summer_precip_fraction >= 0.7 and 280
or summer_precip_fraction >= 0.3 and 140
or 0)
end
-- in C: cond ? a : b
-- in Python: a if cond else b
local function ternary(cond, a, b)
if cond then
return a
else
return b
end
end
local function check_temperatures_and_precipitation(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
temperatures = Stats(temperatures, Southern_Hemisphere)
precipitation = Stats(precipitation, Southern_Hemisphere)
return temperatures, precipitation
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, location)
temperatures, precipitation =
check_temperatures_and_precipitation(temperatures, precipitation, Southern_Hemisphere)
-- E takes precedence over B, B over A, C, D:
-- http://hanschen.org/koppen/
if temperatures.max.value < 0 then
return "EF"
elseif temperatures.max.value < 10 then
return "ET"
end
local aridity_threshold =
get_aridity_threshold(temperatures.mean, precipitation.sum, precipitation.summer_sum)
if precipitation.sum <= aridity_threshold then
return "B"
.. (precipitation.sum > aridity_threshold / 2 and "S" or "W") -- semi-arid, arid
.. (ternary(use_alt_hk_isotherm, temperatures.mean >= 18,
temperatures.min.value > 0)
and "h" or "k")
end
local first_letter =
temperatures.min.value >= 18 and "A"
or temperatures.min.value > (use_alt_CD_isotherm and -3 or 0) and "C"
or "D"
if first_letter == "A" then
return first_letter
.. (precipitation.min.value >= 60 and "f"
or precipitation.min.value / precipitation.sum > 0.04 and "m"
or in_range(precipitation.min.index, unpack(precipitation.summer_months))
and "s"
or "w")
else
local second_letter =
ternary(use_alt_w_isotherm, precipitation.sum / precipitation.summer_sum >= 0.7,
precipitation.summer_max.value > precipitation.winter_min.value * 10)
and "w"
or precipitation.summer_min.value < 30
and precipitation.winter_max.value > precipitation.summer_min.value * 3
and "s"
or "f"
local third_letter
if temperatures.above_10 <= 3 then
if temperatures.min.value < -38 then
third_letter = "d"
else
third_letter = "c"
end
elseif temperatures.max.value < 22 then
third_letter = "b"
else
third_letter = "a"
end
return first_letter .. second_letter .. third_letter
end
end
function p.Trewartha(temperatures, precipitation, Southern_Hemisphere)
temperatures, precipitation =
check_temperatures_and_precipitation(temperatures, precipitation, Southern_Hemisphere)
if temperatures.max.value < 0 then
return "Fi"
elseif temperatures.max.value < 10 then
return "Ft"
end
-- according to Wikipedia article
local aridity_threshold =
10 * (temperature.mean - 10) + 3 * precipitation.summer_sum / precipitation.sum
if precipitation.sum < aridity_threshold then
return "BW"
elseif precipitation.sum < aridity_threshold * 2 then
return "BS"
end
if temperature.min >= 18 then
if precipitation.below_60 <= 2 then
return "Ar"
elseif in_range(precipitation.min.index, unpack(precipitation.winter_months)) then
return "Aw"
else
return "As"
end
elseif temperature.above_10 >= 8 then
return "C" -- TODO: Cf, Cs, Cw; a, b, c
elseif temperature.above_10 >= 4 then
if temperature.max.value > 0 then -- TODO: a, b, c?
return "Do"
else
return "Dc"
end
else
if temperature.min.value > -10 then
return "Eo"
else
return "Ec"
end
end
-- H excluded
-- Universal Thermal Scale?
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.example(frame)
local args = frame.args
local temperature_string = args[1]
local precipitation_string = args[2]
local temperatures, precipitation =
gather_numbers(temperature_string), gather_numbers(precipitation_string)
local yesno = require 'Module:yesno'
local Southern_Hemisphere = yesno(args[3])
if yesno(args.alt_CD) then
use_alt_CD_isotherm = true
end
if yesno(args.alt_hk) then
use_alt_hk_isotherm = true
end
if yesno(args.alt_w) then
use_alt_w_isotherm = true
end
local location = args.location
local result = p.Koeppen(temperatures, precipitation, Southern_Hemisphere, location)
-- mw.logObject{ temperatures = temperatures, precipitation = precipitation }
return ("[%s %s]: %s"):format(args.url, location, result)
end
local function convert(values, to, from)
if to == from then
errorf("Cannot convert from %s to %s", from, to)
elseif to == "C" and from == "F" then
return map(function(value) return (value - 32) * 5/9 end, values)
elseif to == "F" and from == "C" then
return map(function(value) return (value * 9/5) + 32 end, values)
elseif to == "mm" then
if from == "inch" then
return map(function(value) return value * 254 end, values)
elseif from == "cm" then
return map(function(value) return value * 10 end, values)
end
end
errorf("Conversion from %s to %s not implemented", from, to)
end
local month_to_number = require "Module:Table".invert {
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
}
setmetatable(month_to_number, {
__index = function (self, key)
errorf("Month %s not recognized", key)
end
})
local precipitation_units = require "Module:Table".listToSet {
"inch", "cm", "mm"
}
function p.Weather_box_Koeppen(frame)
local temperatures, precipitation = {}, {}
local args = frame:getParent().args
for k, v in pairs(args) do
local month, unit = arg:match("(%u%l%l) mean ([CF])")
if month then
if temperatures.unit and temperatures.unit ~= unit then
error("Mean monthly temperature given in two different units, "
.. "%s and %s", temperatures.unit, unit)
end
temperatures.unit = unit
temperatures[month_to_number[month]] = tonumber(v)
or errorf("Value of parameter '|%s=%s' cannot be parsed as a number",
k, v)
else
month, unit = arg:match("(%u%l%l) precipitation (%l+)")
if month and precipitation_units[unit] then
if precipitation.unit and precipitation.unit ~= unit then
errorf("Mean monthly precipitation given in two different units, "
.. "%s and %s", precipitation.unit, unit)
end
precipitation[month_to_number[month]]
= tonumber(v)
or errorf("Value of parameter '|%s=%s' cannot be parsed as a number",
k, v)
end
end
end
if temperatures.unit ~= "C" then
temperatures = convert(temperatures, "C", temperatures.unit)
end
if precipitation.unit ~= "mm" then
precipitation = convert(precipitation, "mm", precipitation.unit)
end
local Southern_Hemisphere = require "Module:Yesno" (args.south)
return p.Koeppen(temperatures, precipitation, Southern_Hemisphere)
end
return p