Modulo:Grafico XY

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

Questo modulo serve a creare un grafico per {{Grafico XY}}.


require("strict")

local getArgs = require('Module:Arguments').getArgs
local p = {}

local flex = {}
flex['primo'] = {'<div style="display:flex;flex-direction:row;flex-wrap:wrap"><div style="flex-grow:0;flex-shrink:0;flex-basis:auto;padding: 0 2em 0 0">','</div>'}
flex['medio'] = {'<div style="flex-grow:0;flex-shrink:0;flex-basis:auto;padding: 0 2em 0 0">','</div>'}
flex['ultimo'] = {'<div style="flex-grow:0;flex-shrink:0;flex-basis:auto;padding: 0 2em 0 0">','</div></div>'}
flex['destra'] = {'<div class="floatright">','</div>'}

local clr = { "#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf" }

-- =====================================================================
-- Legge gli argomenti per caricare un array di valori numerici.
-- =====================================================================
local function leggi(args, a)
	local array = {}
	if args[a] then
		array = mw.text.split(string.gsub(args[a], "%s", ""), ",")
	end
	for _,v in ipairs(array) do
		v = tonumber(v)
	end
	return array
end
-- =====================================================================
-- Fonde due liste in un'unica lista
-- =====================================================================
local function unisci(a,b,c,d)
	local values = { }
	local t
	for i =1,#b do
		t = string.format("%g",math.floor(10*b[i]/d+0.5)/10)
		t = t:gsub('%.',',')
		values[i] = { nx = a[i]/c, ny = b[i]/d, num = i, txt = t }
	end
	return values
end

-- =====================================================================
-- Generazione del grafico
-- =====================================================================
function p._grafico(args)
	-- Definizione base del grafico
	local graph = {
		version = 2,
		width = 350,
		height = 250,
		padding = "auto",
		data = { },
		scales = {
			{
				name = "x",
				type = "linear",
				range = "width",
				zero = false,
				domain = {fields = {}}
			},
			{
				name = "y",
				type = "linear",
				range = "height",
				zero = true,
				nice = true,
				domain = {fields = {}}
			},
			{
				name = "colori",
				type = "ordinal",
				domain = { data = "nomi", field = "testo"},
				range = { }
			},
			{
				name = "colori2",
				type = "ordinal",
				domain = { data = "area", field = "testo"},
				range = { }
			}
		},
		axes = {
			{
				type = "x",
				scale = "x",
				title = "",
				format = args['formatoX'] or "d",
				labelOverlap = "greedy",
				grid = true,
				properties = {
					labels = { font = { value = "Helvetica" } },
					title = { font = { value = "Helvetica" } },
				}
			},
			{
				type = "y",
				scale = "y",
				title = "",
				grid = true,
				format = args['formatoY'] or "d",
				labelOverlap = "greedy",
				layer = "back",
				properties = {
					labels = { font = { value = "Helvetica" } },
					title = { font = { value = "Helvetica" } },
				}
			}
		},
		legends = {
			{
				fill = "colori",
				title = "",
				orient = "top-left",
				offset = 8,
      			properties = {
      				labels = { font = { value = "Helvetica" }, fontSize = { value = 12 } },
					symbols = {
						strokeWidth = { value = 0 },
						shape = { value = "square" },
						opacity = { value = 0.7 }
					},
					legend = { fill = { value = "#fff" } }
				}
			},
			{
				fill = "colori2",
				title = "",
				orient = "top-left",
				offset = 8,
      			properties = {
      				labels = { font = { value = "Helvetica" }, fontSize = { value = 12 } },
					symbols = {
						strokeWidth = { value = 0 },
						shape = { value = "square" },
						opacity = { value = 0.3 }
					},
					legend = { fill = { value = "#fff" } }
				}
			}
		},
		marks = {
			{
				type = "rect",
				from = { data = "area" },
				properties = {
					enter = {
						x = {scale = "x",field = "x1"},
						x2 = {scale = "x",field = "x2"},
						y = {value = 0},
						y2 = {signal = "height"},
						fill = {scale = "colori2", field = "testo" },
						opacity = {value =0.2 }
					}
				}
			}
		}
	}
	-- titoli assi
	local titoloX = args.titoloX or ''
	local fattoreX = args.fattoreX and tonumber(args.fattoreX) and tonumber(args.fattoreX) or 1
	if fattoreX == 0 then
		fattoreX = 1
	elseif fattoreX == 1000 then
		titoloX = titoloX..' (migliaia)'
	elseif fattoreX == 1000000 then
		titoloX = titoloX..' (milioni)'
	end
	graph['axes'][1]['title'] = titoloX
	local titoloY = args.titoloY or ''
	local fattoreY = args.fattoreY and tonumber(args.fattoreY) and tonumber(args.fattoreY) or 1
	if fattoreY == 0 then
		fattoreY = 1
	elseif fattoreY == 1000 then
		titoloY = titoloY..' (migliaia)'
	elseif fattoreY == 1000000 then
		titoloY = titoloY..' (milioni)'
	end
	graph['axes'][2]['title'] = titoloY

	-- zero su assi
	if (args.zeroX and args.zeroX == 's') then graph['scales'][1]['zero'] = true end
	if (args.zeroY and args.zeroY == 'n') then graph['scales'][2]['zero'] = false end

	-- dimensioni
	graph['width'] = args.dimx and tonumber(args.dimx) and tonumber(args.dimx) or 350
	graph['height'] = args.dimy and tonumber(args.dimy) and tonumber(args.dimy) or 250

	-- legge valori
	local numero = 1
	local dx, dy, dt, cc
	local err = '-'
	dt = {}

	while (args['x'..numero]) do
		dx = leggi(args, "x"..numero)
		dy = leggi(args, "y"..numero)
		if (dx and dy) then
			if (#dx == #dy) then
				dt[numero] =  unisci(dx, dy, fattoreX, fattoreY)
			else
				err = 'Errore nel numero di elementi per serie '..numero
				break
			end
		else
			err = 'Errore nella serie '..numero
			break
		end
		numero = numero+1
	end
	numero = numero-1
	
	if err == '-' then
		for i = 1,numero do
			cc = i%10
			if cc == 0 then cc = 10 end
			graph['data'][i] = {
				name = "tab"..i,
				transform = { { type = "sort", by = "nx" } },
			}
			graph['data'][i]['values'] = dt[i]
			graph['marks'][1+i] = {
				type = "line",
				from = {data = "tab"..i },
				properties = {
					enter = {
						interpolate = {value = "linear"},
						x = {scale = "x",field = "nx"},
						y = {scale = "y",field = "ny"},
						stroke = {value = clr[cc] },
						strokeWidth = {value = 4},
						opacity = {value = 0.5}
					}
				}
			}
			graph['marks'][1+i+numero] = {
				type = "symbol",
				from = {data = "tab"..i },
				properties = {
					enter = {
						x = {scale = "x",field = "nx"},
						y = {scale = "y",field = "ny"},
						stroke = {value = clr[cc] },
						fill = {value = "#fff"},
						size = {value = 30}
					}
				}
			}
			graph['marks'][1+i+2*numero] = {
				type = "text",
				from = { data = "tab"..i },
				properties = {
					enter = {
						x = {scale = "x",field = "nx"},
						y = {scale = "y",field = "ny", offset = -8},
						align = {value = "center"},
						fill = {value = "#000"},
						font = {value = "Helvetica"},
						fontSize = { value = 12 },
						text = {field = "txt" }
					}
				}
			}
			graph['scales'][1]['domain']['fields'][i] = {data = "tab"..i,field = "nx"}
			graph['scales'][2]['domain']['fields'][i] = {data = "tab"..i,field = "ny"}
			graph['scales'][3]['range'][i] = clr[cc]
			-- colori
			if args['colore'..i] then
				graph['marks'][1+i]['properties']['enter']['stroke']['value'] = args['colore'..i]
				graph['marks'][1+i+numero]['properties']['enter']['stroke']['value'] = args['colore'..i]
				graph['scales'][3]['range'][i] = args['colore'..i]
			end
			-- opzioni etichette
			if args['etichette'..i] then
				if args['etichette'..i] == 'dispari' then
					graph['marks'][1+i+2*numero]['from']['transform'] = {{ type = "filter", test = "datum.num % 2 == 1"}}
				elseif args['etichette'..i] == 'pari' then
					graph['marks'][1+i+2*numero]['from']['transform'] = {{ type = "filter", test = "datum.num % 2 == 0"}}
				elseif args['etichette'..i] == 'no' then
					graph['marks'][1+i+2*numero]['from']['transform'] = {{ type = "filter", test = "datum.num < 1"}}
				end
			end
			-- mostra dati
			if args['mostra'..i] then
				if args['mostra'..i] == '1' then
					graph['marks'][1+numero+i]['from']['transform'] = {{ type = "filter", test = "datum.num < 1"}}
				elseif args['mostra'..i] == '2' then
					graph['marks'][1+i]['from']['transform'] = {{ type = "filter", test = "datum.num < 1"}}
				end
			end
		end

		-- legenda
		graph['data'][numero+1] = {
			name = "nomi",
			values = { }
		}
		local lg = 1
		while (args['nome'..lg]) do
			graph['data'][numero+1]['values'][lg] = { testo = args['nome'..lg] }
			lg = lg +1
		end
		if args.legenda then graph['legends'][1]['orient'] = args.legenda end

		-- annotazioni
		graph['data'][numero+2] = {
			name = "area",
			values = { }
		}
		lg = 1
		while (args['area'..lg]) do
			graph['data'][numero+2]['values'][lg] = { testo = args['area'..lg], x1 = args['area'..lg..'_x1'], x2 = args['area'..lg..'_x2'] }
			graph['scales'][4]['range'][lg] = args['area'..lg..'_colore'] or clr[lg]
			graph['scales'][1]['domain']['fields'][numero+2*lg-1] = {data = "area",field = "x1"}
			graph['scales'][1]['domain']['fields'][numero+2*lg] = {data = "area",field = "x2"}
			lg = lg +1
		end
		if args.area_legenda then graph['legends'][2]['orient'] = args.area_legenda end

		if (args.minimoX and tonumber(args.minimoX)) then
			graph['scales'][1]['domainMin'] = tonumber(args.minimoX)
		end
		if (args.massimoX and tonumber(args.massimoX)) then
			graph['scales'][1]['domainMax'] = tonumber(args.massimoX)
		end

		if (args.minimoY and tonumber(args.minimoY)) then
			graph['scales'][2]['domainMin'] = tonumber(args.minimoY)
		end
		if (args.massimoY and tonumber(args.massimoY)) then
			graph['scales'][2]['domainMax'] = tonumber(args.massimoY)
		end

		local ris = {}
		local allinea = args['allinea'] or ''
		if (flex[allinea]) then table.insert(ris,flex[allinea][1]) end
		table.insert(ris,mw.getCurrentFrame():extensionTag('graph', mw.text.jsonEncode(graph)))
		if (args.didascalia) then
			table.insert(ris,'<p style="font-size:90%;margin-left:30px">'..args.didascalia..'</p>')
		end
		if (flex[allinea]) then table.insert(ris,flex[allinea][2]) end
		if args['debug'] then
			return mw.text.jsonEncode(graph)
		else
			return table.concat(ris)
		end
	else
		return '<span style="color:red"><b>Template GraficoXY:</b> '..err..'</span>'
	end
end

-- ======================================================================================================
-- Funzione di intefaccia con template:Grafico XY
-- ======================================================================================================
function p.grafico(frame)
	local args = getArgs(frame)
	return p._grafico(args)
end

return p