From Wikipedia, the free encyclopedia

  1. ^ "Alturas,California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  2. ^ "Bakersfield Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  3. ^ "Bishop Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  4. ^ "Bodie, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  5. ^ "Death Valley". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  6. ^ "Eureka WFO Woodley Island, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  7. ^ "Fresno Yosemite International Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  8. ^ "Los Angeles Downtown University of Southern California Campus, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  9. ^ "Needles Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  10. ^ "Redding Municipal Airport". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  11. ^ "Riverside Fire Station 3, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  12. ^ "Sacramento Executive Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  13. ^ "San Diego Lindbergh Field, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  14. ^ "San Francisco Downtown, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  15. ^ "San Jose". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  16. ^ "Santa Rosa, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  17. ^ "South Lake Tahoe Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  18. ^ Ross, Douglas (1971–2000). "Daily Normals for Alexandria, Minnesota". NCDC. Retrieved 2007-06-07.
  19. ^ Ross, Douglas (1971–2000). "Daily Normals for Brainerd, Minnesota". NCDC. Retrieved 2007-12-27.
  20. ^ Ross, Douglas (1971–2000). "Daily Normals for Duluth, Minnesota". NCDC. Retrieved 2007-06-07.
  21. ^ Ross, Douglas (1971–2000). "Daily Normals for Grand Marais, Minnesota". NCDC. Retrieved 2007-12-27.
  22. ^ Ross, Douglas (1971–2000). "Daily Normals for International Falls, Minnesota". NCDC. Retrieved 2007-06-07.
  23. ^ Ross, Douglas (1971–2000). "Daily Normals for Redwood Falls, Minnesota". NCDC. Retrieved 2007-06-07.
  24. ^ Ross, Douglas (1971–2000). "Daily Normals for Thief River Falls, Minnesota". NCDC. Retrieved 2007-06-07.
  25. ^ Ross, Douglas (1971–2000). "Daily Normals for the Twin Cities". NCDC. Retrieved 2007-06-07.
  26. ^ Ross, Douglas (1971–2000). "Daily Normals for Winona, Minnesota". NCDC. Retrieved 2007-06-07.
  27. ^ Ross, Douglas (1971–2000). "Daily Normals for Worthington, Minnesota". NCDC. Retrieved 2007-06-07.

f = {}

degree = "°" -- used by add_unit_names()

minus = "−" -- used by makeRow() and makeTable()

thinSpace = mw.ustring.char(0x2009) -- used by makeCell()



-- Error message handling

message = ""



local function add_message(new_message)

	if show then

		if check_for_string(message) then

			message = message .. " " .. new_message

		else

			message = "Notices: " .. new_message

		end

	end

end



-- Input and output parameters

local function get_format (frame)

	local input_parameter = frame.args.input

	local output_parameter = frame.args.output

	

	if input_parameter == nil then

		error("Please provide the number of values and a unit in the input parameter")

	else

		length = tonumber(string.match(input_parameter, "(%d+)")) -- Find digits in the input parameter.

		input_unit = string.match(input_parameter, "([CF])") -- C or F

		if string.find(input_parameter, "[^CF%d%s]") then

			add_message("There are extraneous characters in the <span style=\"background-color: #EEE; font-family: monospace;\">output</span> parameter.")

		end

	end

	

	if input_unit == "C" then

		output_unit = "F"

	elseif input_unit == "F" then

		output_unit = "C"

	else

		error ("Please provide an input unit in the input parameter: F for Fahrenheit or C for Celsius", 0)

	end

	

	if length == nil then

		error ("get_format has not found a length value in the input parameter")

	end

	

	if output_parameter == nil then

		add_message("No output format has been provided in the <span style=\"background-color: #EEE; font-family: monospace;\">output</span> parameter.")

	else

		cell_format = {}

		local n = 1

		for unit in output_parameter:gmatch("[CF]") do

			cell_formatn = unit

			n = n + 1

			if n > 2 then

				break

			end

		end

		local function set_format(key, formatVariable, formatValue1, formatValue2)

			if string.find(output_parameter, key) then

				cell_formatformatVariable = formatValue1

			else

				cell_formatformatVariable = formatValue2

			end

		end

		if cell_format1 then

			cell_format.first = cell_format1

		else

			error("C or F not found in output parameter")

		end

		if cell_format2 == nil then

			cell_format"convert_units" = "no"

		else

			if cell_format2 == cell_format1 then

				error("There should not be two of the same unit name in the output parameter.")

			else

				cell_format"convert_units" = "yes"

			end

		end

		set_format("unit", "unit_names", "yes", "no")

		set_format("no ?color", "color", "no", "yes")

		set_format("sort", "sortable", "yes", "no")

		set_format("full ?size", "small_font", "no", "yes")

		set_format("no ?brackets", "brackets", "no", "yes")

		set_format("round", "decimals", "0", "")

		if string.find(output_parameter, "line break") then

			cell_format"line_break" = "yes"

		elseif string.find(output_parameter, "one line") then

			cell_format"line_break" = "no"

		else

			cell_format"line_break" = "auto"

		end

		if string.find(output_parameter, "one line") and string.find(output_parameter, "line break") then

			error("Place either \"one line\" or \"line break\" in the output parameter, not both")

		end

	end

	if frame.args.palette == nil then

		palette = "cool2avg"

	else

		palette = frame.args.palette

	end

	

	if frame.args.messages == "show" then

		show = true

	else

		show = false

	end

	

	return length, input_unit, output_unit

