Module:Climate chart
Appearance
![]() | This module is rated as ready for general use. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by sandbox testing rather than repeated trial-and-error editing. |
![]() | This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
![]() | This module depends on the following other modules: |
![]() | This module uses TemplateStyles: |
Implements {{climate chart}}. At some point.
Usage
{{#invoke:Climate chart|function_name}}
local p = {}
local cfg = mw.loadData('Module:Climate chart/configuration')
-- from https://lua-users.org/wiki/SimpleRound
local function round(num, decimal_places)
local mult = 10^(decimal_places or 0)
return math.floor(num * mult + 0.5) / mult
end
local function c_to_f(temperature_in_c)
return temperature_in_c * 1.8 + 32
end
local function f_to_c(temperature_in_f)
return (temperature_in_f - 32) * 5/9
end
local function mm_to_in(precipitation_in_mm)
return precipitation_in_mm / 25.4
end
local function in_to_mm(precipitation_in_in)
return precipitation_in_in * 25.4
end
-- this implements the fahrenheit subtemplate, will need to look into what the
-- celsius template is doing
-- unit_system currently unused accordingly
local function month_column(month, max_precipitation, unit_system)
local precipitation = month.precipitation or 80
local base_precipitation = max_precipitation
local precipitation_scale = math.max(1, base_precipitation / 750) -- 750 mm is the maximum for height
local precipitation_bar_height = precipitation / 50 / precipitation_scale -- 50 is a magic constant
local precipitation_in_inches = precipitation / 25.4
local decimal_places = precipitation_in_inches < 10 and 1 or 0
local rounded_precipitation_inches = round(precipitation_in_inches, decimal_places)
local low_temp = month.minimum or 10
local high_temp = month.maximum or 20
local low_temp_shift_for_bar = low_temp / 5 + 8 -- magic numbers
local temp_bar_height = (high_temp - low_temp) / 5 -- magic numbers
local high_temp_shift = high_temp / 5 + 8
local low_temp_shift = low_temp / 5 + 6.5
local rounded_high_temp_f = round(high_temp_f, 0)
local high_temp_sign = ''
if rounded_high_temp_f < 0 then high_temp_sign = '−' end
local abs_high_temp = math.abs(rounded_high_temp_f)
local rounded_low_temp_f = round(low_temp_f, 0) --presentation
local low_temp_sign = ''
if rounded_low_temp_f < 0 then low_temp_sign = '−' end
local abs_low_temp = math.abs(rounded_low_temp_f)
local column = mw.html.create('div')
column:addClass('climate-chart-column')
:tag('div')
:addClass('climate-chart-column-spacer')
:wikitext(' ')
:done()
:tag('div')
:addClass('climate-chart-column-precip-bar')
:wikitext(' ')
:css('height', precipitation_bar_height .. 'em')
:css('print-color-adjust', 'exact') -- css sanitizer doesn't accept yet
:done()
:tag('div')
:addClass('climate-chart-column-value climate-chart-column-precip')
:tag('span')
:wikitext(rounded_precipitation_inches)
:done()
:done()
:tag('div')
:addClass('climate-chart-column-spacer2')
:wikitext(' ')
:done()
:tag('div')
:addClass('climate-chart-column-temp-bar')
:wikitext(' ')
:css('bottom', low_temp_shift_for_bar .. 'em' )
:css('height', temp_bar_height .. 'em')
:css('print-color-adjust', 'exact') -- css sanitizer doesn't accept yet
:done()
:tag('div')
:addClass('climate-chart-column-value climate-chart-column-high-temp')
:css('bottom', high_temp_shift .. 'em')
:tag('span')
:wikitext(high_temp_sign .. abs_high_temp)
:done()
:done()
:tag('div')
:addClass('climate-chart-column-value climate-chart-column-low-temp')
:css('bottom', low_temp_shift .. 'em')
:tag('span')
:wikitext(low_temp_sign .. abs_low_temp)
:done()
:done()
:done()
return column
end
local function header_row()
local month_row = mw.html.create('tr')
for _, month in ipairs(cfg.i18n.months) do
month_row:tag('th')
:attr('scope', 'col')
:wikitext(month)
:done()
end
return month_row:allDone()
end
local function arg_or_default(args, from_arg, default)
local arg = mw.text.trim(args[from_arg] or '')
if arg ~= '' then
return arg
else
return default
end
end
local function fill_nice_tables(args)
local primary_table = {}
local secondary_table = {}
for n = 2, 37, 3 do
local minimum = tonumber(args[n])
local maximum = tonumber(args[n+1])
local precipitation = tonumber(args[n+2])
-- we use the fact that `tonumber` returns nil if it gets not_a_string
-- _OR_ the empty string later, since the defaults are unit-specific
table.insert(primary_table, {
minimum = minimum,
maximum = maximum,
precipitation = precipitation
})
table.insert(secondary_table, {
minimum = minimum,
maximum = maximum,
precipitation = precipitation
})
end
return primary_table, secondary_table
end
local function convert_inplace(t, convert_temperature, convert_precipitation, imperial)
local defaults = cfg.default
for month in t do
if month.minimum then
month.minimum = convert_temperature(month.minimum)
else
-- TODO???
end
if month.maximum then
month.maximum = convert_temperature(month.maximum)
else
-- TODO???
end
local precipitation = month.precipitation
if month.precipitation then
month.precipitation = convert_precipitation(month.precipitation)
else
-- TODO???
end
end
end
local function chart_row(args, imperial)
local metric_t
local imperial_t
local maximum_precipitation = tonumber(args.maxprecip)
local imperial_max_precipitation
local metric_max_precipitation
local default_max_precipitation = 1
if imperial then
imperial_t, metric_t = fill_nice_tables(args)
convert_inplace(metric_t, f_to_c, in_to_mm, imperial)
if maximum_precipitation then
imperial_max_precipitation = maximum_precipitation
metric_max_precipitation = in_to_mm(maximum_precipitation)
else
imperial_max_precipitation = default_max_precipitation
metric_max_precipitation = default_max_precipitation
end
else
metric_t, imperial_t = fill_nice_tables(args)
convert_inplace(imperial_t, c_to_f, mm_to_in, imperial)
if maximum_precipitation then
metric_max_precipitation = maximum_precipitation
imperial_max_precipitation = mm_to_in(maximum_precipitation)
else
metric_max_precipitation = default_max_precipitation
imperial_max_precipitation = default_max_precipitation
end
end
-- make_table_pretty()
-- just has to decide on presentation
make_table_pretty()
-- the HTML can be done based solely on the imperial numbers
-- only thing that needs to be fed is
get_presentation_data()
-- everything all initialized. now what?
local row = mw.html.create('tr')
for i = 1, 12 do
row:tag('td')
:node(month_column(annual_weather[i], maximum_precipitation, imperial))
:done()
end
return row
end
-- TODO: implement
local function table_insanity(args, imperial)
local primary = mw.html.create('table')
:addClass('climate-chart-primary climate-chart-internal')
:node(header_row())
:node(chart_row(args, imperial))
:done()
local secondary_chart = mw.html.create('table')
:addClass('climate-chart-secondary climate-chart-internal')
:node(header_row())
:done()
local secondary_title = imperial and cfg.i18n.secondary_title_metric or cfg.i18n.secondary_title_imperial
-- primary has html_chart
return primary, {
title = secondary_title,
chart = secondary_chart
}
end
local function wrap_secondary_content(chart_content, temp_explanation, precip_explanation)
local ret = mw.html.create('div')
ret:addClass('climate-chart-secondary mw-collapsible mw-collapsed')
:tag('div')
:addClass('climate-chart-secondary-title')
:wikitext(chart_content.title)
:done()
:tag('div')
:addClass('mw-collapsible-content')
:node(chart_content.chart)
:node(temp_explanation)
:node(precip_explanation)
:done()
return ret
end
local function explain_bar(bar_type, text)
local ret = mw.html.create('p')
ret:addClass('climate-change-explain-bar-' .. bar_type)
:tag('span')
:wikitext(cfg.i18n.explainer_key)
:done()
:wikitext(text)
:done()
return ret
end
local function explain(imperial, bar_type, imperial_explanation, metric_explanation)
if imperial then
return explain_bar(bar_type, imperial_explanation),
explain_bar(bar_type, metric_explanation)
else
return explain_bar(bar_type, metric_explanation),
explain_bar(bar_type, imperial_explanation)
end
end
local function add_source(source)
if not source then return end
return mw.html.create('p'):wikitext(string.format(cfg.i18n.source, source))
end
local function add_title_content(title)
local ret = mw.html.create()
ret:tag('div')
:addClass('climate-chart-title')
:wikitext(title)
:done()
:tag('div')
:addClass('climate-chart-explainer')
:wikitext(cfg.i18n.explainer)
:done()
return ret
end
function p._main(args)
local float = arg_or_default(args, cfg.arg.float, nil)
local float_class = nil
if float then
if float == 'right' then
float_class = 'climate-chart-right'
elseif float == 'left' then
float_class = 'climate-chart-left'
end
end
local clear = arg_or_default(args, cfg.arg.clear, nil) or float
local width = arg_or_default(args, cfg.arg.width, nil)
local climate_chart = mw.html.create('div')
climate_chart:addClass('climate-chart')
:css('width', width)
:css('clear', clear)
local units = string.lower(arg_or_default(args, cfg.arg.units, ''))
local is_imperial_primary = units == cfg.keyword.imperial and true or false
local title = arg_or_default(args, cfg.arg.title, '')
local title_content = add_title_content(title)
local primary_chart, secondary_chart_content = table_insanity(
args,
is_imperial_primary
)
local primary_temp_explanation, secondary_temp_explanation = explain(
is_imperial_primary,
'temp',
cfg.i18n.explainer_fahrenheit,
cfg.i18n.explainer_celsius
)
local primary_precip_explanation, secondary_precip_explanation = explain(
is_imperial_primary,
'precip',
cfg.i18n.explainer_in,
cfg.i18n.explainer_mm
)
local source = arg_or_default(args, 'source', nil)
local wrapped_source = add_source(source)
local secondary_content = wrap_secondary_content(
secondary_chart_content,
secondary_temp_explanation,
secondary_precip_explanation
)
climate_chart:node(title_content)
:node(primary_chart)
:node(primary_temp_explanation)
:node(primary_precip_explanation)
:node(wrapped_source)
:node(secondary_content)
:allDone()
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Climate chart/styles.css' }
} .. tostring(climate_chart)
end
function p.main(frame)
return p._main(frame:getParent().args)
end
return p