Module:Diagramme
Ce module exporte deux fonctions :
histogramme
: un diagramme en colonnes (bar chart)camembert
: un diagramme en secteurs, circulaire (pie chart)
Histogramme
[modifier le wikicode]Le module est appelé par le code {{#invoke:Diagramme | histogramme |…|…|…}}
Paramètres
[modifier le wikicode]Paramètre | Résultat |
---|---|
séparateur | Délimite plusieurs valeurs lorsqu'il est spécifié en utilisant par défaut le symbole à deux points (:). En principe vous n'avez pas à toucher à ce paramètre |
largeur | Si ce paramètre est renseigné, la valeur indiquée doit être au minimum de 200. (La valeur par défaut est de 500.) |
hauteur | Si ce paramètre est renseigné, la valeur minimale est de 200. (La valeur par défaut est de 350.) |
groupe n | « n » étant un nombre, écrivez groupe 1, groupe 2, etc. en fonction de groupes de colonnes présents dans le graphique ; les valeurs devant être renseignées, voir l'exemple illustré. |
infobulle n | Info-bulle associée à une colonne spécifique. Si aucune info-bulle n'est définie spécifiquement pour une colonne, et que cette dernière a un lien, alors ce lien sera utilisée comme info-bulle. Sinon, l'info-bulle sera combiné à partir du nom du groupe et la valeur, éventuellement avec "unités préfixe" et "unités suffixe" .. |
liens n | Les liens vers les articles associés à chaque colonne. |
empilement | Empilement au sein d'une même colonne. Le seul fait de mentionner ce paramètre signifie « oui » (même en laissant la valeur en blanc). Pour dire « non », il suffit de l'omettre. |
valeur cumulée | Fonctionne uniquement avec le paramètre empilement , destinée à faire afficher dans l'info-bulle la valeur cumulée de tous les blocs (non testé).
|
couleurs | Couleurs désignant les différents groupes ; il devrait y avoir en avoir autant que le nombre de groupes. Utilisez les noms de couleurs html ou les notations #xxx ou #xxxxxx. |
légendes | Les légendes pour chaque groupe de colonne ; la syntaxe wiki peut être employée pour insérer un lien interne. |
légendes cachées | Les légendes du groupe ne seront pas affichés en dessous du tableau. Toute valeur signifie « oui ». Pour dire « non », il suffit de ne pas spécifier ce paramètre, ou laisser la valeur à blanc. |
échelle par groupe | Configure une échelle Y distincte pour chaque groupe. Incompatible avec empilement . Même si certaines de ces échelles sont identiques, elles seront tirées au sort séparément lorsque ce paramètre est activé. Toute valeur non-vide signifie « oui ». Pour dire « non », il suffit de ne spécifiez pas ce paramètre, ou laisser la valeur à blanc.
|
préfixe | Préfixe qui apparaîtrait dans l'info-bulle. Par exemple, si vous ajoutiez le symbole $, si les valeurs s'afficheraient sous la forme « $500 » au lieu de « 500 » dans l'info-bulle |
unité | Suffixe qui apparaîtrait dans l'info-bulle. Si vous utilisez € l'info-bulle affichera par exemple 88€ au lieu de 88 . Utiliser _€ affichera 88 € (le tiret bas est remplacé par un espace dans l'info-bulle).
|
noms | Les légendes affectées aux différents groupes de colonnes. |
Modules externes et autres éléments dont ce module a besoin pour fonctionner :
Table des couleurs
[modifier le wikicode]Table des couleurs
Page d’aide principale : Aide:Couleurs
|
Exemple basique
[modifier le wikicode]{{ #invoke:Diagramme | histogramme | groupe 1 = 40 : 50 : 60 : 20 | groupe 2 = 20 : 60 : 12 : 44 | groupe 3 = 55 : 14 : 33 : 5 | liens 1 = Pomme : McIntosh (pomme) : Golden delicious | liens 2 = Banane : Abricot : Pêche (fruit) | liens 3 = Orange : Poire : Raisin | infobulle 2 = infobulle 1 : infobulle 2 : infobulle 3 : infobulle 4 | couleurs = green : yellow : orange | noms = Pomme: Banane : Orange | légendes = Avant : Pendant : Après : Post mortem }}
- Pomme
- Banane
- Orange
Exemple avec empilement
[modifier le wikicode]Le même graphique, avec une taille plus petite, utilisant les paramètres hauteur, largeur, empilement, unité
.
{{ #invoke:Diagramme | histogramme | hauteur = 250 | largeur = 300 | empilement = 1 | groupe 1 = 40 : 50 : 60 : 20 | groupe 2 = 20 : 60 : 12 : 44 | groupe 3 = 55 : 14 : 33 : 5 | couleurs = green : yellow : orange | noms = Pomme : Banane : Orange | unité = kg | légendes = Avant : Pendant : Après : Post mortem }}
- Pomme
- Banane
- Orange
Exemple avec une échelle différente par groupe
[modifier le wikicode]Il est possible d'afficher une échelle et des unités différentes pour chaque groupe :
{{ #invoke:Diagramme | histogramme | largeur = 800 | groupe 1 = 1500000 : 2500000 : 3500000 | groupe 2 = 200 : 5000 : 45000 | groupe 3 = 2000 : 5000 : 20000 | couleurs = red : blue : green | noms = Population : Auto : Coût moyen | légendes = 1920 : 1965 : 2002 | infobulle 2 = : Il n'y a pas de donnée fiable pour le nombre de voiture en 1965. Nous avons pris 5000 comme étant la meilleure estimation. | unité =::_€ | échelle par groupe = 1 }}
Regardez le paramètre "unit suffix" : nous n'avons pas besoin du préfixe pour les 2 premiers groupes, nous utilisons donc des colonnes sans contenu.
Prenez connaissance aussi de l'info-bulle spéciale pour "Auto"
- Population
- Auto
- Coût moyen
Exemple avec un grand nombre de légendes
[modifier le wikicode]{{ #invoke:Diagramme | histogramme | largeur = 800 | hauteur = 550 | groupe 1 = 1:2:3:4:5:4:3:2:1 | groupe 2 = 1:2:3:4:5:4:3:2:1 | groupe 3 = 1:2:3:4:5:4:3:2:1 | groupe 4 = 1:2:3:4:5:4:3:2:1 | groupe 5 = 1:2:3:4:5:4:3:2:1 | groupe 6 = 1:2:3:4:5:4:3:2:1 | groupe 7 = 1:2:3:4:5:4:3:2:1 | groupe 8 = 1:2:3:4:5:4:3:2:1 | groupe 9 = 1:2:3:4:5:4:3:2:1 | groupe 10 = 1:2:3:4:5:4:3:2:1 | groupe 11 = 1:2:3:4:5:4:3:2:1 | groupe 12 = 1:2:3:4:5:4:3:2:1 | groupe 13 = 1:2:3:4:5:4:3:2:1 | groupe 14 = 1:2:3:4:5:4:3:2:1 | groupe 15 = 1:2:3:4:5:4:3:2:1 | groupe 16 = 1:2:3:4:5:4:3:2:1 | groupe 17 = 1:2:3:4:5:4:3:2:1 | groupe 18 = 1:2:3:4:5:4:3:2:1 | groupe 19 = 1:2:3:4:5:4:3:2:1 | groupe 20 = 1:2:3:4:5:4:3:2:1 | groupe 21 = 1:2:3:4:5:4:3:2:1 | couleurs = Silver:Gray:Black:Red:Maroon:Yellow:Olive:Lime:Green:Aqua:Teal:Blue:Navy:Fuchsia:Purple:ForestGreen:Tomato:LightSeaGreen:RosyBrown:DarkOliveGreen:MediumVioletRed | noms = Alabama:Alaska:Arizona:Arkansas:California:Colorado:Connecticut:Delaware:Florida:Georgia: Hawaii:Idaho:Illinois:Indiana:Iowa:Kansas:Kentucky:Louisiana:Maine:Maryland:Massachusetts | légendes = 1920 : 1930 : 1940: 1950 : 1960 : 1970 : 1990 : 2000 : 2010 | préfixe = $ | unité = _Billion | empilement = 1 }}
Principalement pour tester l'affichage avec un grand nombre de groupes.
- Alabama
- Alaska
- Arizona
- Arkansas
- California
- Colorado
- Connecticut
- Delaware
- Florida
- Georgia
- Hawaii
- Idaho
- Illinois
- Indiana
- Iowa
- Kansas
- Kentucky
- Louisiana
- Maine
- Maryland
- Massachusetts
Exemple divers
[modifier le wikicode]{{ #invoke:Diagramme | histogramme | groupe 1 = 1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16:17:18:19:20:21:22:23:24:25:26:27:28:29:30 :31:32:33:34:35:36:37:38:39:40:41:42:43:44:45:46:47:48:49:50:51:52:53:54:55:56:57:58:59 | unité = _Things | noms = Some | légendes = ::::1940::::::::::1950::::::::::1960::::::::::1970::::::::::1980::::::::::1990:::: }}
Diagramme circulaire
[modifier le wikicode]Le module est appelé par le code {{#invoke:Diagramme | camembert |…|…|…}}
Paramètres
[modifier le wikicode]Paramètres | Résultat |
---|---|
séparateur | Idem tableau précédent |
rayon | Le nombre exprimant le nombre de pixels |
secteurs | Utilisez le paramètre séparateur à l'intérieur des parenthèses (Table Uplet (Tuple)) = ( Value1 : Name1 : Color1 : Link1 ) ( Value2 : Name2 : Color2 : Link2 ) ... Les valeurs sont des nombres qui peuvent être entiers ou des fractions décimales, ou utilisant la notation scientifique : 7.24e6, 7,240,000, ou 7240000.00 sont toutes acceptables pour 7 millions et 240 mille. Les couleurs sont optionnelles. Vous pouvez utiliser les couleurs du web, comme "red" ou "#FF0000". Jusqu'à 26 couleurs sont définies, mais si votre diagramme comporte plus de 26 secteurs, vous devez définir les couleurs à partir du 27e et plus. Les liens peuvent être externes ou internes, y compris des liens ancrés à des sections présentes dans l'article lui-même.
|
secteur n | Syntaxe alternative à secteurs . n est le numéro de la tranche, en commençant par 1. Veillez à ne pas sauter de numéro : si vous définissez secteur 1, secteur 2, secteur 4, secteur 5 ..., en sautant secteur 3 , seuls les deux premières tranches seront affichées. La syntaxe est incompatible avec secteurs , c'est à dire qu'ils ne doivent pas être utilisés en association dans le même appel. Les deux codes secteurs et secteur n dans le même appel entraînerait des résultats imprévisibles. La valeur est comme un « tuple », comme expliqué ci-dessus, mais sans les parenthèses:
| secteur 1 = Value1 : Name1 : Color1 : Link1 | secteur 2 = Value2 : Name2 : Color2 : Link2 | ... L'intérêt de cette syntaxe alternative est de vous permettre d'utiliser des parenthèses dans les noms, des liens et des couleurs. |
pourcentage | Si utilisé, le pourcentage de chaque tranche sera calculé et ajouté à la légende: si vous avez deux tranches, comme suit: ( 1: younglings ) ( 3 : Elders ), et utilisez pourcentage , les légendes deviendront « younglings: 1 (25%) » et « aînés: 3 (75%) », au lieu de simplement « Younglings: 1 » and « elders: 3 ». Toute valeur non-vide signifie « oui ». Pour dire « non », il suffit de ne pas spécifier ce paramètre à tout, ou laisser la valeur à blanc.
|
préfixe | Idem tableau précédent |
unité | Idem tableau précédent |
légendes cachées | Idem tableau précédent |
Exemple avec paramètre « secteurs »
[modifier le wikicode]{{#invoke:Diagramme|camembert | rayon = 150 | secteurs = ( 1000000 : Pommes) ( 2000000 : Bananes : gold) ( 1440000 : Abricots ) ( 6.4e5 : Pêches : : [[Pêche (fruit)|Pêches]] ) ( 750,000 : Ananas) | unité = _tonnes | pourcentage = true }}
Vous pouvez laisser la couleur vide pour utiliser la valeur par défaut, mais pour ajouter le lien « pêche », nous avons du ajouter un séparateur supplémentaire pour marquer le paramètre de couleur omis. Également, les valeurs peuvent être fournies avec le format « langage spécifique » (comme 1,000), ou la notation scientifique comme 6.4e5. Dans les légendes, les numéros seront toujours affichés normalement (peut être différent si le module est importé dans d'autres wikis).
- Pommes: 1 000 000 tonnes (19,7 %)
- Bananes: 2 000 000 tonnes (39,4 %)
- Abricots: 1 440 000 tonnes (28,3 %)
- Pêches: 640 000 tonnes (12,6 %)
- Ananas: 750 tonnes (0 %)
Exemple avec paramètre « secteur n »
[modifier le wikicode]Un exemple avec la syntaxe alternative avec "secteur 1", "secteur 2" etc.
{{#invoke:Diagramme|camembert |rayon= 200 |unité = _Unités | secteur 1 = 1 : 1 | secteur 2 = 7 : 7 | secteur 3 = 8 : 8 | secteur 4 = 9 : 9 | secteur 5 = 10 : 10 | secteur 6 = 11 : 11 | secteur 7 = 12 : 12 | secteur 8 = 13 : 13 | secteur 9 = 14 : 14 | secteur 10 = 15 : 15 | secteur 11 = 16 : 16 | secteur 12 = 17 : 17 | secteur 13 = 18 : 18 | secteur 14 = 19 : 19 | secteur 15 = 20 : 20 | secteur 16 = 21 : 21 | secteur 17 = 22 : 22 | secteur 18 = 23 : 23 | secteur 19 = 24 : 24 | secteur 20 = 25 : 25 | secteur 21 = 26 : 26 | secteur 22 = 27 : 27 | secteur 23 = 28 : 28 | secteur 24 = 29 : 29 | secteur 25 = 30 : 30 | secteur 26 = 31 : 31 | pourcentage = true }}
- 1: 1 Unités (0,2 %)
- 7: 7 Unités (1,5 %)
- 8: 8 Unités (1,7 %)
- 9: 9 Unités (1,9 %)
- 10: 10 Unités (2,1 %)
- 11: 11 Unités (2,3 %)
- 12: 12 Unités (2,5 %)
- 13: 13 Unités (2,7 %)
- 14: 14 Unités (2,9 %)
- 15: 15 Unités (3,2 %)
- 16: 16 Unités (3,4 %)
- 17: 17 Unités (3,6 %)
- 18: 18 Unités (3,8 %)
- 19: 19 Unités (4 %)
- 20: 20 Unités (4,2 %)
- 21: 21 Unités (4,4 %)
- 22: 22 Unités (4,6 %)
- 23: 23 Unités (4,8 %)
- 24: 24 Unités (5 %)
- 25: 25 Unités (5,3 %)
- 26: 26 Unités (5,5 %)
- 27: 27 Unités (5,7 %)
- 28: 28 Unités (5,9 %)
- 29: 29 Unités (6,1 %)
- 30: 30 Unités (6,3 %)
- 31: 31 Unités (6,5 %)
--<source lang=lua>
--[[
keywords are used for languages: they are the names of the actual
parameters of the template
]]
local keywords = {
barChart = 'histogramme',
pieChart = 'camembert',
width = 'largeur',
height = 'hauteur',
stack = 'empilement',
colors = 'couleurs',
group = 'groupe',
xlegend = 'légendes',
tooltip = 'infobulle',
accumulateTooltip = 'valeur cumulée',
links = 'liens',
defcolor = 'couleur par défaut',
scalePerGroup = 'échelle par groupe',
unitsPrefix = 'préfixe',
unitsSuffix = 'unité',
groupNames = 'noms',
hideGroupLegends = 'légendes cachées',
slices = 'secteurs',
slice = 'secteur',
radius = 'rayon',
percent = 'pourcentage',
delimiter = 'séparateur',
} -- here is what you want to translate
local defColors = require "Module:Plotter/DefaultColors"
local hideGroupLegends
local function nulOrWhitespace( s )
return not s or mw.text.trim( s ) == ''
end
local function createGroupList( tab, legends, cols )
if #legends > 1 and not hideGroupLegends then
table.insert( tab, mw.text.tag( 'div' ) )
local list = {}
local spanStyle = "padding:0 1em;background-color:%s;box-shadow:2px -1px 4px 0 silver;margin-right:1em;"
for gi = 1, #legends do
local span = mw.text.tag( 'span', { style = string.format( spanStyle, cols[gi] ) }, ' ' ) .. ' '.. legends[gi]
table.insert( list, mw.text.tag( 'li', {}, span ) )
end
table.insert( tab,
mw.text.tag( 'ul',
{style="width:100%;list-style:none;-webkit-column-width:12em;-moz-column-width:12em;column-width:12em;"},
table.concat( list, '\n' )
)
)
table.insert( tab, '</div>' )
end
end
function pieChart( frame )
local res, imslices, args = {}, {}, frame.args
local radius
local values, colors, names, legends, links = {}, {}, {}, {}, {}
local delimiter = args[keywords.delimiter] or args.delimiter or ':'
local lang = mw.getContentLanguage()
function getArg( s, def, subst, with )
local result = args[keywords[s]] or def or ''
if subst and with then result = mw.ustring.gsub( result, subst, with ) end
return result
end
function analyzeParams()
function addSlice( i, slice )
local value, name, color, link = unpack( mw.text.split( slice, '%s*' .. delimiter .. '%s*' ) )
values[i] = tonumber( lang:parseFormattedNumber( value ) )
or error( string.format( 'Slice %d: "%s", first item("%s") could not be parsed as a number', i, value or '', sliceStr ) )
colors[i] = not nulOrWhitespace( color ) and color or defColors[i * 2]
names[i] = name or ''
links[i] = link
end
radius = getArg( 'radius', 150 )
hideGroupLegends = not nulOrWhitespace( args[keywords.hideGroupLegends] )
local slicesStr = getArg( 'slices' )
local prefix = getArg( 'unitsPrefix', '', '_', ' ' )
local suffix = getArg( 'unitsSuffix', '', '_', ' ' )
local percent = args[keywords.percent]
local sum = 0
local i, value = 0
for slice in mw.ustring.gmatch( slicesStr or '', "%b()" ) do
i = i + 1
addSlice( i, mw.ustring.match( slice, '^%(%s*(.-)%s*%)$' ) )
end
for k, v in pairs(args) do
local ind = mw.ustring.match( k, '^' .. keywords.slice .. '%s+(%d+)$' )
if ind then addSlice( tonumber( ind ), v ) end
end
for _, val in ipairs( values ) do sum = sum + val end
for i, value in ipairs( values ) do
local addprec = percent and string.format( ' (%s %%)', lang:formatNum( math.floor( value / sum * 1000 + 0.5 ) / 10 ) ) or ''
legends[i] = mw.ustring.format( '%s: %s%s%s%s', names[i], prefix, lang:formatNum( value ), suffix, addprec )
links[i] = mw.text.trim( links[i] or mw.ustring.format( '[[#noSuchAnchor|%s]]', legends[i] ) )
end
end
function addRes( ... )
for _, v in pairs( { ... } ) do
table.insert( res, v )
end
end
function createImageMap()
addRes( '{{#tag:imagemap|', 'Image:Circle frame.svg{{!}}' .. ( radius * 2 ) .. 'px' )
addRes( unpack( imslices ) )
addRes( 'desc none', '}}' )
end
function drawSlice( i, q, start )
local color = colors[i]
local angle = start * 2 * math.pi
local sin, cos = math.abs( math.sin( angle ) ), math.abs( math.cos( angle ) )
local wsin, wcos = sin * radius, cos * radius
local s1, s2, w1, w2, w3, w4, width, border
local style
if q == 1 then
border = 'left'
w1, w2, w3, w4 = 0, 0, wsin, wcos
s1, s2 = 'bottom', 'left'
elseif q == 2 then
border = 'bottom'
w1, w2, w3, w4 = 0, wcos, wsin, 0
s1, s2 = 'bottom', 'right'
elseif q == 3 then
border = 'right'
w1, w2, w3, w4 = wsin, wcos, 0, 0
s1, s2 = 'top', 'right'
else
border = 'top'
w1, w2, w3, w4 = wsin, 0, 0, wcos
s1, s2 = 'top', 'left'
end
local style = string.format( 'position:absolute;%s:%spx;%s:%spx;width:%spx;height:%spx', s1, radius, s2, radius, radius, radius )
if start <= ( q - 1 ) * 0.25 then
style = string.format( '%s;border:0;background-color:%s', style, color )
else
style = string.format( '%s;border-width:%spx %spx %spx %spx;border-%s-color:%s', style, w1, w2, w3, w4, border, color )
end
addRes( mw.text.tag( 'div', { class = 'transborder', style = style }, '' ) )
end
function createSlices()
function coordsOfAngle( angle )
return ( 100 + math.floor( 100 * math.cos( angle ) ) ) .. ' ' .. ( 100 - math.floor( 100 * math.sin( angle ) ) )
end
local sum, start = 0, 0
for _, value in ipairs( values ) do sum = sum + value end
for i, value in ipairs(values) do
local poly = { 'poly 100 100' }
local startC, endC = start / sum, ( start + value ) / sum
local startQ, endQ = math.floor( startC * 4 + 1 ), math.floor( endC * 4 + 1 )
for q = startQ, math.min( endQ, 4 ) do drawSlice( i, q, startC ) end
for angle = startC * 2 * math.pi, endC * 2 * math.pi, 0.02 do
table.insert( poly, coordsOfAngle( angle ) )
end
table.insert( poly, coordsOfAngle( endC * 2 * math.pi ) .. ' 100 100 ' .. links[i] )
table.insert( imslices, table.concat( poly, ' ' ) )
start = start + values[i]
end
end
analyzeParams()
if #values == 0 then error( "no slices found - can't draw pie chart" ) end
addRes( mw.text.tag( 'div', { style = string.format( "max-width:%spx", radius * 2 ) } ) )
addRes( mw.text.tag( 'div', { style = string.format( 'position:relative;min-width:%spx;min-height:%spx;max-width:%spx;overflow:hidden;', radius * 2, radius * 2, radius * 2 ) } ) )
createSlices()
addRes( mw.text.tag( 'div', { style = string.format( 'position:absolute;min-width:%spx;min-height:%spx;overflow:hidden;', radius * 2, radius * 2 ) } ) )
createImageMap()
addRes( '</div>' ) -- close "position:relative" div that contains slices and imagemap.
addRes( '</div>' ) -- close "position:relative" div that contains slices and imagemap.
createGroupList( res, legends, colors ) -- legends
addRes( '</div>' ) -- close containing div
return frame:preprocess( table.concat( res, '\n' ) )
end
function barChart( frame )
local res = {}
local args = frame.args -- can be changed to frame:getParent().args
local values, xlegends, colors, tooltips, yscales = {}, {}, {}, {} ,{}, {}, {}
local groupNames, unitsSuffix, unitsPrefix, links = {}, {}, {}, {}
local width, height, stack = 500, 350, false
local delimiter = args[keywords.delimiter] or args.delimiter or ':'
local chartWidth, chartHeight, defcolor, scalePerGroup, accumulateTooltip
local numGroups, numValues
local scaleWidth
function validate()
function asGroups( name, tab, toDuplicate, emptyOK )
if #tab == 0 and not emptyOK then
error( "must supply values for " .. keywords[name] )
end
if #tab == 1 and toDuplicate then
for i = 2, numGroups do tab[i] = tab[1] end
end
if #tab > 0 and #tab ~= numGroups then
error ( keywords[name] .. ' should contain the same number of items as the number of groups (' .. numGroups .. ')')
end
end
-- do all sorts of validation here, so we can assume all params are good from now on.
-- among other things, replace numerical values with mw.language:parseFormattedNumber() result
chartHeight = height - 80
numGroups = #values
numValues = #values[1]
defcolor = defcolor or 'blue'
colors[1] = colors[1] or defcolor
scaleWidth = scalePerGroup and 80 * numGroups or 100
chartWidth = width -scaleWidth
asGroups( 'unitsPrefix', unitsPrefix, true, true )
asGroups( 'unitsSuffix', unitsSuffix, true, true )
asGroups( 'colors', colors, true, true )
asGroups( 'groupNames', groupNames, false, false )
if stack and scalePerGroup then
error( string.format( 'Illegal settings: %s and %s are incompatible.', keyword.stack, keyword.scalePerGroup ) )
end
for gi = 2, numGroups do
if #values[gi] ~= numValues then error( keywords.group .. " " .. gi .. " does not have same number of values as " .. keywords.group .. " 1" ) end
end
if #xlegends ~= numValues then error( 'Illegal number of ' .. keywords.xlegend .. '. Should be exatly ' .. numValues ) end
end
function extractParams()
function testone( keyword, key, val, tab )
i = keyword == key and 0 or key:match( keyword .. "%s+(%d+)" )
if not i then return end
i = tonumber( i ) or error("Expect numerical index for key " .. keyword .. " instead of '" .. key .. "'")
if i > 0 then tab[i] = {} end
for s in mw.text.gsplit( val, '%s*' .. delimiter .. '%s*' ) do
table.insert( i == 0 and tab or tab[i], s )
end
return true
end
for k, v in pairs( args ) do
if k == keywords.width then
width = tonumber( v )
if not width or width < 200 then
error( 'Illegal width value (must be a number, and at least 200): ' .. v )
end
elseif k == keywords.height then
height = tonumber( v )
if not height or height < 200 then
error( 'Illegal height value (must be a number, and at least 200): ' .. v )
end
elseif k == keywords.stack then stack = true
elseif k == keywords.scalePerGroup then scalePerGroup = true
elseif k == keywords.defcolor then defcolor = v
elseif k == keywords.accumulateTooltip then accumulateTooltip = not nulOrWhitespace( v )
elseif k == keywords.hideGroupLegends then hideGroupLegends = not nulOrWhitespace( v )
else
for keyword, tab in pairs( {
group = values,
xlegend = xlegends,
colors = colors,
tooltip = tooltips,
unitsPrefix = unitsPrefix,
unitsSuffix = unitsSuffix,
groupNames = groupNames,
links = links,
} ) do
if testone( keywords[keyword], k, v, tab )
then break
end
end
end
end
end
function roundup( x ) -- returns the next round number: eg., for 30 to 39.999 will return 40, for 3000 to 3999.99 wil return 4000. for 10 - 14.999 will return 15.
local ordermag = 10 ^ math.floor( math.log10( x ) )
local normalized = x / ordermag
local top = normalized >= 1.5 and ( math.floor( normalized + 1 ) ) or 1.5
return ordermag * top, top, ordermag
end
function calcHeightLimits() -- if limits were passed by user, use ithem, otherwise calculate. for "stack" there's only one limet.
if stack then
local sums = {}
for _, group in pairs( values ) do
for i, val in ipairs( group ) do sums[i] = ( sums[i] or 0 ) + val end
end
local sum = math.max( unpack( sums ) )
for i = 1, #values do yscales[i] = sum end
else
for i, group in ipairs( values ) do yscales[i] = math.max( unpack( group ) ) end
end
for i, scale in ipairs( yscales ) do yscales[i] = roundup( scale ) end
if not scalePerGroup then for i = 1, #values do yscales[i] = math.max( unpack( yscales ) ) end end
end
function tooltip( gi, i, val )
if tooltips and tooltips[gi] and not nulOrWhitespace( tooltips[gi][i] ) then return tooltips[gi][i], true end
local groupName = not nulOrWhitespace( groupNames[gi] ) and groupNames[gi] .. ': ' or ''
local prefix = unitsPrefix[gi] or unitsPrefix[1] or ''
local suffix = unitsSuffix[gi] or unitsSuffix[1] or ''
return mw.ustring.gsub(groupName .. prefix .. mw.getContentLanguage():formatNum( tonumber( val ) or 0 ) .. suffix, '_', ' '), false
end
function calcHeights( gi, i, val )
local barHeight = math.floor( val / yscales[gi] * chartHeight + 0.5 ) -- add half to make it "round" insstead of "trunc"
local top, base = chartHeight - barHeight, 0
if stack then
local rawbase = 0
for j = 1, gi - 1 do rawbase = rawbase + values[j][i] end -- sum the "i" value of all the groups below our group, gi.
base = math.floor( chartHeight * rawbase / yscales[gi] ) -- normally, and especially if it's "stack", all the yscales must be equal.
end
return barHeight, top - base
end
function groupBounds( i )
local setWidth = math.floor( chartWidth / numValues )
local setOffset = ( i - 1 ) * setWidth
return setOffset, setWidth
end
function calcx( gi, i )
local setOffset, setWidth = groupBounds( i )
if stack then
local barWidth = math.min( 38, math.floor( 0.8 * setWidth ) )
return setOffset + (setWidth - barWidth) / 2, barWidth
end
setWidth = 0.85 * setWidth
local barWidth = math.floor( 0.75 * setWidth / numGroups )
local left = setOffset + math.floor( ( gi - 1 ) / numGroups * setWidth )
return left, barWidth
end
function drawbar( gi, i, val, ttval )
local color, tooltip, custom = colors[gi] or defcolor or 'blue', tooltip( gi, i, ttval or val )
local left, barWidth = calcx( gi, i )
local barHeight, top = calcHeights( gi, i, val )
local style = string.format("position:absolute;left:%spx;top:%spx;height:%spx;min-width:%spx;max-width:%spx;background-color:%s;box-shadow:2px -1px 4px 0 silver;overflow:hidden;",
left, top, barHeight, barWidth, barWidth, color)
local link = links[gi] and links[gi][i] or ''
local img = not nulOrWhitespace( link ) and mw.ustring.format( '[[File:Transparent.png|1000px|link=%s|%s]]', link, custom and tooltip or '' ) or ''
local tag = mw.text.tag( 'div', { style = style, title = tooltip, }, img ):gsub( '&#nbsp;', ' ' ) -- gsub to correct mw.text.encode bug with unbreakable space
table.insert( res, tag )
end
function drawYScale()
function drawSingle( gi, color, width, single )
local yscale = yscales[gi]
local _, top, ordermag = roundup( yscale * 0.999 )
local numnotches = top <= 1.5 and top * 4
or top < 4 and top * 2
or top
local valStyleStr =
single and 'position:absolute;height=20px;text-align:right;vertical-align:middle;width:%spx;top:%spx;padding:0 2px'
or 'position:absolute;height=20px;text-align:right;vertical-align:middle;width:%spx;top:%spx;left:3px;background-color:%s;color:white;font-weight:bold;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;padding:0 2px'
local notchStyleStr = 'position:absolute;height=1px;min-width:5px;top:%spx;left:%spx;border:1px solid %s;'
for i = 1, numnotches do
local val = i / numnotches * yscale
local y = chartHeight - calcHeights( gi, 1, val )
local div = mw.text.tag( 'div', { style = string.format( valStyleStr, width - 10, y - 10, color ) }, mw.getContentLanguage():formatNum( tonumber( val ) or 0 ) )
table.insert( res, div )
div = mw.text.tag( 'div', { style = string.format( notchStyleStr, y, width - 4, color ) }, '' )
table.insert( res, div )
end
end
if scalePerGroup then
local colWidth = 80
local colStyle = "position:absolute;height:%spx;min-width:%spx;left:%spx;border-right:1px solid %s;color:%s"
for gi = 1, numGroups do
local left = ( gi - 1 ) * colWidth
local color = colors[gi] or defcolor
table.insert( res, mw.text.tag( 'div', { style = string.format( colStyle, chartHeight, colWidth, left, color, color ) } ) )
drawSingle( gi, color, colWidth )
table.insert( res, '</div>' )
end
else
drawSingle( 1, 'black', scaleWidth, true )
end
end
function drawXlegends()
local setOffset, setWidth
local legendDivStyleFormat = "position:absolute;left:%spx;top:10px;min-width:%spx;max-width:%spx;text-align:center;veritical-align:top;"
local tickDivstyleFormat = "position:absolute;left:%spx;height:10px;width:1px;border-left:1px solid black;"
for i = 1, numValues do
if not nulOrWhitespace( xlegends[i] ) then
setOffset, setWidth = groupBounds( i )
-- setWidth = 0.85 * setWidth
table.insert( res, mw.text.tag( 'div', { style = string.format( legendDivStyleFormat, setOffset - 5, setWidth - 10, setWidth - 10 ) }, xlegends[i] or '' ) )
table.insert( res, mw.text.tag( 'div', { style = string.format( tickDivstyleFormat, setOffset + setWidth / 2 ) }, '' ) )
end
end
end
function drawChart()
table.insert( res, mw.text.tag( 'div', { style = string.format( 'max-width:%spx;', width ) } ) )
table.insert( res, mw.text.tag( 'div', { style = string.format("position:relative;min-height:%spx;min-width:%spx;max-width:%spx;", height, width, width ) } ) )
table.insert( res, mw.text.tag( 'div', { style = string.format("float:right;position:absolute;left:%spx;min-height:%spx;min-width:%spx;max-width:%spx;border-left:1px black solid;border-bottom:1px black solid;", scaleWidth, chartHeight, chartWidth, chartWidth ) } ) )
local acum = stack and accumulateTooltip and {}
for gi, group in pairs( values ) do
for i, val in ipairs( group ) do
if acum then acum[i] = ( acum[i] or 0 ) + val end
drawbar( gi, i, val, acum and acum[i] )
end
end
table.insert( res, '</div>' )
table.insert( res, mw.text.tag( 'div', { style = string.format("position:absolute;height:%spx;min-width:%spx;max-width:%spx;", chartHeight, scaleWidth, scaleWidth, scaleWidth ) } ) )
drawYScale()
table.insert( res, '</div>' )
table.insert( res, mw.text.tag( 'div', { style = string.format( "position:absolute;top:%spx;left:%spx;width:%spx;", chartHeight, scaleWidth, chartWidth ) } ) )
drawXlegends()
table.insert( res, '</div>' )
table.insert( res, '</div>' )
createGroupList( res, groupNames, colors )
table.insert( res, '</div>' )
end
extractParams()
validate()
calcHeightLimits()
drawChart()
return table.concat( res, "\n" )
end
return {
barChart = barChart,
['bar chart'] = barChart,
['bar-chart'] = barChart,
[keywords.barChart] = barChart,
pieChart = pieChart,
['pie chart'] = pieChart,
[keywords.pieChart] = pieChart,
}
--</source>