end



-- Number and string-handling functions

local function check_for_number(value)

	return type(tonumber(value)) == "number"

end



function check_for_string(string)

	string = tostring(string)

	return string ~= "" and string ~= nil

end



local function round(value, decimals)

	value = tonumber(value)

	if type(value) == "number" then

		local string = string.format("%." .. decimals .. "f", value)

		return string

	elseif value == nil then

		value = "nil"

		add_message("Format was asked to operate on " .. value .. ", which cannot be converted to a number.", 2)

		return ""

	end

end



local function convert(value, decimals, unit) -- Unit is the unit being converted from. It defaults to input_unit.

	if not unit then

		unit = input_unit

	end

	if check_for_number(value) then

		local value = tonumber(value)

		if unit == "C" then

			add_message(value .. " " .. degree .. unit .. " was converted.")

			return round(value * 9/5 + 32, decimals)

		elseif unit == "F" then

			add_message(value .. " " .. degree .. unit .. " was converted.")

			return round((value - 32) * 5/9, decimals)

		else

			error("Input unit not recognized", 2)

		end

	else

		return "" -- Setting result to empty string if value is not a number avoids concatenation errors.

	end

end



-- Input parsing

function make_array(parameter, array, frame)

	local array = {}

	local n = 1

	for number in parameter:gmatch("%-?%d+%.?%d?") do

		local number = number

		if n == 1 then

			local decimals = number:match("%.(%d+)")

			if decimals == nil then

				precision = "0"

			else

				precision = #decimals

			end

		end

		table.insert(array, n, number)

		n = n + 1

		if n > length then

			break

		end

	end

	if not arraylength then

		add_message("There are not " .. length .. " values in the " .. parameter .. " parameter.")

	end

	return array, precision

end



function make_arrays(frame)

	get_format(frame)

	local parameter_a = frame.args.a

	local parameter_b = frame.args.b

	local parameter_c = frame.args.c

	if parameter_a then

		a = make_array(parameter_a, a, frame)

	else

		error("Please provide a set of numbers in parameter a")

	end

	if parameter_b then

		b = make_array(parameter_b, b, frame)

	else

		add_message("There is no content in parameter <span style=\"background-color: #EEE; font-family: monospace;\">b</span>.")

	end

	if parameter_c then

		c = make_array(parameter_c, c, frame)

	else

		add_message("There is no content in parameter <span style=\"background-color: #EEE; font-family: monospace;\">c</span>.")

	end

	return a, b, c

end



-- Color generation



palettes = {

	-- The first three arrays in each palette defines background color using a table of four numbers,

	-- say { 11, 22, 33, 44 } (values in °C).

	-- That means the color is 0 below 11 and above 44, and is 255 from 22 to 33.

	-- The color rises from 0 to 255 between 11 and 22, and falls between 33 and 44.

	cool = {

		{ -42.75,   4.47, 41.5, 60   },

		{ -42.75,   4.47,  4.5, 41.5 },

		{ -90   , -42.78,  4.5, 23   },

		white = { -23.3, 37.8 },

	},

	cool2 = {

		{ -42.75,   4.5 , 41.5, 56   },

		{ -42.75,   4.5 ,  4.5, 41.5 },

		{ -90   , -42.78,  4.5, 23   },

		white = { -23.3, 35 },

	},

	cool2avg = {

		{ -38,   4.5, 25  , 45   },

		{ -38,   4.5,  4.5, 30   },

		{ -70, -38  ,  4.5, 23   },

		white = { -23.3, 25 },

	},

}



local function temperature_color(palette, value, out_rgb)

	--[[ Return style for a table cell based on the given value which

		should be a temperature in °C. ]]

	local background_color, text_color

	value = tonumber(value)

	if value == nil then

		background_color, text_color = 'FFF', '000'

		add_message("Value supplied to <span style=\"background-color: #EEE; font-family: monospace;\">temperature_color</span> is not recognized.")

	else

		local min, max = unpack(palette.white or { -23, 35 })

		if value < min or value >= max then

			text_color = 'FFF'

		else

			text_color = '' -- This assumes that black text color is the default for most readers.

		end



		local background_rgb = out_rgb or {}

		for i, v in ipairs(palette) do

			local a, b, c, d = unpack(v)

			if value <= a then

				background_rgbi = 0

			elseif value < b then

				background_rgbi = (value - a) * 255 / (b - a)

			elseif value <= c then

				background_rgbi = 255

			elseif value < d then

				background_rgbi = 255 - ( (value - c) * 255 / (d - c) )

			else

				background_rgbi = 0

			end

		end

		background_color = string.format('%02X%02X%02X', background_rgb1], background_rgb2], background_rgb3])

	end

	if text_color == "" then

		return background_color

	else

		return background_color, text_color

	end

