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

模块:WeatherGraph:修订间差异

来自Blockland Wiki
无编辑摘要
无编辑摘要
 
(未显示同一用户的1个中间版本)
第1行: 第1行:
local p = {}
local p = {}


-- 柯本气候分类计算器
-- 柯本气候分类计算逻辑(保持不变)
local function calculateKoppen(temps, precips)
local function calculateKoppen(temps, precips)
     local t_mean = 0
     local t_mean_annual, p_total, t_hot, t_cold = 0, 0, -99, 99
    local p_total = 0
     local s_p, w_p = 0, 0
    local t_hot = -99
    local t_cold = 99
     local p_min_summer = 9999
    local p_max_summer = 0
    local p_min_winter = 9999
    local p_max_winter = 0
   
    local summer_p_total = 0
    local winter_p_total = 0
 
     for i = 1, 12 do
     for i = 1, 12 do
         local t = tonumber(temps[i]) or 0
         local t, p = tonumber(temps[i]) or 0, tonumber(precips[i]) or 0
        local p = tonumber(precips[i]) or 0
         t_mean_annual = t_mean_annual + t
         t_mean = t_mean + t
         p_total = p_total + p
         p_total = p_total + p
         if t > t_hot then t_hot = t end
         if t > t_hot then t_hot = t end
         if t < t_cold then t_cold = 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
        -- 简单划分夏冬半季(北半球:4-9月为夏,10-3月为冬)
         if i >= 4 and i <= 9 then
            summer_p_total = summer_p_total + p
            if p < p_min_summer then p_min_summer = p end
            if p > p_max_summer then p_max_summer = p end
        else
            winter_p_total = winter_p_total + 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
    t_mean = t_mean / 12
 
    -- 计算干燥阈值 (Pth)
    local pth = 20 * t_mean
    if summer_p_total > (p_total * 0.7) then
        pth = pth + 280
    elseif winter_p_total > (p_total * 0.7) then
        pth = pth
    else
        pth = pth + 140
     end
     end
 
    t_mean_annual = t_mean_annual / 12
     -- B类:干旱气候
     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
     if p_total < pth then
         if p_total < (pth / 2) then
         local suffix = t_mean_annual >= 18 and "h" or "k"
            return t_mean >= 18 and "BWh (热带沙漠气候)" or "BWk (温带沙漠气候)"
         return p_total < pth / 2 and "BW" .. suffix or "BS" .. suffix
         else
            return t_mean >= 18 and "BSh (热带草原/半干旱气候)" or "BSk (温带草原/半干旱气候)"
        end
     end
     end
 
     if t_cold >= 18 then return "A" end
    -- A类:热带气候
     if t_hot >= 10 then return t_cold > -3 and "C" or "D" end
     if t_cold >= 18 then
     return t_hot > 0 and "ET" or "EF"
        if (p_min_winter < 60 and p_min_summer < 60) then return "Aw/As (热带干湿季气候)" end
        return "Af (热带雨林气候)"
    end
 
    -- C类与D类
     if t_hot >= 10 and t_cold > -3 and t_cold < 18 then
        return "C 类 (温带气候)"
    end
    if t_hot >= 10 and t_cold <= -3 then
        return "D 类 (大陆性气候)"
    end
 
    -- E类:极地气候
     if t_hot < 10 then
        return t_hot > 0 and "ET (苔原气候)" or "EF (冰原气候)"
    end
 
    return "未分类"
end
end


