Module:Weather box

-- Implement Template:Weather box.

local _precision = require('Module:Math')._precision

local function precision(text)

-- Input like 'Jan precipitation inch = trace' calls this with text = 'trace'

-- which would cause _precision to throw an error since it is not numeric.

-- Workaround: Return 0 as the precision if an error occurs.

local success, result = pcall(_precision, text)

if success then

return result

end

return 0

end

local function stripToNil(text)

-- If text is a non-empty string, return its trimmed content.

-- Otherwise, return nothing (text is an empty string or is not a string).

if type(text) == 'string' then

return text:match('(%S.-)%s*$')

end

end

local function isAny(args, suffix)

local months = { 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' }

for _, month in ipairs(months) do

if stripToNil(args[month .. suffix]) then

return true

end

end

end

local function makeLabel(args, options, is_first, base, what)

local first

if isAny(args, ' ' .. what .. ' cm') then

first = 'cm'

elseif isAny(args, ' ' .. what .. ' mm') then

first = 'mm'

else

first = (what:sub(1, 4) == 'snow' or

precision(args['Jan ' .. what .. ' inch'] or '0') < 1)

and 'cm'

or 'mm'

end

local second = 'inches'

if not stripToNil(args['metric first']) then

first, second = second, first

end

if is_first then

if options.wantSingleLine then

first = first .. ' (' .. second .. ')'

end

else

first = second

end

return base .. ' ' .. first

end

local function makeSources(frame, args)

local source1 = stripToNil(args.source) or stripToNil(args['source 1'])

local source2 = stripToNil(args['source 2']) or stripToNil(args['source2'])

local result = '|-\n|colspan="14" style="text-align:center;font-size:95%;"|'

local text

if source1 or source2 then

if source1 and source2 then

text = 'Source 1: ' .. source1 .. '\n' .. result .. 'Source 2: ' .. source2

else

text = 'Source: ' .. (source1 and source1 or source2)

end

else

text = frame:expandTemplate({ title = 'citation needed', args = {date = stripToNil(args.date)} })

end

return result .. text .. '\n|}'

end

local function wantSingle(parm)

--------------------------------------------------------------------------------

-- Result before July 2022 for single_line setting:

-- blank/omitted : separate table rows for metric and imperial (two rows)

-- N : bug: only one row with either metric or imperial (not both)

-- but heading like "cm (inches)"

-- Y/junk : single row with both metric/imperial in same cell

--------------------------------------------------------------------------------

-- Intend changing in July 2022 so single_line=Y is the default and

-- need single_line=N for separate lines.

-- However, this release (1 July 2022) keeps blank/omitted as meaning "no"

-- so the other large changes can be tested before switching the default.

parm = (parm or ''):lower()

return not (parm == 'no' or parm == 'n' or parm == '')

end

local function getDefinitions(frame, args, options)

-- Return a list of tables or strings that define each row.

local function _if(parm, a, b)

return stripToNil(args[parm]) and a or b or ''

end

local function _ifset(parm, a)

return stripToNil(args[parm]) and args[parm] or a

end

local function _ifany(suffix)

return isAny(args, suffix)

end

local function _ifSingle(a, b)

return options.wantSingleLine and a or b or ''

end

local function _colorscheme(what, default)

return stripToNil(args[what .. ' colour']) or stripToNil(args[what .. ' color']) or default

end

local function _margin()

if stripToNil(args.margin) then

return 'margin:' .. args.margin .. ';'

end

return ''

end

local location = _if('location', args.location, '{{{location}}}') -- show "{{{location}}}" to alert editor if parameter is blank

local navbarText

if not stripToNil(args.open) then

if stripToNil(args.name) then

local navbar = require('Module:Navbar')._navbar

navbarText = navbar({'Climate data for ' .. location, args.name, collapsible=1})

end

end

return {

----------- HEADER ----------

'{| class="wikitable ' .. _if('open', '', 'mw-collapsible' .. _if('collapsed', ' mw-collapsed')) ..

'" style="width:' .. _ifset('width', 'auto') ..

'; text-align:center; line-height:1.2em;' ..

_margin() .. '"' ..

_if('open',

'\n|+Climate data for ' .. location,

'\n|-' ..

'\n!colspan="14" | ' .. _if('name', navbarText, 'Climate data for ' .. location)

) ..

[=[

|-

!scope="row" |Month

!scope="col" |Jan

!scope="col" |Feb

!scope="col" |Mar

!scope="col" |Apr

!scope="col" |May

!scope="col" |Jun

!scope="col" |Jul

!scope="col" |Aug

!scope="col" |Sep

!scope="col" |Oct

!scope="col" |Nov

!scope="col" |Dec

!scope="col" style="border-left-width:medium" |Year

]=],

{---------- FIRST LINE MAXIMUM HUMIDEX ----------

WANTROW = _ifany(' maximum humidex') and (_ifset('metric first') or options.wantSingleLine),

mode = 'basic',

group_name = 'maximum humidex',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Record high humidex',

annual_mode = 'max',

},

{---------- FIRST LINE RECORD HIGH TEMPERATURES ----------

WANTROW = _ifany(' record high C') or _ifany(' record high F'),

mode = 'temperature',

group_name = 'record high',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Record high °' .. _if('metric first', 'C', 'F') .. _ifSingle(' (°' .. _if('metric first', 'F', 'C') .. ')'),

annual_mode = 'max',

},

{---------- FIRST-SECOND LINE AVG MONTHLY MAXIMUM TEMPERATURES ----------

WANTROW = _ifany(' avg record high C') or _ifany(' avg record high F'),

mode = 'temperature',

group_name = 'avg record high',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Mean maximum °' .. _if('metric first', 'C', 'F') .. _ifSingle(' (°' .. _if('metric first', 'F', 'C') .. ')'),

annual_mode = 'max',

},

{---------- FIRST LINE MEAN DAILY MAXIMUM TEMPERATURES ----------

WANTROW = _ifany(' high C') or _ifany(' high F'),

mode = 'temperature',

group_name = 'high',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Mean daily maximum °' .. _if('metric first', 'C', 'F') .. _ifSingle(' (°' .. _if('metric first', 'F', 'C') .. ')'),

annual_mode = 'avg',

},

{---------- FIRST LINE DAILY MEAN TEMPERATURES ----------

WANTROW = _ifany(' mean C') or _ifany(' mean F'),

mode = 'temperature',

group_name = 'mean',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Daily mean °' .. _if('metric first', 'C', 'F') .. _ifSingle(' (°' .. _if('metric first', 'F', 'C') .. ')'),

annual_mode = 'avg',

},

{---------- FIRST LINE MEAN DAILY MINIMUM TEMPERATURES ----------

WANTROW = _ifany(' low C') or _ifany(' low F'),

mode = 'temperature',

group_name = 'low',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Mean daily minimum °' .. _if('metric first', 'C', 'F') .. _ifSingle(' (°' .. _if('metric first', 'F', 'C') .. ')'),

annual_mode = 'avg',

},

{---------- FIRST-SECOND LINE AVG MONTHLY MINIMUM TEMPERATURES ----------

WANTROW = _ifany(' avg record low C') or _ifany(' avg record low F'),

mode = 'temperature',

group_name = 'avg record low',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Mean minimum °' .. _if('metric first', 'C', 'F') .. _ifSingle(' (°' .. _if('metric first', 'F', 'C') .. ')'),

annual_mode = 'min',

},

{---------- FIRST LINE RECORD LOW TEMPERATURES ----------

WANTROW = _ifany(' record low C') or _ifany(' record low F'),

mode = 'temperature',

group_name = 'record low',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Record low °' .. _if('metric first', 'C', 'F') .. _ifSingle(' (°' .. _if('metric first', 'F', 'C') .. ')'),

annual_mode = 'min',

},

{---------- FIRST LINE MINIMUM WIND CHILL ----------

WANTROW = _ifany(' chill') and (_ifset('metric first') or options.wantSingleLine),

mode = 'basic',

group_name = 'chill',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Record low wind chill',

annual_mode = 'min',

},

{---------- FIRST LINE TOTAL PRECIPITATION ----------

WANTROW = _ifany(' precipitation cm') or _ifany(' precipitation mm') or _ifany(' precipitation inch'),

mode = 'precipitation',

group_name = 'precipitation',

color_scheme = _colorscheme('precipitation', 'p'),

date_mode = true,

scale_factor = '1',

prefer_cm = precision(_ifset('Jan precipitation inch', '0')) < 1,

label = makeLabel(args, options, true, 'Average precipitation', 'precipitation'),

annual_mode = 'sum',

},

{---------- FIRST LINE RAINFALL ----------

WANTROW = _ifany(' rain cm') or _ifany(' rain mm') or _ifany(' rain inch'),

mode = 'precipitation',

group_name = 'rain',

color_scheme = _colorscheme('rain', 'p'),

date_mode = true,

scale_factor = '1',

prefer_cm = precision(_ifset('Jan rain inch', '0')) < 1,

label = makeLabel(args, options, true, 'Average rainfall', 'rain'),

annual_mode = 'sum',

},

{---------- FIRST LINE SNOWFALL ----------

WANTROW = _ifany(' snow cm') or _ifany(' snow mm') or _ifany(' snow inch'),

mode = 'precipitation',

group_name = 'snow',

prefer_cm = true,

color_scheme = _colorscheme('snow', 'p'),

date_mode = true,

scale_factor = '1',

label = makeLabel(args, options, true, 'Average snowfall', 'snow'),

annual_mode = 'sum',

},

{---------- FIRST LINE AVERAGE EXTREME SNOW DEPTH ----------

WANTROW = _ifany(' snow depth cm') or _ifany(' snow depth mm') or _ifany(' snow depth inch'),

mode = 'precipitation',

group_name = 'snow depth',

prefer_cm = true,

color_scheme = _colorscheme('snow', 'p'),

scale_factor = '0.2',

label = makeLabel(args, options, true, 'Average extreme snow depth', 'snow depth'),

annual_mode = 'max',

},

{---------- SECOND LINE MAXIMUM HUMIDEX ----------

WANTROW = not options.wantSingleLine and _ifany(' maximum humidex'),

mode = 'basic',

group_name = 'maximum humidex',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Humidex',

annual_mode = 'max',

second_line = true,

},

{---------- SECOND LINE RECORD HIGH TEMPERATURES ----------

WANTROW = not options.wantSingleLine and (_ifany(' record high C') or _ifany(' record high F')),

mode = 'temperature',

group_name = 'record high',

second_line = true,

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Record high °' .. _if('metric first', 'F', 'C'),

annual_mode = 'max',

},

{---------- SECOND LINE MEAN DAILY MAXIMUM TEMPERATURES ----------

WANTROW = not options.wantSingleLine and (_ifany(' high C') or _ifany(' high F')),

mode = 'temperature',

group_name = 'high',

second_line = true,

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Mean daily maximum °' .. _if('metric first', 'F', 'C'),

annual_mode = 'avg',

},

{---------- SECOND LINE DAILY MEAN TEMPERATURES ----------

WANTROW = not options.wantSingleLine and (_ifany(' mean C') or _ifany(' mean F')),

mode = 'temperature',

group_name = 'mean',

second_line = true,

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Daily mean °' .. _if('metric first', 'F', 'C'),

show = _if('metric first', '2', '1'),

annual_mode = 'avg',

},

{---------- SECOND LINE MEAN DAILY MINIMUM TEMPERATURES ----------

WANTROW = not options.wantSingleLine and (_ifany(' low C') or _ifany(' low F')),

mode = 'temperature',

group_name = 'low',

second_line = true,

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Mean daily minimum °' .. _if('metric first', 'F', 'C'),

show = _if('metric first', '2', '1'),

annual_mode = 'avg',

},

{---------- SECOND LINE RECORD LOW TEMPERATURES ----------

WANTROW = not options.wantSingleLine and (_ifany(' record low C') or _ifany(' record low F')),

mode = 'temperature',

group_name = 'record low',

second_line = true,

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Record low °' .. _if('metric first', 'F', 'C'),

show = _if('metric first', '2', '1'),

annual_mode = 'min',

},

{---------- SECOND LINE MINIMUM WIND CHILL ----------

WANTROW = not options.wantSingleLine and (_ifany(' chill') and _if('metric first')),

mode = 'basic',

group_name = 'chill',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Wind chill',

annual_mode = 'min',

},

{---------- SECOND LINE TOTAL PRECIPITATION ----------

WANTROW = not options.wantSingleLine and (_ifany(' precipitation cm') or _ifany(' precipitation mm') or _ifany(' precipitation inch')),

mode = 'precipitation',

group_name = 'precipitation',

second_line = true,

color_scheme = _colorscheme('precipitation', 'p'),

date_mode = true,

scale_factor = '1',

prefer_cm = precision(_ifset('Jan precipitation inch', '0')) < 1,

label = makeLabel(args, options, false, 'Average precipitation', 'precipitation'),

annual_mode = 'sum',

},

{---------- SECOND LINE RAINFALL ----------

WANTROW = not options.wantSingleLine and (_ifany(' rain cm') or _ifany(' rain mm') or _ifany(' rain inch')),

mode = 'precipitation',

group_name = 'rain',

second_line = true,

color_scheme = _colorscheme('rain', 'p'),

date_mode = true,

scale_factor = '1',

prefer_cm = precision(_ifset('Jan rain inch', '0')) < 1,

label = makeLabel(args, options, false, 'Average rainfall', 'rain'),

annual_mode = 'sum',

},

{---------- SECOND LINE SNOWFALL ----------

WANTROW = not options.wantSingleLine and (_ifany(' snow cm') or _ifany(' snow mm') or _ifany(' snow inch')),

mode = 'precipitation',

group_name = 'snow',

second_line = true,

prefer_cm = true,

color_scheme = _colorscheme('snow', 'p'),

date_mode = true,

scale_factor = '1',

label = makeLabel(args, options, false, 'Average snowfall', 'snow'),

annual_mode = 'sum',

},

{---------- SECOND LINE AVERAGE EXTREME SNOW DEPTH ----------

WANTROW = not options.wantSingleLine and (_ifany(' snow depth cm') or _ifany(' snow depth mm') or _ifany(' snow depth inch')),

mode = 'precipitation',

group_name = 'snow depth',

second_line = true,

prefer_cm = true,

color_scheme = _colorscheme('snow', 'p'),

scale_factor = '0.2',

label = makeLabel(args, options, false, 'Average extreme snow depth', 'snow depth'),

annual_mode = 'max',

},

{---------- PRECIPITATION DAYS ----------

WANTROW = _ifany(' precipitation days'),

mode = 'basic',

group_name = 'precipitation days',

color_scheme = _colorscheme('precip days', 'd'),

date_mode = true,

scale_factor = '1',

label = 'Average precipitation days' .. _if('unit precipitation days', ' (≥ ' .. _ifset('unit precipitation days', '') .. ')'),

annual_mode = 'sum',

},

{---------- RAINY DAYS ----------

WANTROW = _ifany(' rain days'),

mode = 'basic',

group_name = 'rain days',

color_scheme = _colorscheme('precip days', 'd'),

date_mode = true,

scale_factor = '1',

label = 'Average rainy days' .. _if('unit rain days', ' (≥ ' .. _ifset('unit rain days', '') .. ')'),

annual_mode = 'sum',

},

{---------- SNOWY DAYS ----------

WANTROW = _ifany(' snow days'),

mode = 'basic',

group_name = 'snow days',

color_scheme = _colorscheme('precip days', 'd'),

date_mode = true,

scale_factor = '1',

label = 'Average snowy days' .. _if('unit snow days', ' (≥ ' .. _ifset('unit snow days', '') .. ')'),

annual_mode = 'sum',

},

{---------- PERCENT RELATIVE HUMIDITY ----------

WANTROW = _ifany(' humidity'),

mode = 'basic',

group_name = 'humidity',

color_scheme = _colorscheme('humidity', 'h'),

scale_factor = '1',

label = 'Average relative humidity (%)' ..

_if('time day', ' (at ' .. _ifset('time day', '') .. ')') ..

_if('daily', ' (daily average)'),

annual_mode = 'avg',

},

{---------- AFTERNOON PERCENT RELATIVE HUMIDITY ----------

WANTROW = _ifany(' afthumidity'),

mode = 'basic',

group_name = 'afthumidity',

color_scheme = _colorscheme('humidity', 'h'),

scale_factor = '1',

label = 'Average afternoon relative humidity (%)' ..

_if('time day', ' (at ' .. _ifset('time day', '') .. ')') ..

_if('daily', ' (daily average)'),

annual_mode = 'avg',

},

{---------- FIRST LINE AVERAGE DEW POINT ----------

WANTROW = _ifany(' dew point C') or _ifany(' dew point F'),

mode = 'temperature',

group_name = 'dew point',

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Average dew point °' .. _if('metric first', 'C', 'F') .. _ifSingle(' (°' .. _if('metric first', 'F', 'C') .. ')'),

annual_mode = 'avg',

},

{---------- SECOND LINE AVERAGE DEW POINT----------

WANTROW = not options.wantSingleLine and (_ifany(' dew point C') or _ifany(' dew point F')),

mode = 'temperature',

group_name = 'dew point',

second_line = true,

color_scheme = _colorscheme('temperature', 't'),

scale_factor = '1',

label = 'Average dew point °' .. _if('metric first', 'F', 'C'),

show = _if('metric first', '2', '1'),

annual_mode = 'avg',

},

{---------- MONTHLY SUNSHINE HOURS ----------

WANTROW = _ifany(' sun'),

mode = 'basic',

group_name = 'sun',

color_scheme = _colorscheme('sun', 's'),

date_mode = true,

scale_factor = '1',

label = 'Mean monthly sunshine hours',

annual_mode = 'sum',

},

{---------- DAILY SUNSHINE HOURS ----------

WANTROW = _ifany('d sun'),

mode = 'basic',

group_name = 'd sun',

color_scheme = _colorscheme('sun', 's'),

include_space = false,

scale_factor = '30.44',

label = 'Mean daily sunshine hours',

annual_mode = 'avg',

},

{---------- DAILY DAYLIGHT HOURS ----------

WANTROW = _ifany(' light'),

mode = 'basic',

group_name = ' light',

color_scheme = _colorscheme('sun', 's'),

include_space = false,

scale_factor = '30.44',

label = 'Mean daily daylight hours',

annual_mode = 'avg',

},

{---------- PERCENT SUNSHINE ----------

WANTROW = _ifany(' percentsun'),

mode = 'basic',

group_name = 'percentsun',

color_scheme = _colorscheme('sun', 's'),

scale_factor = '7.2',

label = 'Percentage possible sunshine',

annual_mode = 'avg',

},

{---------- ULTRAVIOLET INDEX ----------

WANTROW = _ifany(' uv'),

mode = 'basic',

group_name = 'uv',

color_scheme = _colorscheme('uv', 'u'),

scale_factor = '1',

label = 'Average ultraviolet index',

annual_mode = 'avg',

},

----------- SOURCES ----------

makeSources(frame, args),

}

end

local function main(frame)

local sandbox = frame:getTitle():find('sandbox', 1, true) and '/sandbox' or ''

local buildRow = require('Module:Weather box/row' .. sandbox)._buildRow

local args = frame:getParent().args

local options = {

wantSingleLine = wantSingle(args['single line']),

sandbox = sandbox,

}

local results = {}

for i, definition in ipairs(getDefinitions(frame, args, options)) do

local row

if type(definition) == 'string' then

row = definition

elseif definition.WANTROW then

row = buildRow(definition, args, options)

else

row = ''

end

results[i] = row

end

return '

\n'..table.concat(results)..'\n
' -- prevent Scribunto from inserting a blank line before the table

end

return {

main = main,

}