end



local function color_CSS(background_color, text_color)

	if background_color and text_color then

		return 'background: #' .. background_color .. '; color: #' .. text_color .. ';'

	elseif background_color then

		return 'background: #' .. background_color .. ';'

	else

		return ''

	end

end



function temperature_CSS(value, unit, palette)

	local palette = palettespalette or palettes.cool

	local value = tonumber(value)

	if value == nil then

		error("The function <span style=\"background-color: #EEE; font-family: monospace;\">temperature_CSS</span> is receiving a nil value")

	else

		if unit == 'C' then

			return color_CSS(temperature_color(palette, value))

		elseif unit == 'F' then

			return color_CSS(temperature_color(palette, convert(value, decimals, 'F')))

		else

			unit_error(unit or "nil")

		end

	end

end



--[[ ==== Cell, row, table generation ==== ]]

local output_formats = {

	high_low_average_F =

		{ first = "F",

		convert_units = "yes",

		unit_names = "no",

		color = "yes",

		small_font = "yes",

		sortable = "yes",

		decimals = "0",

		brackets = "yes",

		line_break = "auto", },

	high_low_average_C =

		{ first = "C",

		convert_units = "yes",

		unit_names = "no",

		color = "yes",

		small_font = "yes",

		sortable = "yes",

		decimals = "0",

		brackets = "yes",

		line_break = "auto", },

	high_low_F =

		{ first = "F",

		convert_units = "yes",

		unit_names = "no",

		color = "no",

		small_font = "yes",

		sortable = "no",

		decimals = "",

		brackets = "yes",

		line_break = "auto", },

	high_low_C =

		{ first = "C",

		convert_units = "yes",

		unit_names = "no",

		color = "no",

		small_font = "yes",

		sortable = "no",

		decimals = "0",

		brackets = "yes",

		line_break = "auto", },

	average_F =

		{ first = "F",

		convert_units = "yes",

		unit_names = "no",

		color = "yes",

		small_font = "yes",

		sortable = "no",

		decimals = "0",

		brackets = "yes",

		line_break = "auto", },

	average_C =

		{ first = "C",

		convert_units = "yes",

		unit_names = "no",

		color = "yes",

		small_font = "yes",

		sortable = "no",

		decimals = "0",

		brackets = "yes",

		line_break = "auto", },

	}



local function add_unit_names(value, unit)

	if not unit then unit = input_unit end

	if output_format.unit_names == "yes" then

		if check_for_string(value) then

			return value .. "&nbsp;" .. degree .. unit

		else

			return value -- Don't add a unit name to an empty string

		end

	else

		return value

	end

end



local function if_yes(parameter, realization1, realization2)

	if realization1 then

		if realization2 then

			if parameter == "yes" then

				parameter = { realization1, realization2 }

			else

				parameter = { "", "" }

			end

		else

			if parameter == "yes" then

				parameter = realization1

			else

				parameter = ""

			end

		end

	else

		parameter = ""

		add_message("<span style=\"background-color: #EEE; font-family: monospace;\">if_yes</span> needs at least one realization")

	end

	return parameter

end