第80行: 第27行:
     local args = frame:getParent().args
     local args = frame:getParent().args
     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 temps = {}
    local precips = {}
      
      
     -- 提取并清理数据
     local d = { high = {}, mean = {}, low = {}, rain = {} }
    local max_t, max_p = 50, 100 -- 初始刻度上限
 
     for i, m in ipairs(months) do
     for i, m in ipairs(months) do
         table.insert(temps, tonumber(args[m .. ' mean C']) or 0)
         d.high[i] = tonumber(args[m .. ' high C']) or 0
         table.insert(precips, tonumber(args[m .. ' rain mm']) 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 .. ' 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
     end
      
      
     local koppen = calculateKoppen(temps, precips)
    -- 向上取整到10的倍数
     local title = args['location'] or "气候数据"
    max_t = math.ceil(max_t / 10) * 10
    max_p = math.ceil(max_p / 20) * 20
 
    -- SVG 绘图配置 (3:4 画幅设计: Width=300, Height=400)
    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;">', w, h)
      
      
     -- ========================================================
     -- 1. 绘制网格线
     -- 使用纯 CSS 绘制双轴图表(MediaWiki 原生完美兼容)
    for v = 0, max_t, 10 do
     -- ========================================================
        local y = ty(v)
     local chart_html = '<div style="position:relative; width:100%; height:200px; background:#f8f9fa; border-radius:4px; margin-top:10px; overflow:hidden;">'
        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
 
    -- 2. 绘制降水柱状图
    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
 
     -- 3. 绘制三条气温曲线
     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
      
      
     -- 0度气温基准线
     svg = svg .. drawLine(d.high, "#f44", 2) -- 高温红色
     local zero_y = 50
     svg = svg .. drawLine(d.mean, "#f90", 2) -- 均温橙色
     chart_html = chart_html .. string.format('<div style="position:absolute; left:0; bottom:%dpx; width:100%%; height:1px; background:#d5d5d5; z-index:1;"></div>', zero_y)
     svg = svg .. drawLine(d.low, "#48f", 2) -- 低温蓝色
   
 
     -- 1. 绘制柱状图 (降水)
     -- 4. 绘制月份标签
     for i, p_val in ipairs(precips) do
     for i, m in ipairs({"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}) do
        local height = p_val * 1.5 -- 降水高度缩放因子
         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)
        local x = (i - 1) * 25 + 10 -- X轴间距分配
         chart_html = chart_html .. string.format('<div style="position:absolute; left:%dpx; bottom:0; width:16px; height:%fpx; background:#87CEFA; z-index:1; border-radius:2px 2px 0 0;"></div>', x, height)
     end
     end
      
      
     -- 2. 绘制气温点和折线
     svg = svg .. '</svg>'
    for i = 1, 12 do
        local t_val = temps[i]
        local x1 = (i - 1) * 25 + 18 -- 当前点 X 坐标 (对齐柱子中心)
        local y1 = zero_y + (t_val * 3) -- 当前点 Y 坐标 (气温缩放因子)
       
        -- 计算两点之间的连线 (利用 CSS 旋转实现)
        if i < 12 then
            local next_t = temps[i+1]
            local x2 = (i) * 25 + 18
            local y2 = zero_y + (next_t * 3)
           
            local dx = x2 - x1
            local dy = y2 - y1
            local length = math.sqrt(dx*dx + dy*dy)
            local angle = math.atan2(dy, dx) * 180 / math.pi
           
            chart_html = chart_html .. string.format('<div style="position:absolute; left:%dpx; bottom:%dpx; width:%fpx; height:2px; background:#FF6347; transform-origin: 0 50%%; transform: rotate(%fdeg); z-index:2;"></div>', x1, y1, length, angle)
        end
       
        -- 绘制气温数据点
        chart_html = chart_html .. string.format('<div style="position:absolute; left:%dpx; bottom:%dpx; width:6px; height:6px; background:#DC143C; border-radius:50%%; z-index:3;"></div>', x1 - 3, y1 - 3)
    end
    chart_html = chart_html .. '</div>'


     -- 组装最终的 Infobox HTML 结构
     -- 封装 HTML 并输出
     local html = string.format([[
     local raw_svg = frame:extensionTag('html', svg)
<div style="float: right; clear: right; margin: 0 0 1em 1em; width: 320px; border: 1px solid #c8ccd1; border-radius: 8px; padding: 10px; background-color: var(--background-color-base, #ffffff); box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-size: 90%%;">
     local koppen = calculateKoppen(d.mean, d.rain)
     <div style="text-align: center; font-weight: bold; font-size: 1.1em; margin-bottom: 5px; background: #eaecf0; padding: 5px; border-radius: 4px;">%s</div>
    %s
    <div style="text-align: center; font-weight: bold; color: #54595d; margin-top: 15px;">柯本气候分类:<br><span style="color: #2a4b8d;">%s</span></div>
    <div style="font-size: 0.85em; color: #72777d; text-align: right; margin-top: 8px;">来源:%s</div>
</div>
    ]], title, chart_html, koppen, args['source'] or "未知")


     return html
     return string.format([[
<div style="float: right; width: 280px; border: 1px solid #ccc; padding: 10px; background: #fff; box-shadow: 2px 2px 5px rgba(0,0,0,0.1);">
    <div style="text-align:center; font-weight:bold; padding:5px; background:#f0f0f0;">%s</div>
    <div style="margin:10px 0;">%s</div>
    <div style="font-size:0.9em; line-height:1.4em;">
        <b>柯本分类:</b>%s<br>
        <b>数据来源:</b>%s
    </div>
</div>]], args['location'] or "未知", raw_svg, koppen, args['source'] or "JGWWC")
end
end


return p
return p

2026年5月12日 (二) 16:37的最新版本

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

local p = {}

-- 柯本气候分类计算逻辑(保持不变)
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

function p.render(frame)
    local args = frame:getParent().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 .. ' 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
    
    -- 向上取整到10的倍数
    max_t = math.ceil(max_t / 10) * 10
    max_p = math.ceil(max_p / 20) * 20

    -- SVG 绘图配置 (3:4 画幅设计: Width=300, Height=400)
    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;">', w, h)
    
    -- 1. 绘制网格线
    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

    -- 2. 绘制降水柱状图
    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

    -- 3. 绘制三条气温曲线
    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)  -- 低温蓝色

    -- 4. 绘制月份标签
    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>'

    -- 封装 HTML 并输出
    local raw_svg = frame:extensionTag('html', svg)
    local koppen = calculateKoppen(d.mean, d.rain)

    return string.format([[
<div style="float: right; width: 280px; border: 1px solid #ccc; padding: 10px; background: #fff; box-shadow: 2px 2px 5px rgba(0,0,0,0.1);">
    <div style="text-align:center; font-weight:bold; padding:5px; background:#f0f0f0;">%s</div>
    <div style="margin:10px 0;">%s</div>
    <div style="font-size:0.9em; line-height:1.4em;">
        <b>柯本分类:</b>%s<br>
        <b>数据来源:</b>%s
    </div>
</div>]], args['location'] or "未知", raw_svg, koppen, args['source'] or "JGWWC")
end

return p