Modulo:ClimaAnnuale

Da Wikipedia, l'enciclopedia libera.
Vai alla navigazione Vai alla ricerca

Modulo in Lua per gestire le funzioni di {{ClimaAnnuale}} e {{ClimaAnnualeAustrale}}


local p = {}

local language = mw.language.getContentLanguage()
local getArgs = require('Module:Arguments').getArgs
local index_month =  {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"}

--carico le tabelle dei colori
local tempo_config = mw.loadData( 'Modulo:ClimaAnnuale/Configurazione' );

local function dump(t, ...)
    local args = {...}
    for _, s in ipairs(args) do
        table.insert(t, s)
    end
end

-- Nota il lua non ha una funzione di arrotondamento
-- funzione copiata da http://lua-users.org/wiki/SimpleRound
function round(num, idp)
    local mult = 10^(idp or 0)
    if num >= 0 then return math.floor(num * mult + 0.5) / mult
    else return math.ceil(num * mult - 0.5) / mult end
end

-- prima di provare ad applicare tonumber a una stringa converte
-- eventuali virgole in punti
local function tonumber_comma(s)
    if s then
        s = string.gsub(s, ',', '.')
        s = string.gsub(s, '−', '-')
        return tonumber(s)
    else
        return nil
    end
end


-- Per leggere una stringa di dati, si aspetta di leggere una stringa
-- di valori separati da "," o ";" da args[<basename>].
-- In caso contrario controlla se sono presenti come sequenza di
-- parametri i valori "args[<base_name>01] .. "args[<basename>12]"
-- Se legge almeno un valore si assicura di restituire almeno 12 valori
-- sostituendo i mancanti con "0"
local function dataArray(args, base_name)
    local list = {}
    local csv = args[base_name]
    if csv then
        if string.find(csv, ";") then
            list = mw.text.split(string.gsub(csv, "%s", ""), ";")
        else
            list = mw.text.split(string.gsub(csv, "%s", ""), ",")
        end
        for i =1, 12 do
            if not list[i] or list[i] == '' then list[i] = "0" end
        end
    elseif args[base_name .. "01"] then  
        for i, index_string in ipairs(index_month) do
            list[i] = args[base_name .. index_string] or "0"
        end
    end
    return list
end

-- Per leggere una stringa di dati, si aspetta di leggere una stringa
-- di valori separati da "," o ";"
local function noteArray(args, base_name, extend)
    local list = {}
    local csv = args[base_name]
    if csv then
        if string.find(csv, ";") then
            list = mw.text.split(string.gsub(csv, "%s", ""), ";")
        else
            list = mw.text.split(string.gsub(csv, "%s", ""), ",")
        end
    else
        for i, index_string in ipairs(index_month) do
            list[i] = args[base_name .. index_string] or ""
        end
    end
    return list
end


---------------------------------------------------------------
-- Funzioni di generazione del colore celle
-- Ognuna di queste funzioni riceve un valore e ritorna un valore
-- esadecimale da usare come sfondo per la cella, il valore può essere
-- basato su una tabella memorizzata in Modulo:ClimaAnnuale/Configurazione
-- oppure può essere calcolato sulla base di una formula matematica
---------------------------------------------------------------------------
local function TempToColour(temp)
    temp = round(temp)
    if temp < -19 then return tempo_config.temperatura[-19] end
    return tempo_config.temperatura[temp] or 'FF1F00'
end

local function PressureToColour(press)
    press = round(press)
    if press < -90 then return tempo_config.pressione[-90] end
    return tempo_config.pressione[press] or 'FFFFFF'
end

local function PressureToColour1000(press)
    return PressureToColour(press-1000)
end

local function WarmToColour(temp)
    temp = round(temp)
    if temp < 0 then return tempo_config.warm[0] end
    return tempo_config.warm[temp] or 'FF0060'
end

local function CloudToColour(okta)
    okta = round(okta, 1)
    if okta < 0.0 then return tempo_config.cloud[0.0] end
    return tempo_config.cloud[okta] or 'FFF0F0'
end

local function RainToColour(mm)
    mm = round(mm, 0)
    if mm < 0 then return tempo_config.rain[0] end
    return tempo_config.rain[mm] or '0000FF'
end

local function RainToColour10(days)
    return RainToColour(days*10)
end

local function SnowToColour(cm)
    cm = round(cm, 0)
    if cm < 0 then return tempo_config.snow[0] end
    return tempo_config.snow[cm] or '0000FF'
end

local function SnowToColour10(days)
    return SnowToColour(days*10)
end

local function convert_to_FF(val, base, mult)
    local result = round(base - (val or 0) * mult)
    return math.max(math.min(result, 255), 0)
end

local function IceToColour(days)
    return string.format('%02X%02XE5', convert_to_FF(days, 229, 8), convert_to_FF(days, 230, 1))
end

local function FogToColour(days)
    return string.format('%02X%02XE5', convert_to_FF(days, 229, 2), convert_to_FF(days, 230, 0.8))
end

local function HumToColour(hum_percent)
    local base_percent = convert_to_FF(hum_percent-50, 255, 4.1)
    return string.format('%02X%02XFF', base_percent, base_percent)
end

local function HelioToColour(elio)
    return string.format('FF%02X%02X', convert_to_FF(-elio, 128, 6.375), convert_to_FF(-elio, 51, 5.1))
end

local function OreSoleToColour(hours)
    return string.format('FF%02X%02X', convert_to_FF(hours, 128, .3), convert_to_FF(hours, 51, .1))
end

local function SunToColour(rad)
    rad = round(rad/100, 0) * 100
    if rad < 0 then return tempo_config.sun[0] end
    return tempo_config.sun[rad] or '0000FF'
end

local function GiorniSerenoToColour(days)
    return string.format('FF%02X%02X', convert_to_FF(days, 175, 3), convert_to_FF(days, 81, 2))
end

local function IntensitaToColour(rad)
    return 'FFFFFF'
end
--------------------------------------------------------------------------
--Fine funzioni di generazione del colore cella
--------------------------------------------------------------------------

local function format_numero(value, force_decimal)
    local value_string = language:formatNum(value)
    if force_decimal and value % 1 == 0 then
        value_string = value_string .. ',0'
    end
    value_string = mw.ustring.gsub(value_string, "^-", "−")
    return value_string
end

--Calcolo del valore medio degli argomenti in ingresso arrotondato alla prima cifra decimale
local function Media( ... )
    local args = { ... }
    local sum = 0
    for _, s in ipairs(args) do
        sum = sum + s
    end
    return round(sum / #args, 1)
end


--Calcolo del totale degli ingressi
local function Totale( ... )
    local args = { ... }
    local sum = 0
    for _, s in ipairs(args) do
        sum = sum + s
    end
    return round(sum, 1)
end

-- Restituisce il valore esadecimale del colore della cella, riceve:
---- color: funzione da richiamare per resistuire il colore
---- value: il valore da usare per il calcolo del valore
---- aggregate_function: la funzione di aggregazione usata
---- div: il numero per cui dividere il valore (3 per la somma di tre mesi, 12 per la somma annuale) per cui dividere il
----      value se la funzione di aggregazione era Total)
local function Cell_color(color, value, aggregate_function, div)
    if aggregate_function == Totale then
        return color(round(value / div))
    else
        return color(value)
    end
end


-- Configurazione dei dati da leggere e della corrispondente riga di tabella da generare:
---- name: nome base della variabile che contiene il valore
---- color: funzione che assegna il colore alla cella
---- yearname: presente solo per alcune righe. Il nome base della della variabile con l'anno del dato
----- direction: presente solo per intensità (vento). il nome base della variabile con la direzione del vento
---- aggregate: funzione per il calcolo dei valori aggregati (stagionali e annuali)
---- aggregatelabel: attributo css "title" delle celle dei dati aggregati
---- force_decimal: se forzare o meno i dati/risultato con uno zero decimale (",0") quando sono interi
local data_type = {
    { name = 'tempmax', color = TempToColour, aggregate = Media, label = '[[Temperatura|T. max. media]] ([[Grado Celsius|°C]])', aggregatelabel = 'media' },
    { name = 'tempmedia', color = TempToColour, aggregate = Media, label = '[[Temperatura|T. media]] ([[Grado Celsius|°C]])', aggregatelabel= 'media' },
    { name = 'tempmin', color = TempToColour, aggregate = Media, label = '[[Temperatura|T. min. media]] ([[Grado Celsius|°C]])', aggregatelabel= 'media' },
    { name = 'tempassmax', color = TempToColour, aggregate = math.max, yearname = 'annotempassmax', label = '[[Temperatura|T. max. assoluta]] ([[Grado Celsius|°C]])', aggregatelabel= 'massimo' },
    { name = 'tempassmin', color = TempToColour, aggregate = math.min, yearname = 'annotempassmin', label='[[Temperatura|T. min. assoluta]] ([[Grado Celsius|°C]])', aggregatelabel= 'minimo' },
    { name = 'warm', color = WarmToColour, aggregate = Totale, label = '[[Onda di calore|Giorni di calura]] ([[temperatura|T<sub>max</sub>]] ≥ 30 [[Grado Celsius|°C]])', aggregatelabel = 'totale'},
    { name = 'giornigelo', color =IceToColour, aggregate = Totale, label = '[[Giorno di gelo|Giorni di gelo]] ([[temperatura|T<sub>min</sub>]] ≤ 0 [[Grado Celsius|°C]])', aggregatelabel = 'totale'},
    { name = 'giornighiaccio', color=IceToColour, aggregate = Totale, label ='[[Giorno di ghiaccio|Giorni di ghiaccio]] ([[temperatura|T<sub>max</sub>]] ≤ 0 [[Grado Celsius|°C]])', aggregatelabel='totale'},
    { name = 'nubi', color=CloudToColour, aggregate=Media, label='[[Nuvola#Nuvolosità|Nuvolosità]] ([[okta]] al [[giorno]])', aggregatelabel='media' },
    { name = 'pioggia', color=RainToColour, aggregate=Totale, label='[[Precipitazione (meteorologia)|Precipitazioni]] ([[Metro#Multipli e sottomultipli|mm]])', aggregatelabel='totale'},
    { name = 'giornipioggia', color=RainToColour10, aggregate=Totale, label='[[Giorno di pioggia|Giorni di pioggia]]', aggregatelabel='totale'},
    { name = 'neve', color=SnowToColour, aggregate=Totale, label='[[Neve|Nevicate]] ([[Metro#Multipli e sottomultipli|cm]])', aggregatelabel='totale'},
    { name = 'giornineve', color=SnowToColour10, aggregate=Totale, label='[[Neve|Giorni di neve]]', aggregatelabel='totale'},
    { name = 'mantonevoso', color=SnowToColour10, aggregate=Totale, label='[[Neve|Giorni con manto nevoso]] ≥ 1 [[Metro#Multipli e sottomultipli|cm]]', aggregatelabel='totale'},
    { name = 'giornigrandine', color=SnowToColour10, aggregate=Totale, label='[[Grandine|Giorni di grandine]]', aggregatelabel='totale'},
    { name = 'giorninebbia', color=FogToColour, aggregate=Totale, label='[[Nebbia|Giorni di nebbia]]', aggregatelabel='totale'},
    { name = 'umidomax', color=HumToColour, aggregate=Media, label='[[Umidità relativa|Umidità relativa massima media]] (%)', aggregatelabel='media'},
    { name = 'umido', color=HumToColour, aggregate=Media, label='[[Umidità relativa|Umidità relativa media]] (%)', aggregatelabel="media"},
    { name = 'umidomin', color=HumToColour, aggregate=Media, label='[[Umidità relativa|Umidità relativa minima media]] (%)', aggregatelabel='media'},
    { name = 'giornisereno', color= GiorniSerenoToColour, aggregate = Totale, label ='[[Sole|Giorni di cielo sereno]]', aggregatelabel='totale'},  
    { name = 'elio', color=HelioToColour, aggregate=Media, label='[[Eliofania|Eliofania assoluta]] ([[ora|ore]] al [[giorno]])', aggregatelabel='media'},
    { name = 'sole', color=SunToColour, aggregate=Totale, label='[[Radiazione solare|Radiazione solare globale media]] (centesimi di [[Joule|MJ]]/[[metro quadrato|m²]])', aggregatelabel='totale'},
    { name = 'oresoleggiamento', color=OreSoleToColour, aggregate=Totale, label='[[Sole|Ore di soleggiamento mensili]]', aggregatelabel='totale'},
    { name= 'pressionereale', color=PressureToColour1000, aggregate=Media, label='[[Pressione atmosferica|Pressione]] a [[Grado Celsius|0 °C]] ([[Pascal (unità di misura)|hPa]])', aggregatelabel='media'},
    { name= 'pressione', color=PressureToColour1000, aggregate=Media, label='[[Pressione atmosferica|Pressione]] a [[livello del mare|0 metri s.l.m.]] ([[Pascal (unità di misura)|hPa]])', aggregatelabel='media'},
    { name= 'tensionevapore', color=PressureToColour, aggregate=Media, label='[[Pressione di vapore|Tensione di vapore]] ([[Pascal (unità di misura)|hPa]])', aggregatelabel='media'},
    { name= 'intensità', color=IntensitaToColour, aggregate=Media, direction='vento', label='[[Vento]] ([[Rosa dei venti|direzione]]-[[metro|m]]/[[secondo|s]])', aggregatelabel='media'}
}

-- ordine in cui aggregare i valori
local aggregate_index = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}

