Jump to content

Module:Weather/sandbox

From Wikipedia, the free encyclopedia

localp={}

require('strict')

localdegree="°"-- used by addUnitNames()
localminus="−"-- used by makeRow() and makeTable()
localthinSpace=mw.ustring.char(0x2009)-- used by makeCell()

localprecision,decimals

-- if not empty
localfunctionine(var)
var=tostring(var)
ifvar==""then
returnnil
else
returnvar
end
end

-- Error message handling
localmessage=""

localfunctionaddMessage(newMessage)
ifine(message)then
message=message..""..newMessage
else
message="Notices:"..newMessage
end
end

localfunctionmonospace(str)
return'<span style= "background-color: #EEE; font-family: monospace;" >'..str..'</span>'
end

-- Input and output parameters
localfunctiongetFormat(inputParameter,outputParameter,palette,messages)
locallength,inputUnit,outputUnit,palette,show,cellFormat

ifinputParameter==nilthen
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?)"))
ifnotlengththen
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
ifstring.find(inputParameter,"C")andstring.find(inputParameter,"F")then
error("Input unit must be either C (Celsius) or F (Fahrenheit)")
else
inputUnit=string.match(inputParameter,"([CF])")orerror("Please provide an input unit in the input parameter: F for Fahrenheit or C for Celsius",0)
end

ifinputUnit=="C"then
outputUnit="F"
else
outputUnit="C"
end

-- Make sure nothing except C, F, numbers, or spaces is in the input parameter.
ifstring.find(inputParameter,"[^CF%d%s]")then
addMessage("There are extraneous characters in the"..monospace("output").."parameter.")
end
end

ifoutputParameter==nilthen
-- 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={}
fori,unitinrequire("Module:StringTools").imatch(outputParameter,"[CF]")do
cellFormat[i]=unit
ifi>2then
break
end
end
localfunctionsetFormat(key,variable,value)
ifstring.find(outputParameter,key)then
cellFormat[variable]=value
else
cellFormat[variable]=notvalue
end
end
ifcellFormat[1]then
cellFormat.first=cellFormat[1]
else
error('C or F not found in output parameter')
end
ifcellFormat[2]==nilthen
cellFormat["convertUnits"]=false
else
ifcellFormat[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","")
ifstring.find(outputParameter,"line break")then
cellFormat["lineBreak"]=true
elseifstring.find(outputParameter,"one line")then
cellFormat["lineBreak"]=false
else
cellFormat["lineBreak"]="auto"
end
ifstring.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=paletteor"cool2avg"

show=messages=="show"

return{
length=length,inputUnit=inputUnit,outputUnit=outputUnit,
cellFormat=cellFormat,show=show,palette=palette
}
end

-- Math functions

localfunctionround(value,decimals)
value=tonumber(value)
iftype(value)=="number"then
returnstring.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

localfunctionconvert(value,unit,decimals)-- Unit is the unit being converted from.
ifnotunitthen
error("No unit supplied to convert.",2)
end
iftonumber(value)then
localvalue=tonumber(value)
ifunit=="C"then
returnround(value*9/5+32,decimals)
elseifunit=="F"then
returnround((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.
localfunction_makeArray(format)
returnfunction(parameter)
ifnotparameterthen
returnnil
end
localarray={}
-- 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.precisionorparameter:find("%d%.%d")and"1"or"0"

localnumbers=mw.text.split(parameter,"%s+")
if#numbers~=format.lengththen
addMessage('There are not '..format.length..' values in the '..parameter..' parameter.')
end

fori,numberinipairs(numbers)do
ifnotnumber:find("^%-?%d%d?%d?.?(%d?)$")then
error('The number "'..number..' "does not fit the expected pattern.')
end

table.insert(array,number)
end

returnarray
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. ]]
localfunctiontemperatureColor(palette,value,outRGB)
localbackgroundColor,textColor
value=tonumber(value)
ifnotvaluethen
backgroundColor,textColor='FFF','000'
addMessage("Value supplied to"..monospace("temperatureColor").."is not recognized.")
else
localmin,max=unpack(palette.whiteor{-23,35})
ifvalue<minorvalue>=maxthen
textColor='FFF'
-- Else nil.
-- This assumes that black text color is the default for most readers.
end

localbackgroundRGB=outRGBor{}
fori,vinipairs(palette)do
locala,b,c,d=unpack(v)
ifvalue<=athen
backgroundRGB[i]=0
elseifvalue<bthen
backgroundRGB[i]=(value-a)*255/(b-a)
elseifvalue<=cthen
backgroundRGB[i]=255
elseifvalue<dthen
backgroundRGB[i]=255-((value-c)*255/(d-c))
else
backgroundRGB[i]=0
end
end
backgroundColor=string.format('%02X%02X%02X',unpack(backgroundRGB))
end
returnbackgroundColor,textColor
end

localfunctioncolorCSS(backgroundColor,textColor)
ifbackgroundColorandtextColorthen
return'background: #'..backgroundColor..'; color: #'..textColor..';'
elseifbackgroundColorthen
return'background: #'..backgroundColor..';'
else
return''
end
end

localfunctiontemperatureColorCSS(palette,value,outRGB)
returncolorCSS(temperatureColor(palette,value,outRGB))
end

localfunctiontemperatureCSS(value,unit,palette)
localpalette=p.palettes[palette]orp.palettes.cool
localvalue=tonumber(value)
ifvalue==nilthen
error("The function"..monospace("temperatureCSS").."is receiving a nil value")
else
ifunit=='F'then
value=convert(value,'F',decimals)
elseifunit~='C'then
unitError(unitor"nil")
end
returncolorCSS(temperatureColor(palette,value))
end
end

localfunctionstyleAttribute(palette,value,outRGB)
localfontSize="font-size: 85%;"
localcolor=temperatureColorCSS(palette,value,outRGB)
return'style=\ "'..color..' '..fontSize..'\ "'
end

localstyle_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}}.
]=]
functionp.temperatureStyle(frame)
localpalette=p.palettes[frame.args.palette]orp.palettes.cool
localunit=frame.args.unitor'C'
localvalue=tonumber(frame.args[1])
ifunit=='F'then
value=convert(value,'F',1)
elseifunit~='C'then
error('Unrecognized unit: '..unit)
end
returnstyleAttribute(palette,value)
end