function makeCell(output_format, a, b, c)

	local cell, cell_content = "", ""

	local color_CSS, other_CSS, title_attribute, sortkey, attribute_separator, converted_units_separator = "", "", "", "", "", "", ""

	local style_attribute, high_low_separator, brackets, values, converted_units = {"", ""}, {"", ""}, {"", ""}, {"", ""}, {"", ""}

	

	if check_for_number(output_format.decimals) then

		decimals = output_format.decimals

		--[[ Precision is the number of decimals in the first number of the last array.

			This may be a problem for data from Weatherbase,

			which seems to inappropriately remove .0 from numbers that have it. ]]

	else

		decimals = precision

	end

	

	if check_for_number(b) and check_for_number(a) then

		values, high_low_separator = { round(a, decimals), round(b, decimals) }, { thinSpace .. "/" .. thinSpace, if_yes(output_format.convert_units, thinSpace .. "/" .. thinSpace) }

	elseif check_for_number(a) then

		values = { round(a, decimals), "" }

	elseif check_for_number(c) then

		values = { round(c, decimals), "" }

	end

	if output_format.first == input_unit then

		if output_format.convert_units == "yes" then

			converted_units = { add_unit_names(convert(values1], decimals), output_unit), add_unit_names(convert(values2], decimals), output_unit) }

		end

		values = { add_unit_names(values1]), add_unit_names(values2]) }

	elseif output_format.first == "C" or output_format.first == "F" then

		if output_format.convert_units == "yes" then

			converted_units = { add_unit_names(values1]), add_unit_names(values2]) }

		end

		values = { add_unit_names(convert(values1], decimals), output_unit), add_unit_names(convert(values2], decimals), output_unit) }

	else

		if output_format.first == nil then

			output_format.first = "nil"

		end

		add_message("<span style=\"background-color: #EEE; font-family: monospace;\">" .. output_format.first .. "</span>, the value for <span style=\"background-color: #EEE; font-family: monospace;\">first</span> in <span style=\"background-color: #EEE; font-family: monospace;\">output_format</span> is not recognized.")

	end

	--[[

		Regarding line breaks:

		If there are two values, there will be at least three characters: 9/1.

		If there is one decimal, numbers will be three to five characters long

		and there will be 3 to 10 characters total even without unit conversion:

			1.1, 116.5/88.0.

		If there are units, that adds three characters per number: 25 °C/20 °C.

		In each of these cases, a line break is needed so that table cells are not too wide;

		even more so when more than one of these things are true.

		]]

	if output_format.convert_units == "yes" then

		brackets = if_yes(output_format.brackets, "(", ")" )

		if output_format.line_break == "auto" then

			if check_for_string(values2]) or decimals ~= "0" or output_format.show_units == "yes" then

				converted_units_separator = "<br>"

			else

				converted_units_separator = "&nbsp;"

			end

		elseif output_format.line_break == "yes" then

			converted_units_separator = "<br>"

		elseif output_format.line_break == "no" then

			converted_units_separator = "&nbsp;"

		else

			error("Value for line_break not recognized")

		end

	end

	

	cell_content = values1 .. high_low_separator1 .. values2 .. converted_units_separator .. brackets1 .. converted_units1 .. high_low_separator2 .. converted_units2 .. brackets2

	

	if check_for_number(c) then

		color_CSS = if_yes(output_format.color, temperature_CSS(c, input_unit, palette))

		if check_for_number(b) and check_for_number(a) then

			local attribute_value

			if output_format.first == input_unit then

				attribute_value = c

			else

				attribute_value = convert(c, decimals)

			end

			sortkey = if_yes(output_format.sortable, " data-sort-value=\"" .. attribute_value .. "\"")

			title_attribute = " title=\"Average temperature: " .. attribute_value .. " " .. degree .. output_format.first .. "\""

		end

	elseif check_for_number(b) then

		color_css = ""

	elseif check_for_number(a) then

		color_CSS = if_yes(output_format.color, temperature_CSS(a, input_unit, palette))

	else

		add_message("Neither a nor b nor c are strings.")

	end

	other_CSS = if_yes(output_format.small_font, "font-size: 85%;")

	if check_for_string(color_CSS) or check_for_string(other_CSS) then

		style_attribute = { "style=\"", "\"" }

	end

	

	if check_for_string(other_CSS) or check_for_string(color_CSS) or check_for_string(title_attribute) or check_for_string(sortkey) then

		attribute_separator = " | "

	end

	cell = "\n| " .. style_attribute1 .. color_CSS .. other_CSS .. style_attribute2 .. title_attribute .. sortkey .. attribute_separator .. cell_content

	return cell

end



function f.makeRow(frame)

	make_arrays(frame)

	local output = ""

	if frame.args1 then

		output = "\n|-"

		output = output .. "\n! " .. frame.args1

		if frame.args2 then

			output = output .. " !! " .. frame.args2

		end

	end

	if cell_format then

		output_format = cell_format

	end

	if a and b and c then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.high_low_average_F

			end

			output = output .. makeCell(output_format, ai], bi], ci])

		end

	elseif a and b then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.high_low_F

			end

			output = output .. makeCell(output_format, ai], bi])

		end

	elseif a then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.average_F

			end

			output = output .. makeCell(output_format, ai])

		end

	end

	output = mw.ustring.gsub(output, "([%p%s])-(%d)", "%1" .. minus .. "%2")

	return output

end



function f.makeTable(frame)

	make_arrays(frame)

	local output = "{| class=\"wikitable center nowrap\""

	if cell_format then

		output_format = cell_format

	end

	if a and b and c then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.high_low_average_F

			end

			output = output .. makeCell(output_format, ai], bi], ci])

		end

	elseif a and b then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.high_low_F

			end

			output = output .. makeCell(output_format, ai], bi])

		end

	elseif a then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.average_F

			end

			output = output .. makeCell(output_format, ai])

		end

	end

	output = mw.ustring.gsub(output, "([%p%s])-(%d)", "%1" .. minus .. "%2")

		--[[  Makes sure that hyphens in "data-sort-type" are not replaced with minuses.

				If Lua had (?<=), a capture would not be necessary.  ]]

	output = output .. "\n|}"

	if show then

		output = output .. "\n\n<span style=\"color: red; font-size: 80%; line-height: 100%;\">" .. message .. "</span>"

	end

	return output

end



