Module:Climate chart/sandbox 1

local p = {}

local cfg = mw.loadData('Module:Climate chart/configuration')

-- from https://lua-users.org/wiki/SimpleRound

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

-- we only draw using the metric numbers

local function compute_column_draw_data(metric_year, max_precipitation)

local column_draw_data = {}

-- so many magic constants

local precipitation_scale = math.max(1, max_precipitation / 750) -- 750 mm is the maximum for height

for _, month in ipairs(metric_year) do

local precipitation_bar_height = month.precipitation / 50 / precipitation_scale -- 50 is a magic constant

local temperature_bar_displacement = month.minimum / 5 + 8

local temperature_bar_height = (month.maximum - month.minimum) / 5

local temperature_high_displacement = month.maximum / 5 + 8

local temperature_low_displacement = month.minimum / 5 + 6.5

table.insert(column_draw_data, {

precipitation_height = precipitation_bar_height,

temperature_height = temperature_bar_height,

temperature_displacement = temperature_bar_displacement,

temperature_high_displacement = temperature_high_displacement,

temperature_low_displacement = temperature_low_displacement

})

end

return column_draw_data

end

local function present_monthly_temperature(temperature)

local rounded_temp = round(temperature, 0)

local temperature_sign = ''

if rounded_temp < 0 then temperature_sign = '−' end

local abs_temp = math.abs(rounded_temp)

return temperature_sign .. abs_temp

end

local function draw_column(month_draw_data, month_data)

local precipitation = month_data.precipitation

local precipitation_decimal_places = precipitation < 10 and 1 or 0

local rounded_precipitation = round(precipitation, precipitation_decimal_places)

local high_temp = present_monthly_temperature(month_data.maximum)

local low_temp = present_monthly_temperature(month_data.minimum)

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', month_draw_data.precipitation_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)

:done()

:done()

:tag('div')

:addClass('climate-chart-column-spacer2')

:wikitext(' ')

:done()

:tag('div')

:addClass('climate-chart-column-temp-bar')

:wikitext(' ')

:css('bottom', month_draw_data.temperature_displacement .. 'em' )

:css('height', month_draw_data.temperature_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', month_draw_data.temperature_high_displacement .. 'em')

:tag('span')

:wikitext(high_temp)

:done()

:done()

:tag('div')

:addClass('climate-chart-column-value climate-chart-column-low-temp')

:css('bottom', month_draw_data.temperature_low_displacement .. 'em')

:tag('span')

:wikitext(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 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 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

local function convert_inplace(t, convert_temperature, convert_precipitation)

for _, month in ipairs(t) do

month.minimum = convert_temperature(month.minimum)

month.maximum = convert_temperature(month.maximum)

month.precipitation = convert_precipitation(month.precipitation)

end

end

local function fill_in_nils(year_t, default_t)

for _, month in ipairs(year_t) do

if not month.precipitation then month.precipitation = default_t.precipitation end

if not month.maximum then month.maximum = default_t.temperature_high end

if not month.minimum then month.minimum = default_t.temperature_low end

end

end

local function chart_rows(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)

fill_in_nils(metric_t, cfg.metric_default)

fill_in_nils(imperial_t, cfg.imperial_default)

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)

fill_in_nils(metric_t, cfg.metric_default)

fill_in_nils(imperial_t, cfg.imperial_default)

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

local column_draw_data = compute_column_draw_data(metric_t, metric_max_precipitation)

local metric_row = mw.html.create('tr')

local imperial_row = mw.html.create('tr')

local function add_columns(row, year)

for i = 1, 12 do

row:tag('td')

:node(draw_column(column_draw_data[i], year[i]))

:done()

end

end

add_columns(metric_row, metric_t)

add_columns(imperial_row, imperial_t)

return metric_row, imperial_row

end

local function present_chart_content(args, imperial)

local primary_row, secondary_row

if imperial then

secondary_row, primary_row = chart_rows(args, imperial)

else

primary_row, secondary_row = chart_rows(args, imperial)

end

local primary = mw.html.create('table')

:addClass('climate-chart-primary climate-chart-internal')

:node(header_row())

:node(primary_row)

:done()

local secondary_chart = mw.html.create('table')

:addClass('climate-chart-secondary climate-chart-internal')

:node(header_row())

:node(secondary_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 units = string.lower(arg_or_default(args, cfg.arg.units, ''))

local is_imperial_primary = units == cfg.keyword.imperial

local title = add_title_content(arg_or_default(args, cfg.arg.title, ''))

local primary_chart, secondary_chart_content = present_chart_content(

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 = add_source(arg_or_default(args, 'source', nil))

local secondary_content = wrap_secondary_content(

secondary_chart_content,

secondary_temp_explanation,

secondary_precip_explanation

)

local climate_chart = mw.html.create('div')

climate_chart:addClass('climate-chart')

:addClass(float_class)

:css('clear', clear)

climate_chart:node(title)

:node(primary_chart)

:node(primary_temp_explanation)

:node(primary_precip_explanation)

:node(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