-- ordine dei nomi delle stagioni secondo emisfero boreale/australe
local seasons = {
    boreale = { "[[Inverno|Inv]]", "[[Primavera|Pri]]", "[[Estate|Est]]", " [[Autunno|Aut]] " },
    australe = { "[[Estate|Est]]", "[[Autunno|Aut]]", "[[Inverno|Inv]]", "[[Primavera|Pri]]"  }
}


function p.ClimaAnnuale(frame)
    -- Se chiamata mediante  #invoke, usa gli argomenti passati al template invocante.
    -- Altrimenti a scopo di test assume che gli argomenti siano passati direttamente

    local args = getArgs(frame)
    local emisphere = args.emisfero or 'boreale'
    local season = seasons[emisphere] or seasons.borealea
    local equatorial = false
    if args.equatoriale and (args.equatoriale == 's' or args.equatoriale == 'S') then
        equatorial = true
    end
        
    local output = {}
    -- testata della tabella
    dump(output, '<div style="overflow-x:auto" >',
                 '<table class="wikitable" style="margin-top:.5em; margin-bottom:.5em; text-align: center;">',
                 '<tr><th rowspan="2" style="border-right-width: 2px;">', args['nome'] or 'Mese', '</th>',
                 '<th colspan="12"> [[Mese|Mesi]] </th>')
    if not equatorial then
        dump(output,'<th colspan="4" style="border-left-width: 2px;" > [[Stagione|Stagioni]] </th>')
    end
    dump(output, '<th rowspan="2" style="border-left-width: 2px; "> [[Anno]] </th>',
                 '</tr>',
                 '<tr>',
                 '<th> [[Gennaio|Gen]] </th>',
                 '<th> [[Febbraio|Feb]] </th>',
                 '<th> [[Marzo|Mar]] </th>',
                 '<th> [[Aprile|Apr]] </th>',
                 '<th> [[Maggio|Mag]] </th>',
                 '<th> [[Giugno|Giu]] </th>',
                 '<th> [[Luglio|Lug]] </th>',
                 '<th> [[Agosto|Ago]] </th>',
                 '<th> [[Settembre|Set]] </th>',
                 '<th> [[Ottobre|Ott]] </th>',
                 '<th> [[Novembre|Nov]] </th>',
                 '<th> [[Dicembre|Dic]] </th>')
    if not equatorial then
        dump(output, '<th style="border-left-width: 2px;" >', season[1],  '</th>')
        for j = 2, 4 do
            dump(output, '<th>', season[j], '</th>')
        end
    end
    dump(output, '</tr>')
    -- generazione delle righe
    for _,row in ipairs(data_type) do
        local values = dataArray(args, row.name)
        local force_decimal = false
        --check esistenza del tipo di dato della riga
        if #values > 0 then
            local yearnames = row.yearname and noteArray(args, row.yearname, false)
            local directions = row.direction and noteArray(args, row.direction, false)
            dump(output, '<tr><th style="border-right-width: 2px"> ', row.label, ' </th>')
            -- ciclo per leggere tutti i valori dei mesi
            for i,raw_value in ipairs(values) do
                if string.find(raw_value, '.', 1, true) or string.find(raw_value, ',', 1, true) then
                    force_decimal = true
                end
                -- converto il valore del mese in numero, se non definito lo setto a 0
                values[i] = tonumber_comma(raw_value) or 0
                -- apro la cella e genero il valore
                dump(output, '<td style="background:#', row.color(values[i]), ';">')
                -- per la direzione del vento (da mettere prima della velocità)
                if row.direction and directions[i] and directions[i]~= "" then
                    dump(output, '<small>', directions[i], '</small> <br />')
                end
                -- aggiungo il valore alla cella
                dump(output, format_numero(values[i], force_decimal) )
                -- check se devo controllare e aggiungere l'anno del dato
                if row.yearname and yearnames[i] ~= '' then
                    dump(output, '<br /><small>(', yearnames[i], ')</small>')
                end
                dump(output,'</td>')
            end
            -- ciclo per generare i dati stagionali aggregati
            if not equatorial then
                for j = 1, 10, 3 do
                    local aggregate_season = row.aggregate(values[aggregate_index[j]], values[aggregate_index[j+1]], values[aggregate_index[j+2]])
                    dump(output, '<td title="', row.aggregatelabel, '" style="border-left-width: 2px;  background:#',
                    Cell_color(row.color, aggregate_season, row.aggregate, 3), ';">\'\'\'', format_numero(aggregate_season, force_decimal), "'''</td>" )
                end
            end
            -- genero il dato annuale aggregato
            local aggregate_year = row.aggregate(unpack(values))       
            dump(output, '<td title="', row.aggregatelabel, '" style="border-left-width: 2px; background:#',
                Cell_color(row.color, aggregate_year, row.aggregate, 12), ';">\'\'\'', format_numero(aggregate_year, force_decimal), "'''</td>")
            dump(output, '</tr>')
        end
    end
    dump(output, '</table></div>')
    return table.concat(output)
end

return p