词条 | Module:Weather/sandbox |
释义 | local p = {} require('Module:No globals') 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 emptylocal function ine(var) var = tostring(var) if var == "" then return nil else return var end end -- Error message handlinglocal message = "" local function addMessage(newMessage) if ine(message) then message = message .. " " .. newMessage else message = "Notices: " .. newMessage end end local function monospace(str) return '' .. str .. '' end -- Input and output parameterslocal 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 functionslocal 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 generationp.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 whichshould 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 " else convertedUnitsSeparator = outputFormat.lineBreak == true and " 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 = "\| " .. 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, "\|-") table.insert(output, "\! " .. 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, "\|}") if format.show then table.insert(output, "\\" .. message .. "") 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('\') 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"\|-\') 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) .. '\') columns = columns + 1 if columns >= 10 then columns = 0 wikitext:add('|-\') end end wikitext:add('|}\') make_chart(wikitext, 'Red', xvals, reds) make_chart(wikitext, 'Green', xvals, greens) make_chart(wikitext, 'Blue', xvals, blues) return wikitext:join() end return p |
随便看 |
|
开放百科全书收录14589846条英语、德语、日语等多语种百科知识,基本涵盖了大多数领域的百科知识,是一部内容自由、开放的电子版国际百科全书。