跳转到内容
打开/关闭菜单
  • 451 条目
  • 808 文件
  • 22 用户
  • 9259 编辑
Blockland Wiki
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

模块:Weather box:修订间差异

来自Blockland Wiki
无编辑摘要
标签:已被回退
无编辑摘要
标签:手工回退
第1行: 第1行:
-- =========================================================================
-- Module:Weather box
-- 集成“一键切换视图”、“柯本分类”、“动态缩放”、“精美气候图”的全能版
-- 認真對待版:徹底解決 pre 醜框,動態範圍,新增圖例,修復 %s 佔位符報錯
-- =========================================================================
local _precision = require('Module:Math')._precision
local _precision = require('Module:Math')._precision
local function precision(text)
local function precision(text)
第79行: 第74行:


-- =========================================================================
-- =========================================================================
-- 图表核心逻辑 - 認真對待版
-- 图表核心计算与渲染
-- =========================================================================
-- =========================================================================
local function calculateKoppen(temps, precips)
local function calculateKoppen(temps, precips)
第106行: 第101行:
     local months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
     local months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
     local d = { high = {}, mean = {}, low = {}, rain = {} }
     local d = { high = {}, mean = {}, low = {}, rain = {} }
   
     local max_t, max_p = 50, 100
    -- ==========================================
 
    -- 1. 数据采集与数据清洗
    -- ==========================================
     local raw_min_t, raw_max_t, raw_max_p = 999, -999, 0
     for i, m in ipairs(months) do
     for i, m in ipairs(months) do
         local high = tonumber(args[m .. ' high C'])
         d.high[i] = tonumber(args[m .. ' high C']) or 0
         local mean = tonumber(args[m .. ' mean C'])
         d.mean[i] = tonumber(args[m .. ' mean C']) or 0
         local low = tonumber(args[m .. ' low C'])
         d.low[i] = tonumber(args[m .. ' low C']) or 0
       
        d.rain[i] = tonumber(args[m .. ' precipitation mm']) or tonumber(args[m .. ' rain mm']) or 0
        -- 对 trace 进行正确处理,不报错
        local function p_parse(val)
            if not val or val == '' then return 0 end
            local n = tonumber(val)
            if n then return n end
            if val:lower() == 'trace' or val:lower() == 't' then return 0.1 end
            return 0
        end
        local rain = p_parse(args[m .. ' precipitation mm'] or args[m .. ' rain mm'])
 
        -- 数据推入表,不存在填0
        d.high[i], d.mean[i], d.low[i], d.rain[i] = high or 0, mean or 0, low or 0, rain
          
          
        -- 确定数据的实际范围
         if d.high[i] > max_t then max_t = d.high[i] end
         if high then raw_max_t = math.max(raw_max_t, high) end
         if d.rain[i] > max_p then max_p = d.rain[i] end
         if mean then raw_max_t = math.max(raw_max_t, mean) end -- 防备 mean > high
        if low then raw_min_t = math.min(raw_min_t, low) end
        if rain then raw_max_p = math.max(raw_max_p, rain) end
     end
     end
      
      
     -- ==========================================
     max_t = math.ceil(max_t / 10) * 10
    -- 2. 动态刻度核心计算
     max_p = math.ceil(max_p / 20) * 20
    -- ==========================================
 
    -- 需求:默认 -20 ~ 40, 0 ~ 600, 不够动态加
    local min_t_bound = math.min(raw_min_t, -20)
    local max_t_bound = math.max(raw_max_t, 40)
    local max_p_bound = math.max(raw_max_p, 600)
   
    -- 向下/上取整到 10 的倍数
    local final_min_t = math.floor(min_t_bound / 10) * 10
    local final_max_t = math.ceil(max_t_bound / 10) * 10
      
    -- 降水向后扩展,至少 600,不够则以100为步长加
    local final_max_p = math.ceil(max_p_bound / 100) * 100
   
    -- ==========================================
    -- 3. SVG 绘图参数设置
    -- ==========================================
     local w, h = 300, 400
     local w, h = 300, 400
    -- 增大顶部 Padding 以容纳图例
     local padding = { t = 40, b = 40, l = 40, r = 40 }
     local padding = { t = 60, b = 40, l = 40, r = 40 }
     local chart_w, chart_h = w - padding.l - padding.r, h - padding.t - padding.b
     local chart_w, chart_h = w - padding.l - padding.r, h - padding.t - padding.b
    local function ty(v) return h - padding.b - (v / max_t * chart_h) end
    local function py(v) return h - padding.b - (v / max_p * chart_h) end
    local function tx(i) return padding.l + (i - 0.5) * (chart_w / 12) end
    local svg = string.format('<svg width="100%%" viewBox="0 0 %d %d" style="background:#fff; font-family:sans-serif; border:1px solid #ddd;">', w, h)
      
      
     -- ==========================================
     for v = 0, max_t, 10 do
    -- 4. 坐标映射函数
    -- ==========================================
    -- 温度 ty (y=0 不在最底部)
    local function ty(v)
        return h - padding.b - ((v - final_min_t) / (final_max_t - final_min_t) * chart_h)
    end
    -- 降水 py (y=0 严格在最底部)
    local function py(v)
        return h - padding.b - (v / final_max_p * chart_h)
    end
    -- X 轴 tx (居中对齐柱子)
    local function tx(i)
        return padding.l + (i - 0.5) * (chart_w / 12)
    end
   
    -- ==========================================
    -- 5. 开始绘制 SVG
    -- ==========================================
    local svg = string.format('<svg width="100%%" viewBox="0 0 %d %d" style="background:#fff; font-family:sans-serif; border:1px solid #c8ccd1;">', w, h)
   
    -- 新增图例区域
    local function drawLegend()
        local l_y = 20
        local items = {
            { type="line", color="#e41a1c", stroke_w=1.5, mark="dot", label="平均高温"},
            { type="line", color="#ff7f00", stroke_w=2.5, mark="dot", label="日均气温"},
            { type="line", color="#377eb8", stroke_w=1.5, mark="dot", label="平均低温"},
            { type="rect", color="#4da6ff", label="平均降水量"}
        }
        local s = ''
        local start_x = 25
        for i, item in ipairs(items) do
            local x = start_x + (i - 1) * 65
            if item.type == "line" then
                s = s .. string.format('<line x1="%f" y1="%f" x2="%f" y2="%f" stroke="%s" stroke-width="%f"/>', x, l_y, x+15, l_y, item.color, item.stroke_w)
                if item.mark == "dot" then
                    s = s .. string.format('<circle cx="%f" cy="%f" r="2.5" fill="%s"/>', x+7.5, l_y, item.color)
                end
            elseif item.type == "rect" then
                s = s .. string.format('<rect x="%f" y="%f" width="15" height="10" fill="%s" opacity="0.65" rx="1" ry="1"/>', x, l_y-5, item.color)
            end
            s = s .. string.format('<text x="%f" y="%f" font-size="9" fill="#54595d" text-anchor="middle">%s</text>', x+7.5, l_y+15, item.label)
        end
        return s
    end
    svg = svg .. drawLegend()
   
    -- 背景与网格 (细灰色线)
    -- 温度主网格
    local grid_step = 10
    for v = final_min_t, final_max_t, grid_step do
         local y = ty(v)
         local y = ty(v)
         svg = svg .. string.format('<line x1="%f" y1="%f" x2="%f" y2="%f" stroke="#eee" stroke-width="1"/>', padding.l, y, w - padding.r, y)
         svg = svg .. string.format('<line x1="%d" y1="%d" x2="%d" y2="%d" stroke="#eee" stroke-width="1" />', padding.l, y, w - padding.r, y)
        -- 左侧温度轴标签
         svg = svg .. string.format('<text x="%d" y="%d" font-size="10" fill="#999" text-anchor="end">%d°</text>', padding.l - 5, y + 4, v)
         svg = svg .. string.format('<text x="%f" y="%f" font-size="10" fill="#999" text-anchor="end">%d°</text>', padding.l - 5, y + 4, v)
     end
     end
    -- 右侧降水轴刻度 (unaligned, just labels)
 
    local precip_label_step = final_max_p / 6
    for i = 0, 6 do
        local v = math.floor(i * precip_label_step)
        local y = py(v)
        svg = svg .. string.format('<text x="%f" y="%f" font-size="10" fill="#999" text-anchor="start">%dmm</text>', w - padding.r + 5, y + 4, v)
    end
   
    -- ==========================================
    -- 6. 核心绘图:降水柱(淡蓝色,优化视效)
    -- ==========================================
     for i, p_val in ipairs(d.rain) do
     for i, p_val in ipairs(d.rain) do
         local bh = (p_val / final_max_p * chart_h)
         local bh = (p_val / max_p * chart_h)
        -- 加宽,微圆角,透明度 0.65,颜色 #4da6ff
         svg = svg .. string.format('<rect x="%f" y="%f" width="%f" height="%f" fill="#cee" opacity="0.8" />', tx(i)-8, py(p_val), 16, bh)
         svg = svg .. string.format('<rect x="%f" y="%f" width="%f" height="%f" fill="#4da6ff" opacity="0.65" rx="1" ry="1"/>', tx(i)-10, py(p_val), 20, bh)
     end
     end
   
 
    -- ==========================================
    -- 7. 核心绘图:温度折线(均温加粗,清晰点标记)
    -- ==========================================
     local function drawLine(data, color, width)
     local function drawLine(data, color, width)
         local pts = ""
         local pts = ""
         for i, v in ipairs(data) do pts = pts .. string.format("%f,%f ", tx(i), ty(v)) end
         for i, v in ipairs(data) do pts = pts .. string.format("%f,%f ", tx(i), ty(v)) end
         return string.format('<polyline points="%s" fill="none" stroke="%s" stroke-width="%f" />', pts, color, width)
         return string.format('<polyline points="%s" fill="none" stroke="%s" stroke-width="%d" />', pts, color, width)
     end
     end
      
      
    -- 平均均温 (橙色, 最醒目)
     svg = svg .. drawLine(d.high, "#f44", 2)
     svg = svg .. drawLine(d.mean, "#ff7f00", 2.5)
     svg = svg .. drawLine(d.mean, "#f90", 2)
    -- 平均高温 (红色)
     svg = svg .. drawLine(d.low, "#48f", 2)
     svg = svg .. drawLine(d.high, "#e41a1c", 1.5)
    -- 平均低温 (蓝色)
     svg = svg .. drawLine(d.low, "#377eb8", 1.5)
   
    -- 为所有温度点添加数据标记点
    for i = 1, 12 do
        svg = svg .. string.format('<circle cx="%f" cy="%f" r="2.5" fill="#e41a1c"/>', tx(i), ty(d.high[i]))
        svg = svg .. string.format('<circle cx="%f" cy="%f" r="2.5" fill="#ff7f00"/>', tx(i), ty(d.mean[i]))
        svg = svg .. string.format('<circle cx="%f" cy="%f" r="2.5" fill="#377eb8"/>', tx(i), ty(d.low[i]))
    end


    -- 月份标签
     for i, m in ipairs({"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}) do
     for i, m in ipairs({"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}) do
         svg = svg .. string.format('<text x="%f" y="%d" font-size="10" fill="#666" text-anchor="middle">%s月</text>', tx(i), h - padding.b + 15, m)
         svg = svg .. string.format('<text x="%f" y="%d" font-size="10" fill="#666" text-anchor="middle">%s</text>', tx(i), h - padding.b + 15, m)
     end
     end
    svg = svg .. string.format('<text x="%d" y="%d" font-size="10" fill="#666" text-anchor="middle">坐标轴:°C | mm</text>', w/2, h - padding.b + 32)
   
     svg = svg .. '</svg>'
     svg = svg .. '</svg>'


    -- 封装 HTML 并输出,注意严格清理开头的空格以防 <pre> 丑框
     local raw_svg = frame:extensionTag('html', svg)
     local raw_svg = frame:extensionTag('html', svg)
     local koppen = calculateKoppen(d.mean, d.rain)
     local koppen = calculateKoppen(d.mean, d.rain)
第367行: 第254行:
local tableText = table.concat(results)
local tableText = table.concat(results)


local uid = "weatherbox_" .. tostring(math.random(100000, 999999))
local raw_svg, koppen = makeGraph(frame, args)
if stripToNil(args['hide graph']) == 'Y' or stripToNil(args['hide graph']) == 'y' then
if stripToNil(args['hide graph']) == 'Y' or stripToNil(args['hide graph']) == 'y' then
    return '<div class="weather-box-container" style="clear:both;">\n' .. tableText .. '\n</div>'
    return '<div class="weather-box-container" style="clear:both;">\n' .. tableText .. '\n</div>'
else
else
    -- 这里已经使用 string.format 修复了 %s 不转义的报错!
    local uid = "weatherbox_" .. tostring(math.random(100000, 999999))
    local raw_svg, koppen = makeGraph(frame, args)
    local loc_text = stripToNil(args.location) or "气候图表"
   
    -- 注意:以下 HTML 字符串不能在开头加空格,以防止 Wiki 渲染成 <pre> 标签
    return string.format('<div class="weather-box-container" style="margin:1em 0; clear:both;">\n' ..
    return string.format('<div class="weather-box-container" style="margin:1em 0; clear:both;">\n' ..
'<div style="text-align:right; margin-bottom:5px;">\n' ..
'<div style="text-align:right; margin-bottom:5px;">\n' ..
第386行: 第274行:
'%s\n' ..
'%s\n' ..
'</div>\n' ..
'</div>\n' ..
'</div>', uid, uid, stripToNil(args.location) or "气候图表", raw_svg, koppen, uid, tableText)
'</div>', uid, uid, loc_text, raw_svg, koppen, uid, tableText)
end
end
end
end

2026年5月12日 (二) 17:13的版本

此模块的文档可以在模块:Weather box/doc创建

local _precision = require('Module:Math')._precision
local function precision(text)
	local success, result = pcall(_precision, text)
	if success then
		return result
	end
	return 0
end

local function stripToNil(text)
	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'
	else
		if 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
	end
	local second = 'Avs'
	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 = '来源1:' .. source1 .. '\n' .. result .. '来源2:' .. source2
		else
			text = '来源:' .. (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)
	parm = (parm or ''):lower()
	return not (parm == 'no' or parm == 'n' or parm == '')
end

-- =========================================================================
-- 图表核心计算与渲染
-- =========================================================================
local function calculateKoppen(temps, precips)
    local t_mean_annual, p_total, t_hot, t_cold = 0, 0, -99, 99
    local s_p, w_p = 0, 0
    for i = 1, 12 do
        local t, p = tonumber(temps[i]) or 0, tonumber(precips[i]) or 0
        t_mean_annual = t_mean_annual + t
        p_total = p_total + p
        if t > t_hot then t_hot = t end
        if t < t_cold then t_cold = t end
        if i >= 4 and i <= 9 then s_p = s_p + p else w_p = w_p + p end
    end
    t_mean_annual = t_mean_annual / 12
    local pth = 20 * t_mean_annual + (s_p > p_total * 0.7 and 280 or (w_p > p_total * 0.7 and 0 or 140))
    if p_total < pth then
        local suffix = t_mean_annual >= 18 and "h" or "k"
        return p_total < pth / 2 and "BW" .. suffix or "BS" .. suffix
    end
    if t_cold >= 18 then return "A" end
    if t_hot >= 10 then return t_cold > -3 and "C" or "D" end
    return t_hot > 0 and "ET" or "EF"
end

local function makeGraph(frame, args)
    local months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
    local d = { high = {}, mean = {}, low = {}, rain = {} }
    local max_t, max_p = 50, 100 

    for i, m in ipairs(months) do
        d.high[i] = tonumber(args[m .. ' high C']) or 0
        d.mean[i] = tonumber(args[m .. ' mean C']) or 0
        d.low[i] = tonumber(args[m .. ' low C']) or 0
        d.rain[i] = tonumber(args[m .. ' precipitation mm']) or tonumber(args[m .. ' rain mm']) or 0
        
        if d.high[i] > max_t then max_t = d.high[i] end
        if d.rain[i] > max_p then max_p = d.rain[i] end
    end
    
    max_t = math.ceil(max_t / 10) * 10
    max_p = math.ceil(max_p / 20) * 20

    local w, h = 300, 400
    local padding = { t = 40, b = 40, l = 40, r = 40 }
    local chart_w, chart_h = w - padding.l - padding.r, h - padding.t - padding.b

    local function ty(v) return h - padding.b - (v / max_t * chart_h) end
    local function py(v) return h - padding.b - (v / max_p * chart_h) end
    local function tx(i) return padding.l + (i - 0.5) * (chart_w / 12) end

    local svg = string.format('<svg width="100%%" viewBox="0 0 %d %d" style="background:#fff; font-family:sans-serif; border:1px solid #ddd;">', w, h)
    
    for v = 0, max_t, 10 do
        local y = ty(v)
        svg = svg .. string.format('<line x1="%d" y1="%d" x2="%d" y2="%d" stroke="#eee" stroke-width="1" />', padding.l, y, w - padding.r, y)
        svg = svg .. string.format('<text x="%d" y="%d" font-size="10" fill="#999" text-anchor="end">%d°</text>', padding.l - 5, y + 4, v)
    end

    for i, p_val in ipairs(d.rain) do
        local bh = (p_val / max_p * chart_h)
        svg = svg .. string.format('<rect x="%f" y="%f" width="%f" height="%f" fill="#cee" opacity="0.8" />', tx(i)-8, py(p_val), 16, bh)
    end

    local function drawLine(data, color, width)
        local pts = ""
        for i, v in ipairs(data) do pts = pts .. string.format("%f,%f ", tx(i), ty(v)) end
        return string.format('<polyline points="%s" fill="none" stroke="%s" stroke-width="%d" />', pts, color, width)
    end
    
    svg = svg .. drawLine(d.high, "#f44", 2)
    svg = svg .. drawLine(d.mean, "#f90", 2)
    svg = svg .. drawLine(d.low, "#48f", 2)

    for i, m in ipairs({"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}) do
        svg = svg .. string.format('<text x="%f" y="%d" font-size="10" fill="#666" text-anchor="middle">%s</text>', tx(i), h - padding.b + 15, m)
    end
    svg = svg .. '</svg>'

    local raw_svg = frame:extensionTag('html', svg)
    local koppen = calculateKoppen(d.mean, d.rain)
    
    return raw_svg, koppen
end
-- =========================================================================

local function getDefinitions(frame, args, options)
	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 location = _if('location', args.location, '{{{location}}}') 
	local navbarText
	if not stripToNil(args.open) and stripToNil(args.name) then
		local navbar = require('Module:Navbar')._navbar
		navbarText = navbar({' ' .. location, args.name, collapsible=1})
	end
	return {
'{| class="wikitable ' .. _if('open', '', 'mw-collapsible' .. _if('collapsed', ' mw-collapsed')) .. '" style="width:' .. _ifset('width', '100%') .. '; text-align:center; line-height: 1.2em; margin: 0 auto;"' ..
_if('open', '\n|+ ' .. location, '\n|-\n!colspan="14" | ' .. _if('name', navbarText, ' ' .. location)) ..
[=[

|-
!scope="row" |月份
!scope="col" |1月
!scope="col" |2月
!scope="col" |3月
!scope="col" |4月
!scope="col" |5月
!scope="col" |6月
!scope="col" |7月
!scope="col" |8月
!scope="col" |9月
!scope="col" |10月
!scope="col" |11月
!scope="col" |12月
!scope="col" style="border-left-width:medium" |全年
]=],
{WANTROW = _ifany(' maximum humidex') and (_ifset('metric first') or options.wantSingleLine), mode = 'basic', group_name = 'maximum humidex', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '历史最高[[湿热指数]]', annual_mode = 'max'},
{WANTROW = _ifany(' record high C') or _ifany(' record high F'), mode = 'temperature', group_name = 'record high', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '历史最高温 °' .. _if('metric first', 'C', 'F') .. _ifSingle('(°' .. _if('metric first', 'F', 'C') .. ')'), annual_mode = 'max'},
{WANTROW = _ifany(' avg record high C') or _ifany(' avg record high F'), mode = 'temperature', group_name = 'avg record high', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '平均最高温 °' .. _if('metric first', 'C', 'F') .. _ifSingle('(°' .. _if('metric first', 'F', 'C') .. ')'), annual_mode = 'max'},
{WANTROW = _ifany(' high C') or _ifany(' high F'), mode = 'temperature', group_name = 'high', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '平均高温 °' .. _if('metric first', 'C', 'F') .. _ifSingle('(°' .. _if('metric first', 'F', 'C') .. ')'), annual_mode = 'avg'},
{WANTROW = _ifany(' mean C') or _ifany(' mean F'), mode = 'temperature', group_name = 'mean', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '日均气温 °' .. _if('metric first', 'C', 'F') .. _ifSingle('(°' .. _if('metric first', 'F', 'C') .. ')'), annual_mode = 'avg'},
{WANTROW = _ifany(' low C') or _ifany(' low F'), mode = 'temperature', group_name = 'low', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '平均低温 °' .. _if('metric first', 'C', 'F') .. _ifSingle('(°' .. _if('metric first', 'F', 'C') .. ')'), annual_mode = 'avg'},
{WANTROW = _ifany(' avg record low C') or _ifany(' avg record low F'), mode = 'temperature', group_name = 'avg record low', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '平均最低温 °' .. _if('metric first', 'C', 'F') .. _ifSingle('(°' .. _if('metric first', 'F', 'C') .. ')'), annual_mode = 'min'},
{WANTROW = _ifany(' record low C') or _ifany(' record low F'), mode = 'temperature', group_name = 'record low', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '历史最低温 °' .. _if('metric first', 'C', 'F') .. _ifSingle('(°' .. _if('metric first', 'F', 'C') .. ')'), annual_mode = 'min'},
{WANTROW = _ifany(' chill') and (_ifset('metric first') or options.wantSingleLine), mode = 'basic', group_name = 'chill', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '历史最低[[風寒指數]]', annual_mode = 'min'},
{WANTROW = _ifany(' precipitation cm') or _ifany(' precipitation mm') or _ifany(' precipitation inch'), mode = 'precipitation', group_name = 'precipitation', color_scheme = _ifset('precipitation colour', 'p'), date_mode = true, scale_factor = '1', prefer_cm = precision(_ifset('Jan precipitation inch', '0')) < 1, label = makeLabel(args, options, true, '平均[[降水量]]', 'precipitation'), annual_mode = 'sum'},
{WANTROW = _ifany(' rain cm') or _ifany(' rain mm') or _ifany(' rain inch'), mode = 'precipitation', group_name = 'rain', color_scheme = _ifset('rain colour', 'p'), date_mode = true, scale_factor = '1', prefer_cm = precision(_ifset('Jan rain inch', '0')) < 1, label = makeLabel(args, options, true, '平均降雨量', 'rain'), annual_mode = 'sum'},
{WANTROW = _ifany(' snow cm') or _ifany(' snow mm') or _ifany(' snow inch'), mode = 'precipitation', group_name = 'snow', prefer_cm = true, color_scheme = _ifset('snow colour', 'p'), date_mode = true, scale_factor = '1', label = makeLabel(args, options, true, '平均降雪量', 'snow'), annual_mode = 'sum'},
{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 = _ifset('snow colour', 'p'), scale_factor = '0.2', label = makeLabel(args, options, true, '平均最大雪深', '雪深'), annual_mode = 'max'},
{WANTROW = not options.wantSingleLine and _ifany(' maximum humidex'), mode = 'basic', group_name = 'maximum humidex', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '[[湿热指数]]', annual_mode = 'max', second_line = true},
{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 = _ifset('temperature colour', 't'), scale_factor = '1', label = '历史最高温 °' .. _if('metric first', 'F', 'C'), annual_mode = 'max'},
{WANTROW = not options.wantSingleLine and (_ifany(' high C') or _ifany(' high F')), mode = 'temperature', group_name = 'high', second_line = true, color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '平均高温 °' .. _if('metric first', 'F', 'C'), annual_mode = 'avg'},
{WANTROW = not options.wantSingleLine and (_ifany(' mean C') or _ifany(' mean F')), mode = 'temperature', group_name = 'mean', second_line = true, color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '日均气温 °' .. _if('metric first', 'F', 'C'), show = _if('metric first', '2', '1'), annual_mode = 'avg'},
{WANTROW = not options.wantSingleLine and (_ifany(' low C') or _ifany(' low F')), mode = 'temperature', group_name = 'low', second_line = true, color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '平均低温 °' .. _if('metric first', 'F', 'C'), show = _if('metric first', '2', '1'), annual_mode = 'avg'},
{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 = _ifset('temperature colour', 't'), scale_factor = '1', label = '历史最低温 °' .. _if('metric first', 'F', 'C'), show = _if('metric first', '2', '1'), annual_mode = 'min'},
{WANTROW = not options.wantSingleLine and (_ifany(' chill') and _if('metric first')), mode = 'basic', group_name = 'chill', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '[[風寒指數]]', annual_mode = 'min'},
{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 = _ifset('precipitation colour', 'p'), date_mode = true, scale_factor = '1', prefer_cm = precision(_ifset('Jan precipitation inch', '0')) < 1, label = makeLabel(args, options, false, '平均[[降水量]]', 'precipitation'), annual_mode = 'sum'},
{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 = _ifset('rain colour', 'p'), date_mode = true, scale_factor = '1', prefer_cm = precision(_ifset('Jan rain inch', '0')) < 1, label = makeLabel(args, options, false, '平均降雨量', 'rain'), annual_mode = 'sum'},
{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 = _ifset('snow colour', 'p'), date_mode = true, scale_factor = '1', label = makeLabel(args, options, false, '平均降雪量', 'snow'), annual_mode = 'sum'},
{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 = _ifset('snow colour', 'p'), scale_factor = '0.2', label = makeLabel(args, options, false, '平均最大雪深', '雪深'), annual_mode = 'max'},
{WANTROW = _ifany(' precipitation days'), mode = 'basic', group_name = 'precipitation days', color_scheme = _ifset('precip days colour', 'd'), date_mode = true, scale_factor = '1', label = '平均降水天数' .. _if('unit precipitation days', '<span style="font-size:90%;" class="nowrap">(≥ ' .. _ifset('unit precipitation days', '') .. ')</span>'), annual_mode = 'sum'},
{WANTROW = _ifany(' rain days'), mode = 'basic', group_name = 'rain days', color_scheme = _ifset('precip days colour', 'd'), date_mode = true, scale_factor = '1', label = '平均降雨天数' .. _if('unit rain days', '<span style="font-size:90%;" class="nowrap">(≥ ' .. _ifset('unit rain days', '') .. ')</span>'), annual_mode = 'sum'},
{WANTROW = _ifany(' snow days'), mode = 'basic', group_name = 'snow days', color_scheme = _ifset('precip days colour', 'd'), date_mode = true, scale_factor = '1', label = '平均降雪天数' .. _if('unit snow days', '<span style="font-size:90%;" class="nowrap">(≥ ' .. _ifset('unit snow days', '') .. ')</span>'), annual_mode = 'sum'},
{WANTROW = _ifany(' humidity'), mode = 'basic', group_name = 'humidity', color_scheme = _ifset('humidity colour', 'h'), scale_factor = '1', label = '平均[[相對濕度]](%)' .. _if('time day', ' <span style="font-size:90%;" class="nowrap">(' .. _ifset('time day', '') .. ')</span>') .. _if('daily', ' <span style="font-size:90%;" class="nowrap">(日均)</span>'), annual_mode = 'avg'},
{WANTROW = _ifany(' afthumidity'), mode = 'basic', group_name = 'afthumidity', color_scheme = _ifset('humidity colour', 'h'), scale_factor = '1', label = '平均午後[[相對濕度]](%)' .. _if('time day', ' <span style="font-size:90%;" class="nowrap">(' .. _ifset('time day', '') .. ')</span>') .. _if('daily', ' <span style="font-size:90%;" class="nowrap">(日均)</span>'), annual_mode = 'avg'},
{WANTROW = _ifany(' dew point C') or _ifany(' dew point F'), mode = 'temperature', group_name = 'dew point', color_scheme = _ifset('temperature colour', 't'), scale_factor = '1', label = '平均[[露点]] °' .. _if('metric first', 'C', 'F') .. _ifSingle('(°' .. _if('metric first', 'F', 'C') .. ')'), annual_mode = 'avg'},
{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 = _ifset('temperature colour', 't'), scale_factor = '1', label = '平均[[露点]] °' .. _if('metric first', 'F', 'C'), show = _if('metric first', '2', '1'), annual_mode = 'avg'},
{WANTROW = _ifany(' sun'), mode = 'basic', group_name = 'sun', color_scheme = _ifset('sun colour', 's'), date_mode = true, scale_factor = '1', label = '月均[[日照時數]]', annual_mode = 'sum'},
{WANTROW = _ifany('d sun'), mode = 'basic', group_name = 'd sun', color_scheme = _ifset('sun colour', 's'), include_space = false, scale_factor = '30.44', label = '日均[[日照時數]]', annual_mode = 'avg'},
{WANTROW = _ifany(' light'), mode = 'basic', group_name = ' light', color_scheme = _ifset('sun colour', 's'), include_space = false, scale_factor = '30.44', label = '日均[[白天|昼长]]', annual_mode = 'avg'},
{WANTROW = _ifany(' percentsun'), mode = 'basic', group_name = 'percentsun', color_scheme = _ifset('sun colour', 's'), scale_factor = '7.2', label = '[[日照時數|可照]]百分比', annual_mode = 'avg'},
{WANTROW = _ifany(' uv'), mode = 'basic', group_name = 'uv', color_scheme = _ifset('uv colour', 'u'), scale_factor = '1', label = '平均[[紫外线指数]]', annual_mode = 'avg'},
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
	local tableText = table.concat(results)

	if stripToNil(args['hide graph']) == 'Y' or stripToNil(args['hide graph']) == 'y' then
	    return '<div class="weather-box-container" style="clear:both;">\n' .. tableText .. '\n</div>'
	else
	    local uid = "weatherbox_" .. tostring(math.random(100000, 999999))
	    local raw_svg, koppen = makeGraph(frame, args)
	    local loc_text = stripToNil(args.location) or "气候图表"
	    
	    -- 注意:以下 HTML 字符串不能在开头加空格,以防止 Wiki 渲染成 <pre> 标签
	    return string.format('<div class="weather-box-container" style="margin:1em 0; clear:both;">\n' ..
'<div style="text-align:right; margin-bottom:5px;">\n' ..
'<span class="mw-customtoggle-%s" style="font-size:12px; color:#0645ad; cursor:pointer; user-select:none; background:#eaecf0; padding:2px 8px; border:1px solid #a2a9b1; border-radius:2px;">[ 切换 表格 / 图表 ]</span>\n' ..
'</div>\n' ..
'<div class="mw-collapsible" id="mw-customcollapsible-%s" style="margin:0 auto; width:300px; padding:10px; background:#f8f9fa; border:1px solid #c8ccd1;">\n' ..
'<div style="text-align:center; font-weight:bold; font-size:13px; margin-bottom:10px; color:#202122;">%s</div>\n' ..
'%s\n' ..
'<div style="font-size:12px; text-align:center; margin-top:10px; color:#54595d;">柯本气候分类:<span style="font-weight:bold; color:#2a4b8d;">%s</span></div>\n' ..
'</div>\n' ..
'<div class="mw-collapsible mw-collapsed" id="mw-customcollapsible-%s" style="overflow-x:auto; width:100%%;">\n' ..
'%s\n' ..
'</div>\n' ..
'</div>', uid, uid, loc_text, raw_svg, koppen, uid, tableText)
	end
end

return {
	main = main,
}