return f
From Wikipedia, the free encyclopedia

  1. ^ "Alturas,California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  2. ^ "Bakersfield Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  3. ^ "Bishop Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  4. ^ "Bodie, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  5. ^ "Death Valley". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  6. ^ "Eureka WFO Woodley Island, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  7. ^ "Fresno Yosemite International Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  8. ^ "Los Angeles Downtown University of Southern California Campus, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  9. ^ "Needles Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  10. ^ "Redding Municipal Airport". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  11. ^ "Riverside Fire Station 3, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  12. ^ "Sacramento Executive Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  13. ^ "San Diego Lindbergh Field, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  14. ^ "San Francisco Downtown, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  15. ^ "San Jose". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  16. ^ "Santa Rosa, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  17. ^ "South Lake Tahoe Airport, California". National Climatic Data Center (NCDC) 1981-2010 Monthly Normals. Western Regional Climate Center (WRCC), National Oceanic and Atmospheric Administration (NOAA).
  18. ^ Ross, Douglas (1971–2000). "Daily Normals for Alexandria, Minnesota". NCDC. Retrieved 2007-06-07.
  19. ^ Ross, Douglas (1971–2000). "Daily Normals for Brainerd, Minnesota". NCDC. Retrieved 2007-12-27.
  20. ^ Ross, Douglas (1971–2000). "Daily Normals for Duluth, Minnesota". NCDC. Retrieved 2007-06-07.
  21. ^ Ross, Douglas (1971–2000). "Daily Normals for Grand Marais, Minnesota". NCDC. Retrieved 2007-12-27.
  22. ^ Ross, Douglas (1971–2000). "Daily Normals for International Falls, Minnesota". NCDC. Retrieved 2007-06-07.
  23. ^ Ross, Douglas (1971–2000). "Daily Normals for Redwood Falls, Minnesota". NCDC. Retrieved 2007-06-07.
  24. ^ Ross, Douglas (1971–2000). "Daily Normals for Thief River Falls, Minnesota". NCDC. Retrieved 2007-06-07.
  25. ^ Ross, Douglas (1971–2000). "Daily Normals for the Twin Cities". NCDC. Retrieved 2007-06-07.
  26. ^ Ross, Douglas (1971–2000). "Daily Normals for Winona, Minnesota". NCDC. Retrieved 2007-06-07.
  27. ^ Ross, Douglas (1971–2000). "Daily Normals for Worthington, Minnesota". NCDC. Retrieved 2007-06-07.

f = {}

degree = "°" -- used by add_unit_names()

minus = "−" -- used by makeRow() and makeTable()

thinSpace = mw.ustring.char(0x2009) -- used by makeCell()



-- Error message handling

message = ""



local function add_message(new_message)

	if show then

		if check_for_string(message) then

			message = message .. " " .. new_message

		else

			message = "Notices: " .. new_message

		end

	end

end



-- Input and output parameters

local function get_format (frame)

	local input_parameter = frame.args.input

	local output_parameter = frame.args.output

	

	if input_parameter == nil then

		error("Please provide the number of values and a unit in the input parameter")

	else

		length = tonumber(string.match(input_parameter, "(%d+)")) -- Find digits in the input parameter.

		input_unit = string.match(input_parameter, "([CF])") -- C or F

		if string.find(input_parameter, "[^CF%d%s]") then

			add_message("There are extraneous characters in the <span style=\"background-color: #EEE; font-family: monospace;\">output</span> parameter.")

		end

	end

	

	if input_unit == "C" then

		output_unit = "F"

	elseif input_unit == "F" then

		output_unit = "C"

	else

		error ("Please provide an input unit in the input parameter: F for Fahrenheit or C for Celsius", 0)

	end

	

	if length == nil then

		error ("get_format has not found a length value in the input parameter")

	end

	

	if output_parameter == nil then

		add_message("No output format has been provided in the <span style=\"background-color: #EEE; font-family: monospace;\">output</span> parameter.")

	else

		cell_format = {}

		local n = 1

		for unit in output_parameter:gmatch("[CF]") do

			cell_formatn = unit

			n = n + 1

			if n > 2 then

				break

			end

		end

		local function set_format(key, formatVariable, formatValue1, formatValue2)

			if string.find(output_parameter, key) then

				cell_formatformatVariable = formatValue1

			else

				cell_formatformatVariable = formatValue2

			end

		end

		if cell_format1 then

			cell_format.first = cell_format1

		else

			error("C or F not found in output parameter")

		end

		if cell_format2 == nil then

			cell_format"convert_units" = "no"

		else

			if cell_format2 == cell_format1 then

				error("There should not be two of the same unit name in the output parameter.")

			else

				cell_format"convert_units" = "yes"

			end

		end

		set_format("unit", "unit_names", "yes", "no")

		set_format("no ?color", "color", "no", "yes")

		set_format("sort", "sortable", "yes", "no")

		set_format("full ?size", "small_font", "no", "yes")

		set_format("no ?brackets", "brackets", "no", "yes")

		set_format("round", "decimals", "0", "")

		if string.find(output_parameter, "line break") then

			cell_format"line_break" = "yes"

		elseif string.find(output_parameter, "one line") then

			cell_format"line_break" = "no"

		else

			cell_format"line_break" = "auto"

		end

		if string.find(output_parameter, "one line") and string.find(output_parameter, "line break") then

			error("Place either \"one line\" or \"line break\" in the output parameter, not both")

		end

	end

	if frame.args.palette == nil then

		palette = "cool2avg"

	else

		palette = frame.args.palette

	end

	

	if frame.args.messages == "show" then

		show = true

	else

		show = false

	end

	

	return length, input_unit, output_unit

