Module:Datastaven
Uit Wiki Raamsdonks Erfgoed
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 = {
'#999999', '#eeeeee', '#95291d', '#ea6a1a', '#aabc1e',
'#edf669', '#3ec9a6', '#0b6b61', '#dda0dd', '#ffe4e1',
'#fff943', '#eca72c', '#eee8aa', '#6bd425', '#618b25',
'#e99fa8', '#e36271', '#db0c23', '#7197b6', '#325a8d'
}
local noGroupColor = '#999999'
-- Translations for parameter names
local w = {
type = 'type',
rankings = 'eindstanden',
customLegend = 'aangepasteLegenda',
chartHeight = 'grafiekhoogte',
barWidth = 'staafbreedte',
groups = 'groepen',
note = 'opm',
truncateX = 'xAfkappen',
invertY = 'yOmkeren',
x = 'x',
y = 'y',
yMax = 'yMax',
ySuffix = 'ySuffix',
yLabel = 'yLabel',
yNote = 'yOpm',
group = 'groep',
subgroup = 'afd',
label = 'label',
color = 'kleur',
tier = 'niveau',
value = 'waarde',
domains = 'domeinen',
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['1'] = nil
item['2'] = nil
item['3'] = 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 pick(param, item)
-- Picks a parameter from an item or else from it's group or else from data.
local value = item[w[param]]
or item[w.group] and data.groups[item[w.group]][w[param]]
or data[param]
if type(value) == "table" then
-- Pick the value where x is within the domain.
local x = tonumber(item[w.x])
local domains = value[w.domains]
if x and domains and type(domains) == "table" then
for _, h in ipairs(domains) do
if h[w.from] or h[w.till] then
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
end
return value[w.value]
end
return value
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,
note = args[w.note],
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.label] or bar[w.group],
[w.color] = bar[w.color] }
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 = 0
if y then
if data.invertY then
h = (1 - ((y - 1) / yMax)) * 100 -- Height % (within it's tier)
if y > yMax then h = 0 end
else
h = y / yMax * 100
if y > yMax then h = 100 end
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()
local x = bar[w.x]
local y = bar[w.y] and bar[w.y] .. pick('ySuffix', bar) or bar[w.yLabel]
local yNote = bar[w.yNote]
if x and x ~= '' then text:tag('b'):wikitext(x) end
if x and (y or yNote) then text:wikitext(' ') end
if y then text:wikitext(y) end
if y and yNote then text:wikitext(' ') end
if yNote then text:wikitext(yNote) end
if bar[w.group] or bar[w.label] 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.yLabel] or bar[w.y]
local h = 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('tabindex', 0)
: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(item1, item2)
-- Order by tier number (asc).
local t1 = pick('tier', item1) or 1000
local t2 = pick('tier', item2) or 1000
return t1 < t2
end
local function getLegendItems()
local legendItems = {}
local groups = {}
-- Copy data.groups to groups.
for k, g in pairs(data.groups) do groups[k] = g end
-- Start legendItems with the groups chosen in `customLegend`.
if data.customLegend then
local codes = mw.text.split(data.customLegend, "%s*,%s*")
for _, code in pairs(codes) do
table.insert(legendItems, groups[code])
groups[code] = nil
end
end
-- Order the (remaining) groups by tier number, then append to legendItems.
local iGroups = {}
for _, g in pairs(groups) do table.insert(iGroups, g) end
table.sort(iGroups, orderItems)
for _, g in ipairs(iGroups) do table.insert(legendItems, g) end
return legendItems
end
local function drawLegend()
local groups = getLegendItems()
local legend = mw.html.create('div')
:addClass('es-legend')
:tag('ul')
for _, group in pairs(groups) do
legend
:newline()
:tag('li')
:tag('span')
:css('background-color', pick('color', group) or '#fff')
:done()
:wikitext(pick('label', group))
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()
if data.note then chart:tag('div'):addClass('es-note'):wikitext(data.note) end
chart: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.4
truncateX = true
ySuffix = 'e'
end
return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles } } .. drawChart(args)
end
return p