p.temperature_style=p.temperatureStyle

--[[ ==== Cell, row, table generation ==== ]]
localoutputFormats={
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",},
}

localoutputFormat

localfunctionaddUnitNames(value,yesOrNo,unit)
ifnotunitthen
error("No unit supplied as argument 3 to addUnitNames",2)
end
-- Don't add a unit name to an empty string
value=yesOrNo==trueandine(value)andvalue.."&nbsp;"..degree..unitorvalue
returnvalue
end

localfunctionifYes(parameter,realization1,realization2)
localresult
ifrealization1then
ifrealization2then
result=parameter==trueand{realization1,realization2}or{"",""}
else
result=parameter==trueandrealization1or""
end
else
result=""
addMessage(monospace("ifYes").."needs at least one realization.")
end
returnresult
end

localfunctionmakeCell(outputFormat,a,b,c,format)
localcell,cellContent="",""
localcolorCSS,otherCSS,titleAttribute,sortkey,attributeSeparator,convertedUnitsSeparator=
"","","","","","",""

-- Distinguish styleAttribute variable from styleAttribute function above.
localstyleAttribute,highLowSeparator,brackets,values,convertedUnits=
{"",""},{"",""},{"",""},{"",""},{"",""}

-- Precision is 1 if any number has one or more decimals.
decimals=tonumber(outputFormat.decimals)andoutputFormat.decimalsorformat.precision

iftonumber(b)andtonumber(a)then
values,highLowSeparator={round(a,decimals),round(b,decimals)},
{thinSpace.."/"..thinSpace,ifYes(outputFormat.convertUnits,thinSpace.."/"..thinSpace)}
elseiftonumber(a)then
values={round(a,decimals),""}
elseiftonumber(c)then
values={round(c,decimals),""}
end

ifoutputFormat.first==format.inputUnitthen
ifoutputFormat.convertUnits==truethen
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)}
elseifoutputFormat.first=="C"oroutputFormat.first=="F"then
ifoutputFormat.convertUnits==truethen
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.
]]
ifoutputFormat.convertUnits==truethen
brackets=outputFormat.brackets==trueand{"(",")"}or{"",""}
ifoutputFormat.lineBreak=="auto"then
convertedUnitsSeparator=(ine(values[2])ordecimals~="0"oroutputFormat.showUnits==true)and"<br>"or"&nbsp;"
else
convertedUnitsSeparator=outputFormat.lineBreak==trueand"<br>"oroutputFormat.lineBreak==falseand"&nbsp;"orerror('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]

iftonumber(c)then
colorCSS=outputFormat.color==trueandtemperatureCSS(c,format.inputUnit,format.palette,format.inputUnit)or""
iftonumber(b)andtonumber(a)then
localattributeValue=outputFormat.first==format.inputUnitandcorconvert(c,format.inputUnit,decimals)
sortkey=outputFormat.sortable==trueand"data-sort-value=\ ""..attributeValue.."\ ""or""
titleAttribute="title=\ "Average temperature: "..attributeValue..""..degree..outputFormat.first.."\ ""
end
elseiftonumber(b)then
colorCSS=""
elseiftonumber(a)then
colorCSS=outputFormat.color==trueandtemperatureCSS(a,format.inputUnit,format.palette)or""
else
addMessage('Neither a nor b nor c are strings.')
end
otherCSS=outputFormat.smallFont==trueand"font-size: 85%;"or""
ifine(colorCSS)orine(otherCSS)then
styleAttribute={"style=\ "","\ ""}
end

ifine(otherCSS)orine(colorCSS)orine(titleAttribute)orine(sortkey)then
attributeSeparator="|"
end
cell="\n| "..styleAttribute[1]..colorCSS..otherCSS..styleAttribute[2]..titleAttribute..sortkey..attributeSeparator..cellContent
returncell
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.
]]
localfunctionhyphenToMinus(str)
returnstr:gsub("([%p%s])-(%d)","%1"..minus.."%2")
end