end



-- Number and string-handling functions

local function check_for_number(value)

	return type(tonumber(value)) == "number"

end



function check_for_string(string)

	string = tostring(string)

	return string ~= "" and string ~= nil

end



local function round(value, decimals)

	value = tonumber(value)

	if type(value) == "number" then

		local string = string.format("%." .. decimals .. "f", value)

		return string

	elseif value == nil then

		value = "nil"

		add_message("Format was asked to operate on " .. value .. ", which cannot be converted to a number.", 2)

		return ""

	end

end



local function convert(value, decimals, unit) -- Unit is the unit being converted from. It defaults to input_unit.

	if not unit then

		unit = input_unit

	end

	if check_for_number(value) then

		local value = tonumber(value)

		if unit == "C" then

			add_message(value .. " " .. degree .. unit .. " was converted.")

			return round(value * 9/5 + 32, decimals)

		elseif unit == "F" then

			add_message(value .. " " .. degree .. unit .. " was converted.")

			return round((value - 32) * 5/9, decimals)

		else

			error("Input unit not recognized", 2)

		end

	else

		return "" -- Setting result to empty string if value is not a number avoids concatenation errors.

	end

end



-- Input parsing

function make_array(parameter, array, frame)

	local array = {}

	local n = 1

	for number in parameter:gmatch("%-?%d+%.?%d?") do

		local number = number

		if n == 1 then

			local decimals = number:match("%.(%d+)")

			if decimals == nil then

				precision = "0"

			else

				precision = #decimals

			end

		end

		table.insert(array, n, number)

		n = n + 1

		if n > length then

			break

		end

	end

	if not arraylength then

		add_message("There are not " .. length .. " values in the " .. parameter .. " parameter.")

	end

	return array, precision

end



function make_arrays(frame)

	get_format(frame)

	local parameter_a = frame.args.a

	local parameter_b = frame.args.b

	local parameter_c = frame.args.c

	if parameter_a then

		a = make_array(parameter_a, a, frame)

	else

		error("Please provide a set of numbers in parameter a")

	end

	if parameter_b then

		b = make_array(parameter_b, b, frame)

	else

		add_message("There is no content in parameter <span style=\"background-color: #EEE; font-family: monospace;\">b</span>.")

	end

	if parameter_c then

		c = make_array(parameter_c, c, frame)

	else

		add_message("There is no content in parameter <span style=\"background-color: #EEE; font-family: monospace;\">c</span>.")

	end

	return a, b, c

end



-- Color generation



palettes = {

	-- The first three arrays in each palette defines background color using a table of four numbers,

	-- say { 11, 22, 33, 44 } (values in °C).

	-- That means the color is 0 below 11 and above 44, and is 255 from 22 to 33.

	-- The color rises from 0 to 255 between 11 and 22, and falls between 33 and 44.

	cool = {

		{ -42.75,   4.47, 41.5, 60   },

		{ -42.75,   4.47,  4.5, 41.5 },

		{ -90   , -42.78,  4.5, 23   },

		white = { -23.3, 37.8 },

	},

	cool2 = {

		{ -42.75,   4.5 , 41.5, 56   },

		{ -42.75,   4.5 ,  4.5, 41.5 },

		{ -90   , -42.78,  4.5, 23   },

		white = { -23.3, 35 },

	},

	cool2avg = {

		{ -38,   4.5, 25  , 45   },

		{ -38,   4.5,  4.5, 30   },

		{ -70, -38  ,  4.5, 23   },

		white = { -23.3, 25 },

	},

}



local function temperature_color(palette, value, out_rgb)

	--[[ Return style for a table cell based on the given value which

		should be a temperature in °C. ]]

	local background_color, text_color

	value = tonumber(value)

	if value == nil then

		background_color, text_color = 'FFF', '000'

		add_message("Value supplied to <span style=\"background-color: #EEE; font-family: monospace;\">temperature_color</span> is not recognized.")

	else

		local min, max = unpack(palette.white or { -23, 35 })

		if value < min or value >= max then

			text_color = 'FFF'

		else

			text_color = '' -- This assumes that black text color is the default for most readers.

		end



		local background_rgb = out_rgb or {}

		for i, v in ipairs(palette) do

			local a, b, c, d = unpack(v)

			if value <= a then

				background_rgbi = 0

			elseif value < b then

				background_rgbi = (value - a) * 255 / (b - a)

			elseif value <= c then

				background_rgbi = 255

			elseif value < d then

				background_rgbi = 255 - ( (value - c) * 255 / (d - c) )

			else

				background_rgbi = 0

			end

		end

		background_color = string.format('%02X%02X%02X', background_rgb1], background_rgb2], background_rgb3])

	end

	if text_color == "" then

		return background_color

	else

		return background_color, text_color

	end

