Module:Datastaven
Uiterlijk
![]() | Deze module maakt gebruik van TemplateStyles: |
Deze module kan gebruikt worden voor het in een staafdiagram weergeven van klasseringen / aantallen / rangen / posities / standen / eindstanden of iets in die trant. Of iets anders.
Deze module wordt toegepast in Sjabloon:Datastaven. Zie voor een overzicht van parameters de documentatie aldaar.
Gebruik
Standaard:
{{#invoke:Datastaven|main}}
Speciaal voor eindstanden kan ook gebruik worden gemaakt van:
{{#invoke:Datastaven|main|type=eindstanden}}
Kleuren
De kleuren van de staven kunnen zowel ingesteld worden op de groepen als op individuele staven. Als er geen kleur is opgegeven wordt er een uit het standaardkleurenpalet gehaald:
(Bewerk)
(Bewerk)
Zie ook
- Module:Datastaven/Groepen - Een verzameling met voorgedefinieerde groepen die gebruikt kunnen worden met deze module.
require('Module:No globals')
local p = {}
local getArgs = require('Module:Arguments').getArgs
local unpackItem = require('Module:Item').unpack
local _delink = require('Module:Delink')._delink
local _yesno = require('Module:Yesno')
local templatestyles = 'Module:Datastaven/styles.css'
local data = {}
local chartHeight = 180 -- In px
local extraHeight = 20 -- Extra height per extra tier, in px
local barWidth = 3 -- In em
local truncateX = false
local invertY = false
local ySuffix = ''
local colors = {
'#325a8d', '#7197b6', '#db0c23', '#e36271',
'#e99fa8', '#618b25', '#6bd425', '#eee8aa',
'#eca72c', '#fff943', '#ffe4e1', '#dda0dd',
'#0b6b61', '#3ec9a6', '#edf669', '#aabc1e',
'#ee2b08', '#95291d', '#999999', '#333333'
}
local noGroupColor = '#002a62'
-- Translations for parameter names
local w = {
type = 'type',
rankings = 'eindstanden',
customLegend = 'aangepasteLegenda',
chartHeight = 'grafiekhoogte',
barWidth = 'staafbreedte',
groups = 'groepen',
truncateX = 'xAfkappen',
invertY = 'yOmkeren',
x = 'x',
y = 'y',
yMax = 'yMax',
ySuffix = 'ySuffix',
group = 'groep',
subgroup = 'subgroep',
division = 'afd', -- Alias of subgroup
label = 'label',
color = 'kleur',
tier = 'niveau',
value = 'waarde',
history = 'historie',
from = 'van',
till = 'tot',
}
local function isItem(arg)
-- An arg is considered an item if it starts with a pipe character.
return string.find(mw.text.trim(arg), '|', 1, true) == 1
end
local function formatItem(item)
-- Aliases
item[w.x] = item[w.x] or item['1']
item[w.y] = item[w.y] or item['2']
item[w.group] = item[w.group] or item['3']
item[w.subgroup] = item[w.subgroup] or item[w.division]
item['1'] = nil
item['2'] = nil
item['3'] = nil
item[w.division] = nil
item[w.y] = tonumber(item[w.y])
item[w.yMax] = tonumber(item[w.yMax])
item[w.tier] = tonumber(item[w.tier])
end
local function yesno(value, default)
if _yesno(value) ~= nil then return _yesno(value) else return default end
end
local function pickFromGroup(param, bar)
local value = bar[w.group] and data.groups[bar[w.group]][w[param]]
if not value then return nil
elseif type(value) ~= "table" then return value
elseif value[w.history] and type(value[w.history]) == "table" then
for _, h in ipairs(value[w.history]) do
local x = tonumber(bar[w.x])
local from = h[w.from] or 0
local till = h[w.till] or 9999
if x > from and x <= till then return h[w.value] end
end
end
return value[w.value]
end
local function pick(param, bar)
-- Picks a parameter from bar or else from it's group or else from data.
return bar[w[param]]
or pickFromGroup(param, bar)
or data[param]
end
local function minn(table)
-- Returns the lowest positive numerical index of the given table, or zero
-- if the table has no numerical indices.
local minn, k = nil, nil
repeat
k = next(table, k)
if type(k) == 'number' then
if k == 1 then return 1 end
if minn == nil or k < minn then minn = k end
end
until not k
return minn or 0
end
local function countTiers(tiers)
-- Gives the number of tiers, empty inbetweens included.
return table.maxn(tiers) - minn(tiers) + 1
end
local function rankTiers(tiers)
-- Ranks the tiers bottom up for convenience, since heights will be calculated
-- from the bottom up. Example:
-- [2] = true, [2] = 4,
-- [3] = true, --> [3] = 3,
-- [5] = true, [5] = 1,
local highestTierNumber = table.maxn(tiers)
for n, _ in pairs(tiers) do
tiers[n] = highestTierNumber - n + 1
end
return tiers
end
local function mergeTables(t1, t2)
if t1 and t2 then for k, v in pairs(t2) do t1[k] = v end end
return t1
end
local function importGroups(basename)
if basename == nil or basename == '' then return {} end
return require('Module:Datastaven/Groepen/' .. basename)
end
local function extractData(args)
-- Extract all the data we need from the args.
data = {
barWidth = args[w.barWidth] or barWidth,
truncateX = yesno(args[w.truncateX], truncateX),
invertY = yesno(args[w.invertY], invertY),
yMax = args[w.yMax] or 0,
ySuffix = args[w.ySuffix] or ySuffix,
noGroupColor = args[w.color] or noGroupColor,
customLegend = args[w.customLegend],
bars = {},
groups = {},
tiers = {},
}
-- Import preset groups.
local presetGroups = importGroups(args[w.groups])
-- Extract from inline groups.
for i = 1, 20 do
local arg = args[w.group .. i]
if arg and isItem(arg) then
local group = unpackItem(arg)
if group[w.group] then
group = mergeTables(presetGroups[group[w.group]] or {}, group)
data.groups[group[w.group]] = group -- Add to our groups
group[w.label] = group[w.label] or group[w.group]
group[w.group] = nil
formatItem(group)
end
end
end
-- Extract from items.
for _, arg in ipairs(args) do
if isItem(arg) then
local bar = unpackItem(arg)
formatItem(bar)
table.insert(data.bars, bar)
if bar[w.y] then
data.yMax = math.max(data.yMax, bar[w.y])
end
if bar[w.group] and data.groups[bar[w.group]] == nil then
data.groups[bar[w.group]] = presetGroups[bar[w.group]]
or { [w.label] = bar[w.group] }
end
local tier = tonumber(pick('tier', bar))
if tier then data.tiers[tier] = true end
end
end
data.tiers = rankTiers(data.tiers)
data.tiersCount = countTiers(data.tiers)
data.chartHeight = args[w.chartHeight] or chartHeight + extraHeight * (data.tiersCount - 1)
return data
end
local function calculateBarHeight(bar)
local y = bar[w.y]
local yMax = pick('yMax', bar)
local tierRank = data.tiers[pick('tier', bar)] or 1
local h
if data.invertY then
h = y and (1 - ((y - 1) / yMax)) * 100 -- Height % (within it's tier)
if y > yMax then h = 0 end
else
h = y and y / yMax * 100
if y > yMax then h = 100 end
end
h = (h + tierRank * 100 - 100) / data.tiersCount -- Add heights of lower tiers
h = math.floor(h * 1000) / 1000 -- Truncate number
return h
end
local function pickColor(bar)
local color = pick('color', bar)
if color then return color end
if bar[w.group] then
color = table.remove(colors) or '#fff'
data.groups[bar[w.group]][w.color] = color
else
color = data.noGroupColor
end
return color
end
local function delink(text)
-- Removes (wiki)links from a text.
if not type(text) == 'string' then return text end
return _delink({ text, urls = 'no', comments = 'no', whitespace = 'no' })
end
local function drawTooltip(bar)
local text = mw.html.create()
if bar[w.x] and bar[w.x] ~= '' then text:tag('b'):wikitext(bar[w.x]) end
if bar[w.x] and bar[w.y] then text:wikitext(' ') end
if bar[w.y] then text:wikitext(bar[w.y] .. pick('ySuffix', bar)) end
if bar[w.group] then
if tostring(text) ~= '' then text:tag('br') end
text:wikitext(delink(pick('label', bar)))
if bar[w.subgroup] then text:wikitext(' ' .. bar[w.subgroup]) end
end
return mw.html.create()
:newline()
:tag('div')
:addClass('es-tip')
:node(text)
:done()
end
local function drawBars()
local bars = mw.html.create()
for _, bar in ipairs(data.bars) do
local x = bar[w.x]
local y = bar[w.y]
local h = y and calculateBarHeight(bar) .. '%'
local c = pickColor(bar)
if x and data.truncateX then x = string.sub(x, -2) end -- Show only the last two digits of the year
bars
:newline()
:tag('li')
:addClass('es-bar')
:attr('data-x', x)
:attr('data-y', y)
:css('height', h)
:css('background-color', c)
:node(drawTooltip(bar))
:newline()
end
return bars
end
local function orderItems(g1, g2)
-- Order by tier number (asc).
g1[w.tier] = g1[w.tier] or 1000
g2[w.tier] = g2[w.tier] or 1001
return g1[w.tier] < g2[w.tier]
end
local function getLegendItems()
local legendItems = {}
if data.customLegend then
-- Pick only the groups chosen in `customLegend`. Keep it's particular order.
local list = mw.text.split(data.customLegend, "%s*,%s*")
for i = 1, #list do
if data.groups[list[i]] then table.insert(legendItems, data.groups[list[i]]) end
end
else
-- Pick all the in the chart occuring groups and order by tier number.
for _, g in pairs(data.groups) do table.insert(legendItems, g) end
table.sort(legendItems, orderItems)
end
return legendItems
end
local function drawLegend()
local legend = mw.html.create('div')
:addClass('es-legend')
:tag('ul')
for _, group in pairs(getLegendItems()) do
legend
:newline()
:tag('li')
:tag('span')
:css('background-color', group[w.color] or '#fff')
:done()
:wikitext(group[w.label])
end
return legend:done()
end
local function drawChart(args)
data = extractData(args)
-- return mw.dumpObject(data)
if #data.bars == 0 then
return "''Geen data om weer te geven.''"
end
local chart = mw.html.create()
:tag('div')
:addClass('es-chart')
:css('overflow-y', 'hidden')
:tag('ul')
:addClass('es-grid')
:css('width', data.barWidth * #data.bars .. 'em')
:css('height', data.chartHeight .. 'px')
:node(drawBars())
:allDone()
:node(drawLegend())
return tostring(chart)
end
function p.main(frame)
local args = getArgs(frame)
args[1] = args[1] or '' -- Data won't show when args[1] is absent
-- Settings for rankings.
if args[w.type] == w.rankings then
invertY = true
barWidth = 1.5
truncateX = true
ySuffix = 'e'
end
return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles } } .. drawChart(args)
end
return p