Jump to content

Module:Climate chart

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Izno (talk | contribs) at 06:09, 8 June 2023 (add a month row). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

local p = {}
-- 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 mm_to_in(precipitation_in_mm)
	return precipitation_in_mm / 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(low_temp, high_temp, precipitation, max_precipitation, unit_system)
	
	local precipitation = precipitation or 80
	local base_precipitation = max_precipitation or 500
	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 = 0
	if precipitation_in_inches < 10 then decimal_places = 1 end
	local rounded_precipitation_inches = round(precipitation_in_inches, decimal_places)
	
	local low_temp = low_temp or 10
	local high_temp = high_temp 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 high_temp_f = c_to_f(high_temp)
	local rounded_high_temp_f = round(high_temp_f, 0)
	local high_temp_sign = ''
	if rounded_high_temp_f < 0 then high_temp_sign = '&minus;' end
	local abs_high_temp = math.abs(rounded_high_temp_f)
	
	local low_temp_f = c_to_f(low_temp)
	local rounded_low_temp_f = round(low_temp_f, 0)
	local low_temp_sign = ''
	if rounded_low_temp_f < 0 then low_temp_sign = '&minus;' 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('&nbsp;')
		:done()
	:tag('div')
		:addClass('climate-chart-column-precip-bar')
		:wikitext('&nbsp;')
		:css('height', precipitation_bar_height .. 'em')
		:css('print-color-adjust', 'exact') -- css sanitizer doesn't accept yet
		:done()
	:tag('div')
		:addClass('climiate-chart-column-precip')
		:tag('span')
			:wikitext(rounded_precipitation_inches)
			:done()
		:done()
	:tag('div')
		:addClass('climate-chart-column-spacer2')
		:wikitext('&nbsp;')
		:done()
	:tag('div')
		:addClass('climate-chart-column-temp-bar')
		:wikitext('&nbsp;')
		: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-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-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 months = { 'J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D' }
	
	local month_row = mw.html.create('tr')
	for month in ipairs(months) do
		rows: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

-- TODO: implement
local function table_insanity(args, imperial)
	local primary = mw.html.create('table')
		:addClass('climate-chart-primary climate-chart-internal')
		:node(header_row())
		:done()
		
	local secondary_chart = mw.html.create('div')
		:addClass('climate-chart-secondary climate-chart-internal')
		:node(header_row())
		:done()
	
	local secondary_title = imperial and 'Metrix conversion' or 'Imperial conversion'
	-- 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('█')
			: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('Source: %s', 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('Climate chart ([[Template:Climate chart/How to read a climate chart|explanation]])')
		:done()
	return ret
end

function p._main(args)
	
	local float = arg_or_default(args, '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, 'clear', nil) or float
	local width = arg_or_default(args, '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, 'units', ''))
	local is_imperial_primary = units == 'imperial' and true or false
	local title = arg_or_default(args, '1', '')
	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',
		' Average max. and min. temperatures in °F',
		' Average max. and min. temperatures in °C'
	)
	local primary_precip_explanation, secondary_precip_explanation = explain(
		is_imperial_primary,
		'precip',
		' Precipitation totals in inches',
		' Precipitation totals in 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