end



local function color_CSS(background_color, text_color)

	if background_color and text_color then

		return 'background: #' .. background_color .. '; color: #' .. text_color .. ';'

	elseif background_color then

		return 'background: #' .. background_color .. ';'

	else

		return ''

	end

end



function temperature_CSS(value, unit, palette)

	local palette = palettespalette or palettes.cool

	local value = tonumber(value)

	if value == nil then

		error("The function <span style=\"background-color: #EEE; font-family: monospace;\">temperature_CSS</span> is receiving a nil value")

	else

		if unit == 'C' then

			return color_CSS(temperature_color(palette, value))

		elseif unit == 'F' then

			return color_CSS(temperature_color(palette, convert(value, decimals, 'F')))

		else

			unit_error(unit or "nil")

		end

	end

end



--[[ ==== Cell, row, table generation ==== ]]

local output_formats = {

	high_low_average_F =

		{ first = "F",

		convert_units = "yes",

		unit_names = "no",

		color = "yes",

		small_font = "yes",

		sortable = "yes",

		decimals = "0",

		brackets = "yes",

		line_break = "auto", },

	high_low_average_C =

		{ first = "C",

		convert_units = "yes",

		unit_names = "no",

		color = "yes",

		small_font = "yes",

		sortable = "yes",

		decimals = "0",

		brackets = "yes",

		line_break = "auto", },

	high_low_F =

		{ first = "F",

		convert_units = "yes",

		unit_names = "no",

		color = "no",

		small_font = "yes",

		sortable = "no",

		decimals = "",

		brackets = "yes",

		line_break = "auto", },

	high_low_C =

		{ first = "C",

		convert_units = "yes",

		unit_names = "no",

		color = "no",

		small_font = "yes",

		sortable = "no",

		decimals = "0",

		brackets = "yes",

		line_break = "auto", },

	average_F =

		{ first = "F",

		convert_units = "yes",

		unit_names = "no",

		color = "yes",

		small_font = "yes",

		sortable = "no",

		decimals = "0",

		brackets = "yes",

		line_break = "auto", },

	average_C =

		{ first = "C",

		convert_units = "yes",

		unit_names = "no",

		color = "yes",

		small_font = "yes",

		sortable = "no",

		decimals = "0",

		brackets = "yes",

		line_break = "auto", },

	}



local function add_unit_names(value, unit)

	if not unit then unit = input_unit end

	if output_format.unit_names == "yes" then

		if check_for_string(value) then

			return value .. "&nbsp;" .. degree .. unit

		else

			return value -- Don't add a unit name to an empty string

		end

	else

		return value

	end

end



local function if_yes(parameter, realization1, realization2)

	if realization1 then

		if realization2 then

			if parameter == "yes" then

				parameter = { realization1, realization2 }

			else

				parameter = { "", "" }

			end

		else

			if parameter == "yes" then

				parameter = realization1

			else

				parameter = ""

			end

		end

	else

		parameter = ""

		add_message("<span style=\"background-color: #EEE; font-family: monospace;\">if_yes</span> needs at least one realization")

	end

	return parameter

end



