Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
Humanipedia
Search
Search
Appearance
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
Module:Weather/sandbox
Module
Discussion
English
Read
Edit source
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit source
View history
General
What links here
Related changes
Special pages
Page information
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
local p = {} require('strict') local degree = "°" -- used by addUnitNames() local minus = "−" -- used by makeRow() and makeTable() local thinSpace = mw.ustring.char(0x2009) -- used by makeCell() local precision, decimals -- if not empty local function ine(var) var = tostring(var) if var == "" then return nil else return var end end -- Error message handling local message = "" local function addMessage(newMessage) if ine(message) then message = message .. " " .. newMessage else message = "Notices: " .. newMessage end end local function monospace(str) return '<span style="background-color: #EEE; font-family: monospace;">' .. str .. '</span>' end -- Input and output parameters local function getFormat(inputParameter, outputParameter, palette, messages) local length, inputUnit, outputUnit, palette, show, cellFormat if inputParameter == nil then error('Please provide the number of values and a unit in the input parameter') else -- Find as many as two digits in the input parameter. length = tonumber(string.match(inputParameter, "(%d%d?)")) if not length then length = 13 addMessage('getFormat has not found a length value in the input parameter; length defaults to "13"') end -- Find C or F, but not both if string.find(inputParameter, "C") and string.find(inputParameter, "F") then error("Input unit must be either C (Celsius) or F (Fahrenheit)") else inputUnit = string.match(inputParameter, "([CF])") or error("Please provide an input unit in the input parameter: F for Fahrenheit or C for Celsius", 0) end if inputUnit == "C" then outputUnit = "F" else outputUnit = "C" end -- Make sure nothing except C, F, numbers, or spaces is in the input parameter. if string.find(inputParameter, "[^CF%d%s]") then addMessage("There are extraneous characters in the " .. monospace("output") .. " parameter.") end end if outputParameter == nil then -- Since there are default values, the module will still generate output with an empty output parameter. addMessage("No output format has been provided in the " .. monospace("output") .. " parameter, so default values will be used.") else cellFormat = {} for i, unit in require("Module:StringTools").imatch(outputParameter, "[CF]") do cellFormat[i] = unit if i > 2 then break end end local function setFormat(key, variable, value) if string.find(outputParameter, key) then cellFormat[variable] = value else cellFormat[variable] = not value end end if cellFormat[1] then cellFormat.first = cellFormat[1] else error('C or F not found in output parameter') end if cellFormat[2] == nil then cellFormat["convertUnits"] = false else if cellFormat[2] == cellFormat[1] then error('There should not be two of the same unit name in the output parameter.') else cellFormat["convertUnits"] = true end end setFormat("unit", "unitNames", true) setFormat("no ?color", "color", false) setFormat("sort", "sortable", true) setFormat("full ?size", "smallFont", false) setFormat("no ?brackets", "brackets", false) setFormat("round", "decimals", "0", "") if string.find(outputParameter, "line break") then cellFormat["lineBreak"] = true elseif string.find(outputParameter, "one line") then cellFormat["lineBreak"] = false else cellFormat["lineBreak"] = "auto" end if string.find(outputParameter, "one line") and string.find(outputParameter, "line break") then error('Place either "one line" or "line break" in the output parameter, not both') end end palette = palette or "cool2avg" show = messages == "show" return { length = length, inputUnit = inputUnit, outputUnit = outputUnit, cellFormat = cellFormat, show = show, palette = palette } end -- Math functions local function round(value, decimals) value = tonumber(value) if type(value) == "number" then return string.format("%." .. decimals .. "f", value) else error("Format was asked to operate on " .. tostring(value) .. ", which cannot be converted to a number.", 2) return "" end end local function convert(value, unit, decimals) -- Unit is the unit being converted from. if not unit then error("No unit supplied to convert.", 2) end if tonumber(value) then local value = tonumber(value) if unit == "C" then return round(value * 9/5 + 32, decimals) elseif unit == "F" then return round((value - 32) * 5/9, decimals) else error("Input unit not recognized", 2) end else -- to avoid concatenation errors return "" end end -- Stick numbers into array. Find out if any have decimals. -- Throw an error if any are invalid. local function _makeArray(format) return function(parameter) if not parameter then return nil end local array = {} -- If there are multiple parameters for numbers, and the first doesn't have -- decimals, the rest will have their decimals rounded off. format.precision = format.precision or parameter:find("%d%.%d") and "1" or "0" local numbers = mw.text.split(parameter, "%s+") if #numbers ~= format.length then addMessage('There are not ' .. format.length .. ' values in the ' .. parameter .. ' parameter.') end for i, number in ipairs(numbers) do if not number:find("^%-?%d%d?%d?.?(%d?)$") then error('The number "' .. number .. '" does not fit the expected pattern.') end table.insert(array, number) end return array end end -- Color generation p.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 that, on the scale from 0 (black) to 255 (saturated), the color is 0 below 11°C and above 44°C, and is 255 from 22°C to 33°C. The color rises from 0 to 255 between 11°C and 22°C, and falls from 255 to 0 between 33°C and 44°C. ]] cool = { { -42.75, 4.47, 41.5, 60 }, -- red { -42.75, 4.47, 4.5, 41.5 }, -- green { -90 , -42.78, 4.5, 23 }, -- blue white = { -23.3, 37.8 }, -- background }, 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 }, }, } --[[ Return style for a table cell based on the given value which should be a temperature in °C. ]] local function temperatureColor(palette, value, outRGB) local backgroundColor, textColor value = tonumber(value) if not value then backgroundColor, textColor = 'FFF', '000' addMessage("Value supplied to " .. monospace("temperatureColor") .. " is not recognized.") else local min, max = unpack(palette.white or { -23, 35 }) if value < min or value >= max then textColor = 'FFF' -- Else nil. -- This assumes that black text color is the default for most readers. end local backgroundRGB = outRGB or {} for i, v in ipairs(palette) do local a, b, c, d = unpack(v) if value <= a then backgroundRGB[i] = 0 elseif value < b then backgroundRGB[i] = (value - a) * 255 / (b - a) elseif value <= c then backgroundRGB[i] = 255 elseif value < d then backgroundRGB[i] = 255 - ( (value - c) * 255 / (d - c) ) else backgroundRGB[i] = 0 end end backgroundColor = string.format('%02X%02X%02X', unpack(backgroundRGB)) end return backgroundColor, textColor end local function colorCSS(backgroundColor, textColor) if backgroundColor and textColor then return 'background: #' .. backgroundColor .. '; color: #' .. textColor .. ';' elseif backgroundColor then return 'background: #' .. backgroundColor .. ';' else return '' end end local function temperatureColorCSS(palette, value, outRGB) return colorCSS(temperatureColor(palette, value, outRGB)) end local function temperatureCSS(value, unit, palette) local palette = p.palettes[palette] or p.palettes.cool local value = tonumber(value) if value == nil then error("The function " .. monospace("temperatureCSS") .. " is receiving a nil value") else if unit == 'F' then value = convert(value, 'F', decimals) elseif unit ~= 'C' then unitError(unit or "nil") end return colorCSS(temperatureColor(palette, value)) end end local function styleAttribute(palette, value, outRGB) local fontSize = "font-size: 85%;" local color = temperatureColorCSS(palette, value, outRGB) return 'style=\"' .. color .. ' ' .. fontSize .. '\"' end local style_attribute = styleAttribute --[=[ Used by {{Average temperature table/row/C/sandbox}}, {{Average temperature table/row/F/sandbox}}, {{Average temperature table/row/C/sandbox}}, {{Template:Avg temp row F/sandbox2}}, {{Template:Avg temp row C/sandbox2}}. ]=] function p.temperatureStyle(frame) local palette = p.palettes[frame.args.palette] or p.palettes.cool local unit = frame.args.unit or 'C' local value = tonumber(frame.args[1]) if unit == 'F' then value = convert(value, 'F', 1) elseif unit ~= 'C' then error('Unrecognized unit: ' .. unit) end return styleAttribute(palette, value) end p.temperature_style = p.temperatureStyle --[[ ==== Cell, row, table generation ==== ]] local outputFormats = { high_low_average_F = { first = "F", convertUnits = true, unitNames = false, color = true, smallFont = true, sortable = true, decimals = "0", brackets = true, lineBreak = "auto", }, high_low_average_C = { first = "C", convertUnits = true, unitNames = false, color = true, smallFont = true, sortable = true, decimals = "0", brackets = true, lineBreak = "auto", }, high_low_F = { first = "F", convertUnits = true, unitNames = false, color = false, smallFont = true, sortable = false, decimals = "", brackets = true, lineBreak = "auto", }, high_low_C = { first = "C", convertUnits = true, unitNames = false, color = false, smallFont = true, sortable = false, decimals = "0", brackets = true, lineBreak = "auto", }, average_F = { first = "F", convertUnits = true, unitNames = false, color = true, smallFont = true, sortable = false, decimals = "0", brackets = true, lineBreak = "auto", }, average_C = { first = "C", convertUnits = true, unitNames = false, color = true, smallFont = true, sortable = false, decimals = "0", brackets = true, lineBreak = "auto", }, } local outputFormat local function addUnitNames(value, yesOrNo, unit) if not unit then error("No unit supplied as argument 3 to addUnitNames", 2) end -- Don't add a unit name to an empty string value = yesOrNo == true and ine(value) and value .. " " .. degree .. unit or value return value end local function ifYes(parameter, realization1, realization2) local result if realization1 then if realization2 then result = parameter == true and { realization1, realization2 } or { "", "" } else result = parameter == true and realization1 or "" end else result = "" addMessage(monospace("ifYes") .. " needs at least one realization.") end return result end local function makeCell(outputFormat, a, b, c, format) local cell, cellContent = "", "" local colorCSS, otherCSS, titleAttribute, sortkey, attributeSeparator, convertedUnitsSeparator = "", "", "", "", "", "", "" -- Distinguish styleAttribute variable from styleAttribute function above. local styleAttribute, highLowSeparator, brackets, values, convertedUnits = {"", ""}, {"", ""}, {"", ""}, {"", ""}, {"", ""} -- Precision is 1 if any number has one or more decimals. decimals = tonumber(outputFormat.decimals) and outputFormat.decimals or format.precision if tonumber(b) and tonumber(a) then values, highLowSeparator = { round(a, decimals), round(b, decimals) }, { thinSpace .. "/" .. thinSpace, ifYes(outputFormat.convertUnits, thinSpace .. "/" .. thinSpace) } elseif tonumber(a) then values = { round(a, decimals), "" } elseif tonumber(c) then values = { round(c, decimals), "" } end if outputFormat.first == format.inputUnit then if outputFormat.convertUnits == true then convertedUnits = { addUnitNames(convert(values[1], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit), addUnitNames(convert(values[2], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit) } end values = { addUnitNames(values[1], outputFormat.unitNames, format.inputUnit), addUnitNames(values[2], outputFormat.unitNames, format.inputUnit) } elseif outputFormat.first == "C" or outputFormat.first == "F" then if outputFormat.convertUnits == true then convertedUnits = { addUnitNames(values[1], outputFormat.unitNames, format.inputUnit), addUnitNames(values[2], outputFormat.unitNames, format.inputUnit) } end values = { addUnitNames(convert(values[1], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit), addUnitNames(convert(values[2], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit) } else addMessage(monospace(tostring(outputFormat.first)) .. ", the value for " .. monospace("first") .. " in " .. monospace("outputFormat") .. " 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 outputFormat.convertUnits == true then brackets = outputFormat.brackets == true and { "(", ")" } or { "", "" } if outputFormat.lineBreak == "auto" then convertedUnitsSeparator = ( ine(values[2]) or decimals ~= "0" or outputFormat.showUnits == true ) and "<br>" or " " else convertedUnitsSeparator = outputFormat.lineBreak == true and "<br>" or outputFormat.lineBreak == false and " " or error('Value for lineBreak not recognized') end end cellContent = values[1] .. highLowSeparator[1] .. values[2] .. convertedUnitsSeparator .. brackets[1] .. convertedUnits[1] .. highLowSeparator[2] .. convertedUnits[2] .. brackets[2] if tonumber(c) then colorCSS = outputFormat.color == true and temperatureCSS(c, format.inputUnit, format.palette, format.inputUnit) or "" if tonumber(b) and tonumber(a) then local attributeValue = outputFormat.first == format.inputUnit and c or convert(c, format.inputUnit, decimals) sortkey = outputFormat.sortable == true and " data-sort-value=\"" .. attributeValue .. "\"" or "" titleAttribute = " title=\"Average temperature: " .. attributeValue .. " " .. degree .. outputFormat.first .. "\"" end elseif tonumber(b) then colorCSS = "" elseif tonumber(a) then colorCSS = outputFormat.color == true and temperatureCSS(a, format.inputUnit, format.palette) or "" else addMessage('Neither a nor b nor c are strings.') end otherCSS = outputFormat.smallFont == true and "font-size: 85%;" or "" if ine(colorCSS) or ine(otherCSS) then styleAttribute = { "style=\"", "\"" } end if ine(otherCSS) or ine(colorCSS) or ine(titleAttribute) or ine(sortkey) then attributeSeparator = " | " end cell = "\n| " .. styleAttribute[1] .. colorCSS .. otherCSS .. styleAttribute[2] .. titleAttribute .. sortkey .. attributeSeparator .. cellContent return cell end --[[ Replaces hyphens that have a punctuation or space character before them and a number after them, making sure that hyphens in "data-sort-type" are not replaced with minuses. If Lua had (?<=), a capture would not be necessary. ]] local function hyphenToMinus(str) return str:gsub("([%p%s])-(%d)", "%1" .. minus .. "%2") end function p.makeRow(frame) local args = frame.args local format = getFormat(args.input, args.output, args.palette, args.messages) local makeArray = _makeArray(format) local a, b, c = makeArray(args.a), makeArray(args.b), makeArray(args.c) local output = {} if args[1] then table.insert(output, "\n|-") table.insert(output, "\n! " .. args[1]) if args[2] then table.insert(output, " !! " .. args[2]) end end if format.cellFormat then outputFormat = format.cellFormat end -- Assumes that if c is defined, b and a are, and if b is defined, a is. if c then if not outputFormat then outputFormat = outputFormats.high_low_average_F end for i = 1, format.length do table.insert(output, makeCell(outputFormat, a[i], b[i], c[i], format)) end elseif b then if not outputFormat then outputFormat = outputFormats.high_low_F end for i = 1, format.length do table.insert(output, makeCell(outputFormat, a[i], b[i], nil, format)) end elseif a then if not outputFormat then outputFormat = outputFormats.average_F end for i = 1, format.length do table.insert(output, makeCell(outputFormat, a[i], nil, nil, format)) end end output = table.concat(output) output = hyphenToMinus(output) return output end function p.makeTable(frame) local args = frame.args local format = getFormat(args.input, args.output, args.palette, args.messages) local makeArray = _makeArray(format) local a, b, c = makeArray(args.a), makeArray(args.b), makeArray(args.c) local output = { "{| class=\"wikitable center nowrap\"" } if format.cellFormat then outputFormat = format.cellFormat end -- Assumes that if c is defined, b and a are, and if b is defined, a is. if c then for i = 1, format.length do if not outputFormat then outputFormat = outputFormats.high_low_average_F end table.insert(output, makeCell(outputFormat, a[i], b[i], c[i], format)) end elseif b then for i = 1, format.length do if not outputFormat then outputFormat = outputFormats.high_low_F end table.insert(output, makeCell(outputFormat, a[i], b[i], nil, format)) end elseif a then for i = 1, format.length do if not outputFormat then outputFormat = outputFormats.average_F end table.insert(output, makeCell(outputFormat, a[i], nil, nil, format)) end end table.insert(output, "\n|}") if format.show then table.insert(output, "\n\n<span style=\"color: red; font-size: 80%; line-height: 100%;\">" .. message .. "</span>") end output = table.concat(output) output = hyphenToMinus(output) return output end local chart = [[ {{Graph:Chart |width=600 |height=180 |xAxisTitle=Celsius |yAxisTitle=__COLOR |type=line |x=__XVALUES |y=__YVALUES |colors=__COLOR }} ]] function p.show(frame) -- For testing, return wikitext to show graphs of how the red/green/blue colors -- vary with temperature, and a table of the resulting colors. local function collection() -- Return a table to hold items. return { n = 0, add = function (self, item) if item then self.n = self.n + 1 self[self.n] = item end end, join = function (self, sep) return table.concat(self, sep) end, } end local function make_chart(result, color, xvalues, yvalues) result:add('\n') result:add(frame:preprocess((chart:gsub('__[A-Z]+', { __COLOR = color, __XVALUES = xvalues:join(','), __YVALUES = yvalues:join(','), })))) end local function with_minus(value) if value < 0 then return minus .. tostring(-value) end return tostring(value) end local args = frame.args local first = args[1] or -90 local last = args[2] or 59 local palette = p.palettes[args.palette] or p.palettes.cool local xvals, reds, greens, blues = collection(), collection(), collection(), collection() local wikitext = collection() wikitext:add('{| class="wikitable"\n|-\n') local columns = 0 for celsius = first, last do local backgroundRGB = {} local style = styleAttribute(palette, celsius, backgroundRGB) local R = math.floor(backgroundRGB[1]) local G = math.floor(backgroundRGB[2]) local B = math.floor(backgroundRGB[3]) xvals:add(celsius) reds:add(R) greens:add(G) blues:add(B) wikitext:add('| ' .. style .. ' | ' .. with_minus(celsius) .. '\n') columns = columns + 1 if columns >= 10 then columns = 0 wikitext:add('|-\n') end end wikitext:add('|}\n') make_chart(wikitext, 'Red', xvals, reds) make_chart(wikitext, 'Green', xvals, greens) make_chart(wikitext, 'Blue', xvals, blues) return wikitext:join() end return p
Summary:
Please note that all contributions to Humanipedia may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
Humanipedia:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Debug console
* The module exports are available as the variable "p", including unsaved modifications. * Precede a line with "=" to evaluate it as an expression or use print(). Use mw.logObject() for tables. * Use mw.log() and mw.logObject() in module code to send messages to this console.
Template:Abbr
(
edit
)
Template:Cite web
(
edit
)
Template:Graph:Chart
(
edit
)
Template:Para
(
edit
)
Template:WRCC
(
edit
)
Module:Citation/CS1
(
edit
)
Module:Citation/CS1/Configuration
(
edit
)
Module:Graph
(
edit
)
Module:String
(
edit
)
Module:StringTools
(
edit
)
Module:Weather/sandbox
(
edit
)
Module:Weather/sandbox/doc
(
edit
)