functionp.makeRow(frame)
localargs=frame.args
localformat=getFormat(args.input,args.output,args.palette,args.messages)
localmakeArray=_makeArray(format)
locala,b,c=makeArray(args.a),makeArray(args.b),makeArray(args.c)
localoutput={}
ifargs[1]then
table.insert(output,"\n|- ")
table.insert(output,"\n!"..args[1])
ifargs[2]then
table.insert(output,"!!"..args[2])
end
end
ifformat.cellFormatthen
outputFormat=format.cellFormat
end
-- Assumes that if c is defined, b and a are, and if b is defined, a is.
ifcthen
ifnotoutputFormatthen
outputFormat=outputFormats.high_low_average_F
end
fori=1,format.lengthdo
table.insert(output,makeCell(outputFormat,a[i],b[i],c[i],format))
end
elseifbthen
ifnotoutputFormatthen
outputFormat=outputFormats.high_low_F
end
fori=1,format.lengthdo
table.insert(output,makeCell(outputFormat,a[i],b[i],nil,format))
end
elseifathen
ifnotoutputFormatthen
outputFormat=outputFormats.average_F
end
fori=1,format.lengthdo
table.insert(output,makeCell(outputFormat,a[i],nil,nil,format))
end
end
output=table.concat(output)
output=hyphenToMinus(output)
returnoutput
end

functionp.makeTable(frame)
localargs=frame.args
localformat=getFormat(args.input,args.output,args.palette,args.messages)
localmakeArray=_makeArray(format)
locala,b,c=makeArray(args.a),makeArray(args.b),makeArray(args.c)
localoutput={"{| class=\ "wikitable center nowrap\ ""}
ifformat.cellFormatthen
outputFormat=format.cellFormat
end
-- Assumes that if c is defined, b and a are, and if b is defined, a is.
ifcthen
fori=1,format.lengthdo
ifnotoutputFormatthen
outputFormat=outputFormats.high_low_average_F
end
table.insert(output,makeCell(outputFormat,a[i],b[i],c[i],format))
end
elseifbthen
fori=1,format.lengthdo
ifnotoutputFormatthen
outputFormat=outputFormats.high_low_F
end
table.insert(output,makeCell(outputFormat,a[i],b[i],nil,format))
end
elseifathen
fori=1,format.lengthdo
ifnotoutputFormatthen
outputFormat=outputFormats.average_F
end
table.insert(output,makeCell(outputFormat,a[i],nil,nil,format))
end
end
table.insert(output,"\n|} ")
ifformat.showthen
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)

returnoutput
end



localchart=[[
{{Graph:Chart
|width=600
|height=180
|xAxisTitle=Celsius
|yAxisTitle=__COLOR
|type=line
|x=__XVALUES
|y=__YVALUES
|colors=__COLOR
}}
]]

functionp.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.
localfunctioncollection()
-- Return a table to hold items.
return{
n=0,
add=function(self,item)
ifitemthen
self.n=self.n+1
self[self.n]=item
end
end,
join=function(self,sep)
returntable.concat(self,sep)
end,
}
end
localfunctionmake_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
localfunctionwith_minus(value)
ifvalue<0then
returnminus..tostring(-value)
end
returntostring(value)
end
localargs=frame.args
localfirst=args[1]or-90
locallast=args[2]or59
localpalette=p.palettes[args.palette]orp.palettes.cool
localxvals,reds,greens,blues=collection(),collection(),collection(),collection()
localwikitext=collection()
wikitext:add('{| class= "wikitable"\n|-\n')
localcolumns=0
forcelsius=first,lastdo
localbackgroundRGB={}
localstyle=styleAttribute(palette,celsius,backgroundRGB)
localR=math.floor(backgroundRGB[1])
localG=math.floor(backgroundRGB[2])
localB=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
ifcolumns>=10then
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)
returnwikitext:join()
end

returnp