function makeCell(output_format, a, b, c)

	local cell, cell_content = "", ""

	local color_CSS, other_CSS, title_attribute, sortkey, attribute_separator, converted_units_separator = "", "", "", "", "", "", ""

	local style_attribute, high_low_separator, brackets, values, converted_units = {"", ""}, {"", ""}, {"", ""}, {"", ""}, {"", ""}

	

	if check_for_number(output_format.decimals) then

		decimals = output_format.decimals

		--[[ Precision is the number of decimals in the first number of the last array.

			This may be a problem for data from Weatherbase,

			which seems to inappropriately remove .0 from numbers that have it. ]]

	else

		decimals = precision

	end

	

	if check_for_number(b) and check_for_number(a) then

		values, high_low_separator = { round(a, decimals), round(b, decimals) }, { thinSpace .. "/" .. thinSpace, if_yes(output_format.convert_units, thinSpace .. "/" .. thinSpace) }

	elseif check_for_number(a) then

		values = { round(a, decimals), "" }

	elseif check_for_number(c) then

		values = { round(c, decimals), "" }

	end

	if output_format.first == input_unit then

		if output_format.convert_units == "yes" then

			converted_units = { add_unit_names(convert(values1], decimals), output_unit), add_unit_names(convert(values2], decimals), output_unit) }

		end

		values = { add_unit_names(values1]), add_unit_names(values2]) }

	elseif output_format.first == "C" or output_format.first == "F" then

		if output_format.convert_units == "yes" then

			converted_units = { add_unit_names(values1]), add_unit_names(values2]) }

		end

		values = { add_unit_names(convert(values1], decimals), output_unit), add_unit_names(convert(values2], decimals), output_unit) }

	else

		if output_format.first == nil then

			output_format.first = "nil"

		end

		add_message("<span style=\"background-color: #EEE; font-family: monospace;\">" .. output_format.first .. "</span>, the value for <span style=\"background-color: #EEE; font-family: monospace;\">first</span> in <span style=\"background-color: #EEE; font-family: monospace;\">output_format</span> is not recognized.")

	end

	--[[

		Regarding line breaks:

		If there are two values, there will be at least three characters: 9/1.

		If there is one decimal, numbers will be three to five characters long

		and there will be 3 to 10 characters total even without unit conversion:

			1.1, 116.5/88.0.

		If there are units, that adds three characters per number: 25 °C/20 °C.

		In each of these cases, a line break is needed so that table cells are not too wide;

		even more so when more than one of these things are true.

		]]

	if output_format.convert_units == "yes" then

		brackets = if_yes(output_format.brackets, "(", ")" )

		if output_format.line_break == "auto" then

			if check_for_string(values2]) or decimals ~= "0" or output_format.show_units == "yes" then

				converted_units_separator = "<br>"

			else

				converted_units_separator = "&nbsp;"

			end

		elseif output_format.line_break == "yes" then

			converted_units_separator = "<br>"

		elseif output_format.line_break == "no" then

			converted_units_separator = "&nbsp;"

		else

			error("Value for line_break not recognized")

		end

	end

	

	cell_content = values1 .. high_low_separator1 .. values2 .. converted_units_separator .. brackets1 .. converted_units1 .. high_low_separator2 .. converted_units2 .. brackets2

	

	if check_for_number(c) then

		color_CSS = if_yes(output_format.color, temperature_CSS(c, input_unit, palette))

		if check_for_number(b) and check_for_number(a) then

			local attribute_value

			if output_format.first == input_unit then

				attribute_value = c

			else

				attribute_value = convert(c, decimals)

			end

			sortkey = if_yes(output_format.sortable, " data-sort-value=\"" .. attribute_value .. "\"")

			title_attribute = " title=\"Average temperature: " .. attribute_value .. " " .. degree .. output_format.first .. "\""

		end

	elseif check_for_number(b) then

		color_css = ""

	elseif check_for_number(a) then

		color_CSS = if_yes(output_format.color, temperature_CSS(a, input_unit, palette))

	else

		add_message("Neither a nor b nor c are strings.")

	end

	other_CSS = if_yes(output_format.small_font, "font-size: 85%;")

	if check_for_string(color_CSS) or check_for_string(other_CSS) then

		style_attribute = { "style=\"", "\"" }

	end

	

	if check_for_string(other_CSS) or check_for_string(color_CSS) or check_for_string(title_attribute) or check_for_string(sortkey) then

		attribute_separator = " | "

	end

	cell = "\n| " .. style_attribute1 .. color_CSS .. other_CSS .. style_attribute2 .. title_attribute .. sortkey .. attribute_separator .. cell_content

	return cell

end



function f.makeRow(frame)

	make_arrays(frame)

	local output = ""

	if frame.args1 then

		output = "\n|-"

		output = output .. "\n! " .. frame.args1

		if frame.args2 then

			output = output .. " !! " .. frame.args2

		end

	end

	if cell_format then

		output_format = cell_format

	end

	if a and b and c then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.high_low_average_F

			end

			output = output .. makeCell(output_format, ai], bi], ci])

		end

	elseif a and b then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.high_low_F

			end

			output = output .. makeCell(output_format, ai], bi])

		end

	elseif a then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.average_F

			end

			output = output .. makeCell(output_format, ai])

		end

	end

	output = mw.ustring.gsub(output, "([%p%s])-(%d)", "%1" .. minus .. "%2")

	return output

end



function f.makeTable(frame)

	make_arrays(frame)

	local output = "{| class=\"wikitable center nowrap\""

	if cell_format then

		output_format = cell_format

	end

	if a and b and c then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.high_low_average_F

			end

			output = output .. makeCell(output_format, ai], bi], ci])

		end

	elseif a and b then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.high_low_F

			end

			output = output .. makeCell(output_format, ai], bi])

		end

	elseif a then

		for i = 1, length do

			if not output_format then

				output_format = output_formats.average_F

			end

			output = output .. makeCell(output_format, ai])

		end

	end

	output = mw.ustring.gsub(output, "([%p%s])-(%d)", "%1" .. minus .. "%2")

		--[[  Makes sure that hyphens in "data-sort-type" are not replaced with minuses.

				If Lua had (?<=), a capture would not be necessary.  ]]

	output = output .. "\n|}"

	if show then

		output = output .. "\n\n<span style=\"color: red; font-size: 80%; line-height: 100%;\">" .. message .. "</span>"

	end

	return output

end



return f

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook