Modulo:Graph: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Moroboshi (discussione | contributi)
aggiorno la posizione del modulo di configurazione
m correggo
 
(12 versioni intermedie di 2 utenti non mostrate)
Riga 31:
if #errors > 0 then
local out = string.format('<strong class="error">%s</strong>', table.concat(errors, "; "))
if nocat or not cfg.uncategorized_namespaces[mw.title.getCurrentTitle().nsnsText] then
out = out .. '[[Category:' .. cfg.errors_category .. ']]'
end
Riga 39:
end
 
-- ==============================================================================
-- Return true if t is a table
-- ===============================================================================
local function isTable(t)
-- Return true if val is not nil and is a string in the array cfg.yes_values
return type(t) == "table"
end
 
-- ===============================================================================
-- Class to manage access to arguments
local function return_yes_value(val)
-- ===============================================================================
return val and cfg.yes_values[mw.ustring.lower(val)]
local Args = {}
Args.__index = Args
 
function Args.new(arguments)
local self = {}
self.args = arguments
return setmetatable(self, Args)
end
 
-- ===============================================================================
-- Return value of parameter name
-- Return true if val is not nil and is a string in the array cfg.no_value
-- ===============================================================================
function Args:value(name, default, transform)
local function return_no_value(val)
if cfg.localization[name] then
return val and cfg.no_values[mw.ustring.lower(val)]
val = self.args[cfg.localization[name]]
if val and transform then
val = transform(val)
end
if val then
return val
end
return default
end
return --TODO raise an error ?
end
 
-- ===============================================================================
-- Return value of parameter name as number
-- ===============================================================================
function Args:number(name, default)
local val = self:value(name)
return (val and tonumber(val)) or default
end
 
 
-- ===============================================================================
-- Return array of value of parameters base_name, base_name2, ... base_namen
-- ===============================================================================
function Args:values_indexed(base_name)
local base_name_localized = cfg.localization[base_name]
if not base_name_localized then return end
local values = {}
local index = 1
if self.args[base_name_localized] then
values[1] = self.args[base_name_localized]
index = 2
end
while true do
local val = self.args[base_name_localized .. tostring(index)]
if not val then break end
values[index] = val
index = index + 1
end
return values
end
 
-- ===============================================================================
-- Return true if parameter arg is present and is a yes value (a valor
-- in the array cfg.yes_values
-- ===============================================================================
function Args:is_yes(name)
local val = self:value(name)
return val and cfg.yes_values[mw.ustring.lower(val)]
end
 
-- ===============================================================================
-- Return true if parameter arg is present and is a yes value (a valor
-- in the array cfg.yes_values
-- ===============================================================================
function Args:is_no(name)
local val = self:value(name)
return val and cfg.no_values[mw.ustring.lower(val)]
end
 
setmetatable(Args, { __call = function(_, ...) return Args.new(...) end })
 
-- ===============================================================================
Line 95 ⟶ 167:
end
 
 
-- ==============================================================================
-- Return true if t is a table
-- ===============================================================================
local function isTable(t)
return type(t) == "table"
end
 
-- ==============================================================================
Line 119 ⟶ 184:
 
-- ==============================================================================
-- Generate a color palette from thea palette name (must be in cfg.colors_palette)
-- or an array of color values
-- with minimal length len
-- ==============================================================================
local function generate_color_palette(palette, lennew_len)
local color_palette = {}
if not cfg.colors_palette[palette] then
local palette_len
colors_palette = "category10"
palette = palette or "category10"
if isTable(palette) and #palette == 1 and cfg.colors_palette[palette[1]] then
palette_len, color_palette = cfg.colors_palette[palette[1]][1], cfg.colors_palette[palette[1]][2]
elseif not isTable(palette) then
palette = (cfg.colors_palette[palette] and palette) or "category10"
palette_len, color_palette = cfg.colors_palette[palette][1], cfg.colors_palette[palette][2]
else
palette_len, color_palette = #palette, palette
end
local new_len = new_len or palette_len
local palette_len, color_palette = cfg.colors_palette[palette][1],cfg.colors_palette[palette][2]
local t = {}
local pos = 1
for i = 1, palette_lennew_len do
t[i] = color_palette[pos]
pos = pos + 1
if pos > palette_len then pos = 1 end
end
--t = extend_table(palette, len or 1)
return t
end
Line 185 ⟶ 258:
:css('-webkit-column-count', col_string)
:css('column-count:', col_string)
end
if not isTable(colors) then
colors = generate_color_palette(colors, #labels)
end
for i,label in ipairs(labels) do
Line 201 ⟶ 271:
function p.pie_chart_json(args)
local data = {}
for pos,x in= ipairs(1,#args.values) do
data[pos] = { x = xargs.labels[pos], colory = args.colorsvalues[pos] }
end
local graph = {
version = 12,
name = args.name,
width = math.floor(args.graphwidth / 3),
Line 213 ⟶ 283:
name = "table",
values = data,
transform = { { type = "pie", value = "data.x" } }
}
},
Line 219 ⟶ 289:
{
type = "arc",
from = { data = "table"},
transform = { { field = "y", type = "pie"} }
},
properties = {
enter = {
x = { field = "data.x", group = "width", mult = 0.5 },
y = { field = "data.x", group = "height", mult = 0.5 },
startAngle = { field = "startAngle"},
endAngle = {field = "endAngle"},
innerRadius = {value = args.inner_radius},
startAngle = { field = "layout_start"},
outerRadius = {value = args.outer_radius },
endAngle = {field = "layout_end"},
stroke = {value = "#fff"},
fill = { field = "x", scale = "color"},
},
update = { fill = { field = "data.color"} },
hover = { fill = {value = "pink"} }
},
}
},
scales = {
{
name = "color",
range = args.colors,
___domain = { data = "table", field = "x"},
["type"] = "ordinal"
}
}
}
if args.internal_legend then
local flags = return_yes_value(args[cfg.localization.debug_json]) and mw.text.JSON_PRETTY
data[#data] = { { fill = "color", stroke = "color", title = args.internal_legend } }
end
local flags = args.debug_json and mw.text.JSON_PRETTY
return mw.text.jsonEncode(graph, flags)
end
Line 244 ⟶ 324:
-- ===================================================================================
function p.pie_chart(frame)
local args = Args(getArgs(frame, {parentOnly = true}))
local argumentspie_args = { }
argumentspie_args.name = args[cfg.localization.:value('name] or', 'grafico a torta')
argumentspie_args.values = numericArray(args[cfg.localization.:value('values], 0'))
-- Se non trova valori validi termina
-- get marks colors, use default category10 palette as default,
if not pie_args.values or #pie_args.values == 0 then
-- if colors is not the name of a palette then read it as an array of colors
add_error('no_values')
arguments.colors = args[cfg.localization.colors] or "category10"
return errors_output(args.NoTracking)
if not cfg.colors_palette[arguments.colors] then
arguments.colors = stringArray(arguments.colors)
else
arguments.colors = generate_color_palette(arguments.colors)
end
arguments.labels = {}
local index = 1
label_string = cfg.localization.label
if args[label_string] then
arguments.labels[1] = args[label_string]
index = 2
end
while true do
if not args[label_string .. tostring(index)] then break end
arguments.labels[index] = args[label_string .. tostring(index)]
index = index + 1
end
pie_args.labels = args:values_indexed('label')
-- Se è definito 'other' assumo che sia calcolato su base %, calcolo il suo valore e l'aggiungo alla tabella dati
if args[cfg.localization.:value('other]') then
local total = 0
for _,val in ipairs(argumentspie_args.values) do total = total + val end
if total > 0 and total < 100 then
argumentspie_args.values[#argumentspie_args.values+1]= math.max(0, 100 - total)
argumentspie_args.labels[#argumentspie_args.values] = "Altri"
end
end
arguments.colors-- build array =of extend_table(arguments.colors, #arguments.values)
local palette = stringArray(args:value('colors'))
arguments.graphwidth = tonumber(args[cfg.localization.width]) or cfg.default.width_piechart
if not palette then palette = stringArray(args:value('color')) end
arguments.outer_radius = arguments.graphwidth / 2 - 5
pie_args.colors = generate_color_palette(palette, #pie_args.values)
if return_yes_value(args[cfg.localization.ring]) then
pie_args.graphwidth = args:number('width', cfg.default.width_piechart)
arguments.inner_radius = arguments.outer_radius / 3
pie_args.outer_radius = pie_args.graphwidth / 2 - 5
if args:is_yes('ring') then
pie_args.inner_radius = pie_args.outer_radius / 3
else
argumentspie_args.inner_radius = 0
end
argumentspie_args.legend = args[cfg.localization.:value('internal_legend]') or args[cfg.localization.:value('external_legend] or', 'Legenda')
for pos,txt in ipairs(argumentspie_args.labels) do
argumentspie_args.labels[pos] = txt .. ' (' .. mw.language.getContentLanguage():formatNum(argumentspie_args.values[pos] or 0) .. '%)'
end
pie_args.debug_json = args:is_yes('debug_json')
local json_code = p.pie_chart_json(arguments)
local json_code = p.pie_chart_json(pie_args)
if args[cfg.localization.debug_json] then return frame:extensionTag('syntaxhighlight', json_code) end
if pie_args.debug_json then return frame:extensionTag('syntaxhighlight', json_code) end
local external_legend
if not return_no_valueargs:is_no(arguments.'legend') then
external_legend = build_legend(argumentspie_args.colors, argumentspie_args.labels, argumentspie_args.legend, args[cfg.localization.:value('nCols]'))
end
local chart = frame:extensionTag('graph', json_code)
local align = args[cfg.localization.:value('thumb]')
return wrap_graph(chart, external_legend, align, argumentspie_args.graphwidth ) .. errors_output(args.:value('NoTracking'))
end
 
Line 344 ⟶ 414:
stats =
{
name = "stats", source = "chart", transform = {
{
{ type = "facetaggregate", keys = { "data.x" } },
{ type summarize = "stats",{ valuey = "data.ysum" },
groupby = { "x" }
}
}
}
Line 359 ⟶ 431:
zero = false, -- do not include zero value
nice = true, -- force round numbers for y scale
___domain = { data = "chart", field = "data.x" }
}
if args.xMin then xscale.domainMin = args.xMin end
Line 379 ⟶ 451:
if args.yMin or args.yMax then yscale.clamp = true end
if args.is_stacked then
yscale.___domain = { data = "stats", field = "sumsum_y" }
else
yscale.___domain = { data = "chart", field = "data.y" }
end
-- Color scale
local colorScale = {
name = "color",
type = "ordinal",
range = args.colors },
___domain = { data = "chart", field = "series" }
}
local alphaScale
if args.alphas then alphaScale = { name = "transparency", graph_type = "ordinal", range = args.alphas } end
-- Symbols scale
local symbolsScale
if type(args.symbols) == 'table' then
if args.symbols then symbolsScale = { name = "symbols", type = "ordinal", range = args.symbols } end
symbolsScale = { name = "symShape", type = "ordinal", range = args.symbols, ___domain = { data = "chart", field = "series" } }
end
-- for bar charts with multiple series: each series is grouped by the x value, therefore the series need their own scale within each x group
local groupScale
if args.graph_type == "rect" and not args.is_stacked and #args.y > 1 then
groupScale = { name = "series", type = "ordinal", range = "width", ___domain = { field = "data.series" } }
xscale.padding = 0.2 -- pad each bar group
end
Line 410 ⟶ 489:
enter =
{
x = { scale = "x", field = "data.x" },
y = { scale = "y", field = "data.y" },
 
},
Line 427 ⟶ 506:
if args.is_stacked then
-- for stacked charts this lower bound is cumulative/stacking
marks.properties.enter.y2 = { scale = "y", field = "y2layout_end" }
else
--[[
Line 444 ⟶ 523:
if not args.is_stacked and #args.y > 1 then
marks.properties.enter.x.scale = "series"
marks.properties.enter.x.field = "data.series"
marks.properties.enter.width.scale = "series"
end
Line 450 ⟶ 529:
if args.graph_type == "line" then marks.properties.enter.strokeWidth = { value = args.stroke_thickness } end
-- stacked charts have their own (stacked) y values
if args.is_stacked then marks.properties.enter.y.field = "ylayout_start" end
-- set interpolation mode
if args.interpolate then marks.properties.enter.interpolate = { value = args.interpolate } end
Line 459 ⟶ 538:
from = { data = "chart" },
properties = {
enter = {
x = { scale = "x", field = "x" },
{
xy = { scale = "xy", field = "data.xy" },
yshape = { scale = "ysymShape", field = "data.yseries" },
shapestroke = { scale = "symbolscolor", field = "series" },
},
update = { stroke = { scale = "color"} }
}
}
if args.symbol_size then symbolsMarks.properties.enter.size = { value = args.symbol_size } end
if alphaScale then
symbolsMarks.properties.enter.fillOpacity = { scale = "transparency", field = "series" }
symbolsMarks.properties.enter.strokeOpacity = { scale = "transparency", field = "series" }
end
end
if #args.y == 1 then
Line 476 ⟶ 558:
-- if there are multiple series, connect colors to series
if args.graph_type == "rect" and args.colorsByGroup then
marks.properties.update[colorField].field = "data.x"
else
marks.properties.update[colorField].field = "data.series"
end
if symbolsScale then
symbolsMarks.properties.enter.shape.field = "data.series"
symbolsMarks.properties.update.stroke.field = "data.series"
end
if alphaScale then marks.properties.update[colorField .. "Opacity"].field = "data.series" end
 
-- apply a grouping (facetting) transformation
Line 498 ⟶ 579:
{
type = "facet",
keysgroupby = { "data.series" }
}
}
Line 505 ⟶ 586:
-- for stacked charts apply a stacking transformation
if args.is_stacked then
table.insert(marks.from.transform[2] = { type = "stack", point = "data.x"1, height = "data.y" }{
field = "y",
type = "stack",
sortby = { "-_id" },
groupby = { "x" }
})
else
-- for bar charts the series are side-by-side grouped by x
if args.graph_type == "rect" then
marks.from.transform[1].keysgroupby = "data.x"
marks.scales = { groupScale }
marks.properties = { enter = { x = { field = "key", scale = "x" }, width = { scale = "x", band = true } } }
Line 529 ⟶ 615:
local output =
{
version = 12,
width = args.graphwidth,
height = args.graphheight,
Line 538 ⟶ 624:
legends = legend
}
local flags = return_yes_value(args[cfg.localization.debug_json]) and mw.text.JSON_PRETTY) or 0
return mw.text.jsonEncode(output, flags)
end
Line 548 ⟶ 634:
 
-- Read ax arguments
local function read_ax_arguments(args, ax_name, argumentschart_arg)
argumentschart_arg[ax_name .. 'title'] = args[cfg.localization[:value(ax_name .. 'AxisTitle']])
argumentschart_arg[ax_name .. 'format'] = args[cfg.localization[:value(ax_name .. 'AxisFormat']])
local grid = cfg.default[ax_name .. 'Grid']
if grid then
grid = not return_no_valueargs:is_no(args[cfg.localization[ax_name .. 'Grid']])
else
grid = return_yes_valueargs:is_yes(args[cfg.localization[ax_name .. 'Grid']])
end
argumentschart_arg[ax_name .. 'grid'] = grid
argumentschart_arg[ax_name .. 'AxisPrimaryTicks'] = numericArray(args[cfg.localization[:value(ax_name .. 'AxisPrimaryTicks']]))
argumentschart_arg[ax_name .. 'nTicks'] = tonumberargs:number(args[cfg.localization[ax_name .. 'AxisPrimaryTicksNumber']])
argumentschart_arg[ax_name .. 'SecondaryTicks'] = tonumberargs:number(args[cfg.localization[ax_name .. 'AxisSecondaryTicks']])
argumentschart_arg[ax_name ..'Min'] = tonumberargs:number(args[cfg.localization[ax_name .. 'AxisMin']])
argumentschart_arg[ax_name ..'Max'] = tonumberargs:number(args[cfg.localization[ax_name .. 'AxisMax']])
end
 
Line 573 ⟶ 659:
end
 
local args = Args(getArgs(frame, {parentOnly = true}))
-- analyze and build data to build the chart
local argumentschart_arg = { }
argumentschart_arg.graphwidth = tonumberargs:number(args[cfg.localization.'width]) or', cfg.default.width)
argumentschart_arg.graphheight = tonumberargs:number(args[cfg.localization.'height]) or', cfg.default.height)
argumentschart_arg.graph_type, argumentschart_arg.is_stacked = get_graph_type(args[cfg.localization.:value('type]'))
argumentschart_arg.interpolate = args[cfg.localization.:value('interpolate]')
if argumentschart_arg.interpolate and not cfg.interpolate[argumentschart_arg.interpolate] then
add_error('value_not_valid', {cfg.localization.interpolate, argumentschart_arg.interpolate})
interpolate = nil
end
-- get marks symbols, default symbol is used if the type of graph is line, otherwise the default
-- is not to use symbol.
if argumentschart_arg.graph_type == "line" and not return_no_valueargs:is_no(args[cfg.localization.'symbols]') then
argumentschart_arg.symbols = stringArray(args[cfg.localization.:value('symbols]') or cfg.default.symbol)
argumentschart_arg.symbol_size = tonumberargs:number(args[cfg.localization.'symbolSize]) or', cfg.default.symbol_size)
end
if argumentschart_arg.graph_type =="line" then
argumentschart_arg.stroke_thickness = tonumberargs:number(args[cfg.localization.'strokeThickness]) or', cfg.default.stroke_thickness)
end
-- show legend, optionally caption
argumentschart_arg.internal_legend = args[cfg.localization.:value('internal_legend]')
-- get x values
argumentschart_arg.x = numericArray(args.:value('x'))
argumentschart_arg.force_x_ordinal = false
if #argumentschart_arg.x == 0 then
argumentschart_arg.force_x_ordinal = true
else
for _,val in ipairs(argumentschart_arg.x) do
if val == 'x' then
argumentschart_arg.force_x_ordinal = true
break
end
end
end
if argumentschart_arg.force_x_ordinal then argumentschart_arg.x = stringArray(args.:value('x')) end
-- get y values (series)
chart_arg.y = args:values_indexed('y')
arguments.y = {}
if not chart_arg.y then return '' end --TODO message error mancanza dati per asse y
local index = 1
argumentschart_arg.seriesTitles = {}args:values_indexed('yTitle')
for pos, y in ipairs(chart_arg.y) do
if args.y then
argumentschart_arg.y[1pos] = numericArray(args.y)
chart_arg.seriesTitles[pos] = chart_arg.seriesTitles[pos] or ("y" .. tostring(pos))
arguments.seriesTitles[1] = args[string.gsub(cfg.localization.yTitle, '#', '')] or args[string.gsub(cfg.localization.yTitle, '#', '1')] or "y"
index = 2
end
while true do
if not args['y'..tostring(index)] then break end
arguments.y[index] = numericArray(args['y'..tostring(index)])
arguments.seriesTitles[index] = args[string.gsub(cfg.localization.yTitle, '#', tostring(index))] or ('y' .. tostring(index))
index = index + 1
end
-- ignore stacked charts if there is only one series
if #argumentschart_arg.y == 1 then argumentschart_arg.is_stacked = false end
-- read axes arguments
read_ax_arguments(args, 'x', argumentschart_arg)
read_ax_arguments(args, 'y', argumentschart_arg)
-- get marks colors, default palette is category10,
-- if colors is not the name of a predefined palette then read it as an array of colors
arguments--chart_arg.colors = args[cfg.localization.colors] or "category10"
--if not cfg.colors_palette[argumentschart_arg.colors] then
-- argumentschart_arg.colors = stringArray(argumentschart_arg.colors)
--elseif argumentschart_arg.colors ~="category10" and argumentschart_arg.colors ~="category20" then
-- argumentschart_arg.colors = generate_color_palette(argumentschart_arg.colors)
--end
local palette = stringArray(args:value('colors'))
if not palette then palette = stringArray(args:value('color')) end
chart_arg.colors = generate_color_palette(palette, #chart_arg.y)
--if true then return mw.text.jsonEncode(chart_arg.colors) end
-- assure that colors, stroke_thickness and symbols table are at least the same lenght that the number of
-- y series
if isTable(argumentschart_arg.colorsstroke_thickness) then argumentschart_arg.colorsstroke_thickness = extend_table(argumentschart_arg.colorsstroke_thickness, #argumentschart_arg.y) end
if isTable(argumentschart_arg.stroke_thicknesssymbols) then argumentschart_arg.stroke_thicknesssymbols = extend_table(argumentschart_arg.stroke_thicknesssymbols, #argumentschart_arg.y) end
if isTable(arguments.symbols) then arguments.symbols = extend_table(arguments.symbols, #arguments.y) end
-- if there is at least one color in the format "#aarrggbb", create a transparency (alpha) scale
if isTable(argumentschart_arg.colors) then
alphas = {}
local hasAlpha = false
for i, color in ipairs(argumentschart_arg.colors) do
local a, rgb = string.match(color, "#(%x%x)(%x%x%x%x%x%x)")
if a then
hasAlpha = true
alphas[i] = tostring(tonumber(a, 16) / 255.0)
argumentschart_arg.colors[i] = "#" .. rgb
else
alphas[i] = "1"
end
end
for i = #argumentschart_arg.colors + 1, #argumentschart_arg.y do alphas[i] = "1" end
if hasAlpha then argumentschart_arg.alphas = alphas end
elseif args[cfg.localization.alpha] then
argumentschart_arg.alphas = stringArray(args[cfg.localization.alpha])
if argumentschart_arg.alphas then
for i,a in ipairs(argumentschart_arg.alphas) do argumentschart_arg.alphas[i] = tostring(tonumber(a, 16) / 255.0) end
argumentschart_arg.alphas = extend_table(argumentschart_arg.alphas, #argumentschart_arg.y)
end
end
argumentschart_arg.colorsByGroup = return_yes_valueargs:is_yes(args[cfg.localization.'colorsByGroup]')
chart_arg.debug_json = args:is_yes('debug_json') or false
local chart_json = p.chart_json(arguments)
-- if args[cfg.localization.debug_json]true then return frame:extensionTag('syntaxhighlight', chart_jsonmw.text.jsonEncode(chart_arg, mw.text.JSON_PRETTY)) end
local chart_json = p.chart_json(chart_arg)
if chart_arg.debug_json then return frame:extensionTag('syntaxhighlight', chart_json) end
local external_legend
if args[cfg.localization.:value('external_legend]') then
external_legend = build_legend(argumentschart_arg.colors, argumentschart_arg.seriesTitles, args[cfg.localization.:value('external_legend]'),
args[cfg.localization.:value('nCols]'))
end
local chart = frame:extensionTag('graph', chart_json)
local align = args[cfg.localization.:value('thumb]')
return wrap_graph(chart, external_legend, align, argumentschart_arg.graphwidth) .. errors_output(args.:value('NoTracking'))
end
 
-- ===================================================================================
-- Return a json structure to generate a map chart
-- Imported and modified from de:Modul:Graph revision 142970943 of 10 giugno 2015
-- ===================================================================================
function p.map_json(args)
-- create highlight scale
local scales
if args.isNumbers then
scales =
{
{
name = "color",
type = args.scaleType,
___domain = { data = "highlights", field = "v" },
range = args.colorScale,
nice = true,
zero = false
}
}
if args.domainMin then scales[1].domainMin = args.domainMin end
if args.domainMax then scales[1].domainMax = args.domainMax end
 
local exponent = string.match(args.scaleType, "pow%s+(%d+%.?%d+)") -- check for exponent
if exponent then
scales[1].type = "pow"
scales[1].exponent = args.exponent
end
end
 
-- create legend
if args.legend then
legend =
{
{
fill = "color",
properties =
{
title = { fontSize = { value = 14 } },
labels = { fontSize = { value = 12 } },
legend =
{
stroke = { value = "silver" },
strokeWidth = { value = 1.5 }
}
}
}
}
end
 
-- get map url
local basemapUrl
if (string.sub(args.basemap, 1, 7) == "http://") or (string.sub(args.basemap, 1, 8) == "https://") or (string.sub(args.basemap, 1, 2) == "//") then
basemapUrl = args.basemap
else
-- if not a (supported) url look for a colon as namespace separator. If none prepend default map directory name.
local basemap = args.basemap
if not string.find(basemap, ":") then basemap = cfg.default.base_map_directory .. basemap end
basemapUrl = mw.title.new(basemap):fullUrl("action=raw")
end
local output =
{
version = 2,
width = 1, -- generic value as output size depends solely on map size and scaling factor
height = 1, -- ditto
data =
{
{
-- data source for the highlights
name = "highlights",
values = args.values
},
{
-- data source for map paths data
name = "countries",
url = basemapUrl,
format = { type = "topojson", feature = "countries" },
transform =
{
{
-- geographic transformation ("geopath") of map paths data
type = "geopath",
value = "data", -- data source
scale = args.scale,
translate = { 0, 0 },
projection = args.projection
},
{
-- join ("zip") of mutiple data source: here map paths data and highlights
type = "zip",
key = "id", -- key for map paths data
with = "highlights", -- name of highlight data source
withKey = "id", -- key for highlight data source
as = "zipped", -- name of resulting table
default = { data = { v = args.defaultValue } } -- default value for geographic objects that could not be joined
}
}
}
},
marks =
{
-- output markings (map paths and highlights)
{
type = "path",
from = { data = "countries" },
properties =
{
enter = { path = { field = "path" } },
update = { fill = { field = "zipped.data.v" } },
hover = { fill = { value = "darkgrey" } }
}
}
},
legends = legend
}
if (scales) then
output.scales = scales
output.marks[1].properties.update.fill.scale = "color"
end
flags = args.debug_json and mw.text.JSON_PRETTY
return mw.text.jsonEncode(output, flags)
end
 
-- ===================================================================================
-- Interface function for template:Mappa a colori
-- ===================================================================================
function p.map(frame)
local args = Args(getArgs(frame, {parentOnly = true}))
map_args = {}
-- map path data for geographic objects
map_args.basemap = args:value('basemap', cfg.default.world_map)
-- scaling factor
map_args.scale = args:number('scale', cfg.default.scale)
-- map projection, see https://github.com/mbostock/d3/wiki/Geo-Projections
map_args.projection = args:value('projection', "equirectangular")
-- defaultValue for geographic objects without data
map_args.defaultValue = args:value('defaultValue')
map_args.scaleType = args:value('scaleType' , "linear")
-- minimaler Wertebereich (nur für numerische Daten)
map_args.domainMin = args:number('domainMin')
-- maximaler Wertebereich (nur für numerische Daten)
map_args.domainMax = args:number('domainMax')
-- Farbwerte der Farbskala (nur für numerische Daten)
local palette = stringArray(args:value('colors'))
if not palette then palette = stringArray(args:value('color')) end
map_args.colors = generate_color_palette(palette)
-- show legend
map_args.legend = args[cfg.localization.internal_legend]
-- map data are key-value pairs: keys are non-lowercase strings (ideally ISO codes) which need
-- to match the "id" values of the map path data
map_args.values = {}
local isNumbers = nil
for name, value in pairs(args) do
if mw.ustring.find(name, "^[^%l]+$") then
if isNumbers == nil then isNumbers = tonumber(value) end
local data = { id = name, v = value }
if isNumbers then data.v = tonumber(data.v) end
map_args.values[#arguments.values+1] = data
end
end
if not map_args.defaultValue then
if isNumbers then map_args.defaultValue = 0 else map_args.defaultValue = "silver" end
end
map_args.isNumbers = isNumbers
map_args.debug_json = args:is_yes('debug_json')
local output_json = p.map_json(map_args)
if map_args.debug_json then
return frame:extensionTag('syntaxhighlight', output_json)
end
return frame:extensionTag('graph', output_json)
end
 
function p.palette_list(frame)
local output = { '<table class="wikitable"><tr><th>Nome</th><th>Colori</th></tr>'}
local palette_name = {}
for name,colors in pairs(cfg.colors_palette) do
palette_name[#palette_name+1] = name
end
table.sort(palette_name)
for _,name in ipairs(palette_name) do
dump(output, '<tr><td>' .. name .. '</td><td>')
for _,color in ipairs(cfg.colors_palette[name][2]) do
dump(output, string.format('<span style="border:none;background-color:%s;color:%s;">██</span>', color, color))
end
dump(output, '</td></tr>')
end
dump(output, '</table>')
return table.concat(output)
end