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

模块:Weather box

来自Blockland Wiki

此模块的文档可以在模块: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 = '英寸'
	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

-- =========================================================================
-- 完整的柯本气候分类 (Köppen-Geiger) 算法
-- =========================================================================
local function calculateKoppen(temps, precips)
    local t_mean_annual, p_total, t_hot, t_cold = 0, 0, -99, 99
    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
    end
    t_mean_annual = t_mean_annual / 12

    if p_total == 0 then return "BWk" end -- 极端缺水降级处理

    -- 动态判断南北半球 (北半球 4-9月较热)
    local t_apr_sep = 0
    for i = 4, 9 do t_apr_sep = t_apr_sep + (tonumber(temps[i]) or 0) end
    local is_northern = t_apr_sep > (t_mean_annual * 6)

    -- 计算冬夏季降水量与极值
    local p_summer, p_winter = 0, 0
    local p_min_summer, p_max_summer = 9999, 0
    local p_min_winter, p_max_winter = 9999, 0

    for i = 1, 12 do
        local p = tonumber(precips[i]) or 0
        local is_summer_month = is_northern and (i >= 4 and i <= 9) or (not is_northern and (i < 4 or i > 9))
        if is_summer_month then
            p_summer = p_summer + p
            if p < p_min_summer then p_min_summer = p end
            if p > p_max_summer then p_max_summer = p end
        else
            p_winter = p_winter + p
            if p < p_min_winter then p_min_winter = p end
            if p > p_max_winter then p_max_winter = p end
        end
    end

    -- B 类干旱气候阈值判定
    local pth = 20 * t_mean_annual
    if (p_summer / p_total) >= 0.7 then
        pth = pth + 280
    elseif (p_winter / p_total) >= 0.7 then
        pth = pth + 0
    else
        pth = pth + 140
    end

    if p_total < pth then
        local b_type = p_total < (pth / 2) and "BW" or "BS"
        return b_type .. (t_mean_annual >= 18 and "h" or "k")
    end

    -- A 类热带气候
    if t_cold >= 18 then
        local p_min = 9999
        for i = 1, 12 do if (tonumber(precips[i]) or 0) < p_min then p_min = tonumber(precips[i]) or 0 end end
        if p_min >= 60 then return "Af" end
        if p_min >= 100 - (p_total / 25) then return "Am" end
        return (p_summer < p_winter) and "As" or "Aw"
    end

    -- E 类极地气候
    if t_hot < 10 then
        return t_hot > 0 and "ET" or "EF"
    end

    -- C 类与 D 类温带/大陆性气候
    local main_type = t_cold >= -3 and "C" or "D"
    local second_letter = "f"
    
    if p_min_summer < 40 and p_max_winter >= 3 * p_min_summer then
        second_letter = "s"
    elseif p_max_summer >= 10 * p_min_winter then
        second_letter = "w"
    end

    local months_above_10 = 0
    for i = 1, 12 do
        if (tonumber(temps[i]) or 0) >= 10 then months_above_10 = months_above_10 + 1 end
    end

    local third_letter = "a"
    if t_hot < 22 then
        if months_above_10 >= 4 then
            third_letter = "b"
        elseif t_cold >= -38 then
            third_letter = "c"
        else
            third_letter = "d"
        end
    else
        if t_cold < -38 then third_letter = "d" end
    end

    return main_type .. second_letter .. third_letter
end

-- =========================================================================
-- 图表渲染 (3:4比例, 原生Wiki精调排版)
-- =========================================================================
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, min_t = 40, -20
    local max_p = 600

    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.low[i] < min_t then min_t = d.low[i] end
        if d.rain[i] > max_p then max_p = d.rain[i] end
    end
    
    max_t = math.ceil(max_t / 10) * 10
    min_t = math.floor(min_t / 10) * 10
    max_p = math.ceil(max_p / 100) * 100

    -- 精调过的经典色系 (避免刺眼,更舒适)
    local c_high = stripToNil(args['graph high colour']) or "#d32f2f"
    local c_mean = stripToNil(args['graph mean colour']) or "#f57c00"
    local c_low = stripToNil(args['graph low colour']) or "#1976d2"
    local c_precip = stripToNil(args['graph precip colour']) or "#90caf9"

    -- 3:4 比例固定尺寸 (宽300,高400)
    local w, h = 300, 400
    local padding = { t = 45, b = 30, l = 35, r = 35 }
    local chart_w, chart_h = w - padding.l - padding.r, h - padding.t - padding.b

    local function ty(v) return h - padding.b - ((v - min_t) / (max_t - min_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="font-family: sans-serif; display: block; margin: 0 auto;">', w, h)
    
    -- 图例
    local cx = w / 2
    svg = svg .. '<g font-size="11" fill="#202122">'
    svg = svg .. '<rect x="' .. (cx - 96) .. '" y="16" width="10" height="10" fill="' .. c_precip .. '" opacity="0.6"/>'
    svg = svg .. '<text x="' .. (cx - 82) .. '" y="25">降水</text>'
    
    svg = svg .. '<line x1="' .. (cx - 40) .. '" y1="21" x2="' .. (cx - 25) .. '" y2="21" stroke="' .. c_high .. '" stroke-width="2.5"/>'
    svg = svg .. '<text x="' .. (cx - 20) .. '" y="25">高温</text>'
    
    svg = svg .. '<line x1="' .. (cx + 10) .. '" y1="21" x2="' .. (cx + 25) .. '" y2="21" stroke="' .. c_mean .. '" stroke-width="2.5"/>'
    svg = svg .. '<text x="' .. (cx + 30) .. '" y="25">均温</text>'
    
    svg = svg .. '<line x1="' .. (cx + 60) .. '" y1="21" x2="' .. (cx + 75) .. '" y2="21" stroke="' .. c_low .. '" stroke-width="2.5"/>'
    svg = svg .. '<text x="' .. (cx + 80) .. '" y="25">低温</text>'
    svg = svg .. '</g>'

    -- 外框与背景
    svg = svg .. string.format('<rect x="%d" y="%d" width="%d" height="%d" fill="#ffffff" stroke="#a2a9b1" stroke-width="1" />', padding.l, padding.t, chart_w, chart_h)

    -- 月份分界线
    for i = 1, 11 do
        local vx = padding.l + i * (chart_w / 12)
        svg = svg .. string.format('<line x1="%f" y1="%d" x2="%f" y2="%d" stroke="#f0f0f0" stroke-width="1" />', vx, padding.t, vx, h - padding.b)
    end

    -- Y轴横向网格与文字 (左侧温度)
    for v = min_t, max_t, 10 do
        local y = ty(v)
        if y >= padding.t and y <= h - padding.b then
            local is_zero = (v == 0)
            local stroke_color = is_zero and "#999999" or "#eaeaea"
            local stroke_width = is_zero and "1.5" or "1"
            svg = svg .. string.format('<line x1="%d" y1="%f" x2="%d" y2="%f" stroke="%s" stroke-width="%s" />', padding.l, y, w - padding.r, y, stroke_color, stroke_width)
            svg = svg .. string.format('<text x="%d" y="%f" dy="0.3em" font-size="10" fill="#54595d" text-anchor="end">%d°</text>', padding.l - 4, y, v)
        end
    end
    
    -- 右侧降水轴(固定分为6格与温度网格对齐)
    local p_step = max_p / 6
    for v = 0, max_p, p_step do
        local y = py(v)
        if y >= padding.t and y <= h - padding.b then
            svg = svg .. string.format('<text x="%d" y="%f" dy="0.3em" font-size="10" fill="#54595d" text-anchor="start">%d</text>', w - padding.r + 4, y, math.floor(v))
        end
    end

    -- 降水量柱状图
    local bar_w = (chart_w / 12) * 0.85
    for i, p_val in ipairs(d.rain) do
        local bh = (p_val / max_p * chart_h)
        local y = py(p_val)
        svg = svg .. string.format('<rect x="%f" y="%f" width="%f" height="%f" fill="%s" opacity="0.6"><title>%d月 降水量: %s mm</title></rect>', tx(i) - bar_w/2, y, bar_w, bh, c_precip, i, tostring(p_val))
    end

    -- 绘制纯平滑折线
    local function drawLine(data, color)
        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="2.5" stroke-linejoin="round" stroke-linecap="round" />', pts, color)
    end
    
    svg = svg .. drawLine(d.high, c_high)
    svg = svg .. drawLine(d.mean, c_mean)
    svg = svg .. drawLine(d.low, c_low)

    -- X轴月份标签
    for i = 1, 12 do
        svg = svg .. string.format('<text x="%f" y="%d" font-size="11" fill="#202122" text-anchor="middle">%d</text>', tx(i), h - padding.b + 14, i)
    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)

    -- ==========================================================
    -- 新增:全局独立提取数据,用于 Cargo 存储
    -- ==========================================================
    local months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
    local mean_temps = {}
    local rain_precips = {}
    local annual_temp_sum = 0
    local annual_rain_sum = 0
    
    for i, m in ipairs(months) do
        local t = tonumber(args[m .. ' mean C']) or 0
        local p = tonumber(args[m .. ' precipitation mm']) or tonumber(args[m .. ' rain mm']) or 0
        mean_temps[i] = t
        rain_precips[i] = p
        annual_temp_sum = annual_temp_sum + t
        annual_rain_sum = annual_rain_sum + p
    end
    
    -- 计算年均温 (保留一位小数) 和年降水量 (保留一位小数)
    local annual_temp_avg = string.format("%.1f", annual_temp_sum / 12)
    local annual_rain_total = string.format("%.1f", annual_rain_sum)
    local koppen = calculateKoppen(mean_temps, rain_precips)
    
    -- 气候类型全称映射字典 (直接带有内链语法)
    local koppen_details = {
Af  = "热带雨林气候 (Af)",
        Am  = "热带季风气候 (Am)",
        Aw  = "热带干湿季气候 (Aw)",
        As  = "热带夏干气候 (As)",
        BWh = "热带沙漠气候 (BWh)",
        BWk = "温带沙漠气候 (BWk)",
        BSh = "热带半干旱气候 (BSh)",
        BSk = "温带半干旱气候 (BSk)",
        Csa = "暖夏型地中海气候 (Csa)",
        Csb = "凉夏型地中海气候 (Csb)",
        Csc = "冷夏型地中海气候 (Csc)",
        Cfa = "亚热带湿润气候 (Cfa)",
        Cwa = "亚热带季风气候 (Cwa)",
        Cwb = "亚热带高原气候 (Cwb)",
        Cwc = "冷夏型副热带干冬气候 (Cwc)",
        Cfb = "温带海洋性气候 (Cfb)",
        Cfc = "副极地海洋性气候 (Cfc)",
        Dfa = "暖夏型温带大陆性湿润气候 (Dfa)",
        Dfb = "凉夏型温带大陆性湿润气候 (Dfb)",
        Dwa = "暖夏型温带季风气候 (Dwa)",
        Dwb = "凉夏型温带季风气候 (Dwb)",
        Dsa = "暖夏型温带大陆性夏干气候 (Dsa)",
        Dsb = "凉夏型温带大陆性夏干气候 (Dsb)",
        Dfc = "亚寒带针叶林气候 (Dfc)",
        Dfd = "极寒亚寒带针叶林气候 (Dfd)",
        Dwc = "亚寒带干冬气候 (Dwc)",
        Dwd = "极寒亚寒带干冬气候 (Dwd)",
        Dsc = "亚寒带夏干气候 (Dsc)",
        Dsd = "极寒亚寒带夏干气候 (Dsd)",
        ET  = "苔原气候 (ET)",
        EF  = "冰原气候 (EF)"
    }
    
    -- 获取完整格式的气候文本
    local detailed_koppen = koppen_details[koppen] or koppen
    
    -- 获取当前页面名称 (等同于 {{PAGENAME}})
    local page_name = mw.title.getCurrentTitle().text

    -- 构造 Cargo 存储的解析器函数字符串 (存入完整的详细文本)
    local cargo_store_text = string.format(
        '{{#cargo_store:_table=WeatherData|CityName=%s|ClimateType=%s|AnnualTemp=%s|AnnualRainfall=%s}}',
        page_name, detailed_koppen, annual_temp_avg, annual_rain_total
    )
    -- 强制 MediaWiki 在后台立刻解析并执行这段代码(完成数据库写入)
    frame:preprocess(cargo_store_text)
    -- ==========================================================

    local final_output = ''

    if stripToNil(args['hide graph']) == 'Y' or stripToNil(args['hide graph']) == 'y' then
        final_output = '<div class="weather-box-table" style="clear:both;">\n' .. tableText .. '\n</div>'
    else
        local raw_svg, _ = makeGraph(frame, args) 
        local loc_text = stripToNil(args.location) or "气候图表"
        local uid = "weatherbox_" .. tostring(math.random(100000, 999999))
        
        -- 图表下方文本同样使用带内链的详细文本
        local graph_html = string.format('<div class="weather-box-chart mw-collapsible mw-collapsed" id="mw-customcollapsible-%s-chart" style="float: right; clear: right; margin: 0.5em 0 1em 1em; width: 312px; background-color: #f8f9fa; border: 1px solid #a2a9b1; padding: 5px; font-size: 90%%; box-sizing: border-box;">\n' ..
'<div style="text-align: right; margin-bottom: 4px;"><span class="mw-customtoggle-%s-chart mw-customtoggle-%s-table" style="color: #0645ad; cursor: pointer;">[切换视图]</span></div>\n' ..
'<div style="text-align: center; font-weight: bold; margin-bottom: 6px;">%s</div>\n' ..
'<div style="margin: 0 auto; width: 300px;">%s</div>\n' ..
'<div style="text-align: center; margin-top: 6px;">柯本气候分类:<b>%s</b></div>\n' ..
'</div>', uid, uid, uid, loc_text, raw_svg, detailed_koppen)

        local table_html = string.format('<div class="weather-box-table mw-collapsible" id="mw-customcollapsible-%s-table" style="clear:both; padding-top:1em;">\n' ..
'<div style="text-align: right; margin-bottom: 5px;">\n' ..
'<span class="mw-customtoggle-%s-chart mw-customtoggle-%s-table" style="color: #0645ad; cursor: pointer; font-size: 90%%;">[ 切换 表格 / 图表 ]</span>\n' ..
'</div>\n' ..
'%s\n' ..
'</div>', uid, uid, uid, tableText)

        final_output = table_html .. '\n' .. graph_html
    end
    
    return final_output
end

return {
    main = main,
}