-- Module Charts SVG
local Args, Parms =, local SeriesData, OriginalData =, local SType, YAxis2, Labels, Color, LineShow, LineWidth, LineDash, Marker, MarkerFill, MarkerSize, FillPattern, FillPatternColor =,,,,,,,,,,, local SeriesText, GroupText, ChartText =,, local SeriesCount, BarSeriesCount, SeriesMaxLen, GroupsCount, ChartTextCount = 0, 0, 0, 0, 0local AutoDataPointsLimit, DataPointsCount = 100, 0local DoPie, DoHorizontal, DoGroupsTopDown, DoArea, DoStack, DoStack100, DoYAxis2, DoChartAdjust = false, false, false, false, false, false, false, falselocal Msgs =
local FontSiz, Siz, Pos, Mult, Adjusts =,,,, local ImageWidth, ImageHeightlocal GroupWidth, UnitWidth, BarWidth, BarSpace
local BaseUnit, BaseFontSize, BaseLineWidth = 3, 3, 1
local r, tr, t =
-- To translate parameters, translate the values in the KeyWords table. Do not translate the keys.local KeyWords =
-- To translate messages, translate the values in the MessageTexts table. Do not translate the keys.local MessageTexts =
local DefColor =
-- grayscale equivalents of the above colourslocal GrayColor =
local DashPattern =
--
function barChart(frame)
Args = frame.args
getAllParms
DoStack = (Parms["Stack"] ~= nil) DoStack100 = (Parms["Stack100"] ~= nil) DoYAxis2 = (Parms["Y2Max"] ~= nil) DoChartAdjust = (Parms["ChartAdjust"] ~= nil)
DoHorizontal = (Parms["HorizontalBarGraph"] ~= nil) DoGroupsTopDown = (Parms["HorizontalBarGraph"] ~= nil) and (Parms["GroupsTopDown"] ~= nil)
-- fill internal tables
for i = 1, SeriesCount do SeriesData[i] = transfer(Parms["Series" .. i .. "Values"], SeriesData[i], 2) DataPointsCount = DataPointsCount + #SeriesData[i] SType[i] = KeyWords["bar"] end copyTable(SeriesData, OriginalData) -- preserve original data BarSeriesCount = SeriesCount
build("Series", "YAxis2", SeriesCount, YAxis2, nil, nil, nil)
build("Series", "Labels", SeriesCount, Labels, nil, nil, nil)
build("Series", "Color", SeriesCount, Color, nil, nil, DefColor)
build("Series", "Pattern", SeriesCount, FillPattern, KeyWords["none"], nil, nil) build("Series", "PatternColor", SeriesCount, FillPatternColor, nil, nil, nil)
build("Series", "Text", SeriesCount, SeriesText, nil, nil, nil)
build("Group", "Text", GroupsCount, GroupText, nil, "Group ", nil)
build("ChartText", "", ChartTextCount, ChartText, nil, nil, nil)
if DoStack or DoStack100 then calcStack DoYAxis2 = false end
if not checkParms then -- messages, instead of SVG output return table.concat(r, "\n") end
if not outputDebugInfo then -- stop after debug output return table.concat(r, "\n") end
-- build the SVG
prelimsCommon prelimsAxes
commonTop
codeAxisGrids
stylesAreas defsAreas elementsGraphs
codeAxes
commonBottom
return table.concat(r, "\n")end
function lineChart(frame)
Args = frame.args
getAllParms
DoArea = (Parms["Area"] ~= nil) DoStack = (Parms["Stack"] ~= nil) DoStack100 = (Parms["Stack100"] ~= nil) DoYAxis2 = (Parms["Y2Max"] ~= nil) DoChartAdjust = (Parms["ChartAdjust"] ~= nil)
-- fill internal tables
for i = 1, SeriesCount do SeriesData[i] = transfer(Parms["Series" .. i .. "Values"], SeriesData[i], 2) DataPointsCount = DataPointsCount + #SeriesData[i] SType[i] = KeyWords["line"] end copyTable(SeriesData, OriginalData) -- preserve original data BarSeriesCount = 0
build("Series", "YAxis2", SeriesCount, YAxis2, nil, nil, nil)
build("Series", "Labels", SeriesCount, Labels, nil, nil, nil)
build("Series", "Color", SeriesCount, Color, nil, nil, DefColor)
build("Series", "Line", SeriesCount, LineShow, "yes", nil, nil) -- the default line visibility is 'yes' build("Series", "Width", SeriesCount, LineWidth, Parms["GraphLineWidth"], nil, nil) build("Series", "Dash", SeriesCount, LineDash, KeyWords["none"], nil, nil)
build("Series", "Marker", SeriesCount, Marker, nil, nil, nil) -- the default marker type is nil build("Series", "MarkerSize", SeriesCount, MarkerSize, 100, nil, nil) build("Series", "MarkerFill", SeriesCount, MarkerFill, nil, nil, Color)
build("Series", "Pattern", SeriesCount, FillPattern, KeyWords["none"], nil, nil) build("Series", "PatternColor", SeriesCount, FillPatternColor, nil, nil, nil)
build("Series", "Text", SeriesCount, SeriesText, nil, nil, nil)
build("Group", "Text", GroupsCount, GroupText, nil, "Group ", nil)
build("ChartText", "", ChartTextCount, ChartText, nil, nil, nil)
if DoStack or DoStack100 then calcStack DoArea = true DoYAxis2 = false end
if not checkParms then -- messages, instead of SVG output return table.concat(r, "\n") end
if not outputDebugInfo then -- stop after debug output return table.concat(r, "\n") end
-- build the SVG
prelimsCommon prelimsAxes
commonTop
codeAxisGrids
if DoArea then stylesAreas defsAreas else stylesLines defsMarkers end elementsGraphs
codeAxes
commonBottom
return table.concat(r, "\n")end
function scatterChart(frame)
Args = frame.args
getAllParms
DoYAxis2 = (Parms["Y2Max"] ~= nil) DoChartAdjust = (Parms["ChartAdjust"] ~= nil)
-- fill internal tables
for i = 1, SeriesCount do SeriesData[i] = transfer(Parms["Series" .. i .. "Values"], SeriesData[i], 2) DataPointsCount = DataPointsCount + #SeriesData[i] SType[i] = KeyWords["line"] end copyTable(SeriesData, OriginalData) -- preserve original data BarSeriesCount = 0
build("Series", "YAxis2", SeriesCount, YAxis2, nil, nil, nil)
build("Series", "Labels", SeriesCount, Labels, nil, nil, nil)
build("Series", "Color", SeriesCount, Color, DefColor, nil, nil)
build("Series", "Line", SeriesCount, LineShow, KeyWords["none"], nil, nil) -- the default line visibility is 'none' build("Series", "Width", SeriesCount, LineWidth, Parms["GraphLineWidth"], nil, nil) build("Series", "Dash", SeriesCount, LineDash, KeyWords["none"], nil, nil)
build("Series", "Marker", SeriesCount, Marker, nil, true, nil) -- the default marker type is the series number build("Series", "MarkerSize", SeriesCount, MarkerSize, 100, nil, nil) build("Series", "MarkerFill", SeriesCount, MarkerFill, nil, nil, Color)
build("Series", "Text", SeriesCount, SeriesText, nil, nil, nil)
build("ChartText", "", ChartTextCount, ChartText, nil, nil, nil)
if not checkParms then -- messages, instead of SVG output return table.concat(r, "\n") end
if not outputDebugInfo then -- stop after debug output return table.concat(r, "\n") end
-- build the SVG
prelimsCommon prelimsAxes
commonTop
codeAxisGrids
stylesLines defsMarkers elementsGraphs
codeAxes
commonBottom
return table.concat(r, "\n")end
function mixedChart(frame)
Args = frame.args
getAllParms
DoYAxis2 = (Parms["Y2Max"] ~= nil) DoChartAdjust = (Parms["ChartAdjust"] ~= nil)
-- fill internal tables
build("Series", "Type", SeriesCount, SType, "line", nil, nil) for i = 1, SeriesCount do if SType[i]
for i = 1, SeriesCount do SeriesData[i] = transfer(Parms["Series" .. i .. "Values"], SeriesData[i], 2) DataPointsCount = DataPointsCount + #SeriesData[i] end copyTable(SeriesData, OriginalData) -- preserve original data
build("Series", "YAxis2", SeriesCount, YAxis2, nil, nil, nil)
build("Series", "Labels", SeriesCount, Labels, nil, nil, nil)
build("Series", "Color", SeriesCount, Color, nil, nil, DefColor)
build("Series", "Line", SeriesCount, LineShow, "yes", nil, nil) -- the default line visibility is 'yes' build("Series", "Width", SeriesCount, LineWidth, Parms["GraphLineWidth"], nil, nil) build("Series", "Dash", SeriesCount, LineDash, KeyWords["none"], nil, nil)
build("Series", "Marker", SeriesCount, Marker, nil, nil, nil) -- the default marker type is nil build("Series", "MarkerSize", SeriesCount, MarkerSize, 100, nil, nil) build("Series", "MarkerFill", SeriesCount, MarkerFill, nil, nil, Color)
build("Series", "Pattern", SeriesCount, FillPattern, KeyWords["none"], nil, nil) build("Series", "PatternColor", SeriesCount, FillPatternColor, nil, nil, nil)
build("Series", "Text", SeriesCount, SeriesText, nil, nil, nil)
build("Group", "Text", GroupsCount, GroupText, nil, "Group ", nil)
build("ChartText", "", ChartTextCount, ChartText, nil, nil, nil)
if not checkParms then -- messages, instead of SVG output return table.concat(r, "\n") end
if not outputDebugInfo then -- stop after debug output return table.concat(r, "\n") end
-- build the SVG
prelimsCommon prelimsAxes
commonTop
codeAxisGrids
stylesAreas defsAreas
stylesLines defsMarkers
elementsGraphs
codeAxes
commonBottom
return table.concat(r, "\n")end
function pieChart(frame)
Args = frame.args
getAllParms GroupsCount = 0 -- ensures any GroupNText parameters are ignored
-- fill internal tables
-- only transfer series 1 if SeriesCount > 0 then transfer(Parms["Series" .. 1 .. "Values"], SeriesData, 2) DataPointsCount = DataPointsCount + #SeriesData end copyTable(SeriesData, OriginalData) -- preserve original data
-- get segment parms for each element in the series -- we do this here because transfer (above) is where SeriesMaxLen is calculated for i = 1, SeriesMaxLen do Parms["Segment" .. i .. "Color"] = checkColor(getParm("Segment", i, "Color", "s", iif(Parms["GrayScale"] ~= nil, GrayColor[i], DefColor[i])))
Parms["Segment" .. i .. "Pattern"] = getParm("Segment", i, "Pattern", "n") if Parms["Segment" .. i .. "Pattern"] ~= nil then Parms["Segment" .. i .. "PatternColor"] = checkColor(getParm("Segment", i, "PatternColor", "s", iif(Parms["GrayScale"] ~= nil, "white", "black"))) end end
build("Segment", "Color", SeriesMaxLen, Color, nil, nil, iif(Parms["GrayScale"] ~= nil, GrayColor, DefColor)) build("Segment", "Pattern", SeriesMaxLen, FillPattern, KeyWords["none"], nil, nil) build("Segment", "PatternColor", SeriesMaxLen, FillPatternColor, nil, nil, nil)
build("ChartText", "", ChartTextCount, ChartText, nil, nil, nil)
-- transfer the first value in each SeriesData pair to table SeriesText, for the legend for k, v in ipairs(SeriesData) do SeriesText[k] = v[1] end
SeriesCount = SeriesMaxLen
DoPie = true
if not checkParms then -- messages, instead of SVG output return table.concat(r, "\n") end
if not outputDebugInfo then -- stop after debug output return table.concat(r, "\n") end
-- build the SVG
prelimsCommon prelimsPie
commonTop
stylesAreas defsAreas elementsPie
commonBottom
return table.concat(r, "\n")end
function getAllParms -- get values for all parameters
-- Note that not all parameters have a default value, ie: it is valid for some parameters to be nil. These are generally switches for some behaviour.
-- SVG file metadata
Parms["FileTitle"] = getParm("FileTitle", nil, nil, "s", "SVG Chart") Parms["FileDesc"] = getParm("FileDesc", nil, nil, "s", "SVG chart generated by Charts SVG")
-- general image
Parms["ImagePadding"] = getParm("ImagePadding", nil, nil, "n") -- switch for replacing default value with user setting for the size of the padding for all 4 spaces Parms["ImagePaddingTop"] = getParm("ImagePaddingTop", nil, nil, "n", Parms["ImagePadding"]) -- switch for replacing default value with user setting for the size of the top padding Parms["ImagePaddingBottom"] = getParm("ImagePaddingBottom", nil, nil, "n", Parms["ImagePadding"]) -- switch for replacing default value with user setting for the size of the bottom padding Parms["ImagePaddingLeft"] = getParm("ImagePaddingLeft", nil, nil, "n", Parms["ImagePadding"]) -- switch for replacing default value with user setting for the size of the left padding Parms["ImagePaddingRight"] = getParm("ImagePaddingRight", nil, nil, "n", Parms["ImagePadding"]) -- switch for replacing default value with user setting for the size of the right padding
Parms["ImageBorder"] = checkColor(getParm("ImageBorder", nil, nil, "s", KeyWords["none"]))
Parms["ImageBackgroundColor"] = checkColor(getParm("ImageBackgroundColor", nil, nil, "s", "white"))
Parms["ImageBackgroundSVG"] = getParm("ImageBackgroundSVG", nil, nil, "s") -- switch for background SVG for the image
Parms["ImageForegroundSVG"] = getParm("ImageForegroundSVG", nil, nil, "s") -- switch for foreground SVG for the image
-- display title
Parms["Title"] = getParm("Title", nil, nil, "s") -- switch to show title text Parms["TitleX"] = getParm("TitleX", nil, nil, "n") -- switch to move title from default position Parms["TitleY"] = getParm("TitleY", nil, nil, "n", 0)
-- footnote
Parms["Footnote"] = getParm("Footnote", nil, nil, "s") -- switch for footnote text Parms["FootnoteX"] = getParm("FootnoteX", nil, nil, "n") -- switch to move footnote from default position Parms["FootnoteY"] = getParm("FootnoteY", nil, nil, "n", 0)
-- general chart
Parms["Area"] = getParm("Area", nil, nil, "s") -- switch for area graphs Parms["Stack"] = getParm("Stack", nil, nil, "s") -- switch for stacked graphs Parms["Stack100"] = getParm("Stack100", nil, nil, "s") -- switch for stacked-to-100% graphs
Parms["HorizontalBarGraph"] = getParm("HorizontalBarGraph", nil, nil, "s") -- switch for horizontal bar graphs Parms["GroupsTopDown"] = getParm("GroupsTopDown", nil, nil, "s") -- switch for showing groups (and series) in top-down order on horizontal bar graphs
Parms["ChartWidth"] = getParm("ChartWidth", nil, nil, "n", 500) Parms["ChartHeight"] = getParm("ChartHeight", nil, nil, "n", 350)
if Parms["ChartWidth"] < 200 then table.insert(Msgs, string.format(MessageTexts.BelowChartMinSize, "ChartWidth", Parms["ChartWidth"], 200)) end if Parms["ChartHeight"] < 200 then table.insert(Msgs, string.format(MessageTexts.BelowChartMinSize, "ChartHeight", Parms["ChartHeight"], 200)) end
Parms["ChartAdjust"] = getParm("ChartAdjust", nil, nil, "s") -- switch for automatic chart size adjustment
Parms["GreyScale"] = getParm("GreyScale", nil, nil, "s") Parms["GrayScale"] = getParm("GrayScale", nil, nil, "s", Parms["GreyScale"]) -- switch for grayscale instead of colours
Parms["LineWidth"] = getParm("LineWidth", nil, nil, "n", 100)
Parms["ChartBackgroundColor"] = checkColor(getParm("ChartBackgroundColor", nil, nil, "s")) -- switch for a background color for the chart
-- XAxis
Parms["XMin"] = getParm("XMin", nil, nil, "n", 0) Parms["XMax"] = getParm("XMax", nil, nil, "n", 100)
Parms["XAxisTitle"] = getParm("XAxisTitle", nil, nil, "s") -- switch to show X axis title
Parms["XAxisValueStep"] = getParm("XAxisValueStep", nil, nil, "n", 10)
Parms["XAxisValueMultiplier"] = getParm("XAxisValueMultiplier", nil, nil, "n", 1) Parms["XAxisValueRound"] = getParm("XAxisValueRound", nil, nil, "n", decPlaces(Parms["XAxisValueStep"])) Parms["XAxisValueAbsolute"] = getParm("XAxisValueAbsolute", nil, nil, "s") -- switch for absolute values display
Parms["XAxisValuePrefix"] = getParm("XAxisValuePrefix", nil, nil, "s", "", "_", " ") Parms["XAxisValueSuffix"] = getParm("XAxisValueSuffix", nil, nil, "s", "", "_", " ")
Parms["XAxisValueFormat"] = getParm("XAxisValueFormat", nil, nil, "s") -- switch to force values formatting on or off Parms["XAxisValueRotate"] = getParm("XAxisValueRotate", nil, nil, "n") -- switch to rotate x-axis values
Parms["XAxisValueSpace"] = getParm("XAxisValueSpace", nil, nil, "n") -- switch to set x-axis values space
Parms["XAxisMark2Step"] = getParm("XAxisMark2Step", nil, nil, "n") -- switch for showing x-axis secondary marks
Parms["XAxisArrows"] = getParm("XAxisArrows", nil, nil, "s") -- switch for showing x-axis arrows
-- YAxis
if Parms["Stack100"] ~= nil then Parms["YMin"] = 0 Parms["YMax"] = 100 else Parms["YMin"] = getParm("YMin", nil, nil, "n", 0) Parms["YMax"] = getParm("YMax", nil, nil, "n", 100) end
Parms["YAxisTitle"] = getParm("YAxisTitle", nil, nil, "s") -- switch to show Y axis title
Parms["YAxisValueStep"] = getParm("YAxisValueStep", nil, nil, "n", 10)
Parms["YAxisValueMultiplier"] = getParm("YAxisValueMultiplier", nil, nil, "n", 1) Parms["YAxisValueRound"] = getParm("YAxisValueRound", nil, nil, "n", decPlaces(Parms["YAxisValueStep"])) Parms["YAxisValueAbsolute"] = getParm("YAxisValueAbsolute", nil, nil, "s") -- switch for absolute values display
Parms["YAxisValuePrefix"] = getParm("YAxisValuePrefix", nil, nil, "s", "", "_", " ") if Parms["Stack100"] ~= nil then Parms["YAxisValueSuffix"] = getParm("YAxisValueSuffix", nil, nil, "s", "%", "_", " ") else Parms["YAxisValueSuffix"] = getParm("YAxisValueSuffix", nil, nil, "s", "", "_", " ") end
Parms["YAxisValueFormat"] = getParm("YAxisValueFormat", nil, nil, "s") -- switch to force values formatting on or off Parms["YAxisValueRotate"] = getParm("YAxisValueRotate", nil, nil, "n") -- switch to rotate y-axis values
Parms["YAxisValueSpace"] = getParm("YAxisValueSpace", nil, nil, "n") -- switch to set y-axis values space
Parms["YAxisMark2Step"] = getParm("YAxisMark2Step", nil, nil, "n") -- switch for showing y-axis secondary marks
Parms["YAxisColor"] = checkColor(getParm("YAxisColor", nil, nil, "s", "black"))
Parms["YAxisArrows"] = getParm("YAxisArrows", nil, nil, "s") -- switch for showing y-axis arrows
-- YAxis2
Parms["YAxis2Title"] = getParm("YAxis2Title", nil, nil, "s") -- switch to show y-axis-2 title
Parms["Y2Min"] = getParm("Y2Min", nil, nil, "n", 0) Parms["Y2Max"] = getParm("Y2Max", nil, nil, "n") -- switch to show y-axis-2
Parms["YAxis2ValueStep"] = getParm("YAxis2ValueStep", nil, nil, "n", 10)
Parms["YAxis2ValueMultiplier"] = getParm("YAxis2ValueMultiplier", nil, nil, "n", 1) Parms["YAxis2ValueRound"] = getParm("YAxis2ValueRound", nil, nil, "n", decPlaces(Parms["YAxis2ValueStep"])) Parms["YAxis2ValueAbsolute"] = getParm("YAxis2ValueAbsolute", nil, nil, "s") -- switch for absolute values display
Parms["YAxis2ValuePrefix"] = getParm("YAxis2ValuePrefix", nil, nil, "s", "", "_", " ") Parms["YAxis2ValueSuffix"] = getParm("YAxis2ValueSuffix", nil, nil, "s", "", "_", " ")
Parms["YAxis2ValueFormat"] = getParm("YAxis2ValueFormat", nil, nil, "s") -- switch to force values formatting on or off Parms["YAxis2ValueRotate"] = getParm("YAxis2ValueRotate", nil, nil, "n") -- switch to rotate y-axis-2 values
Parms["YAxis2ValueSpace"] = getParm("YAxis2ValueSpace", nil, nil, "n") -- switch to set y-axis-2 values space
Parms["YAxis2Mark2Step"] = getParm("YAxis2Mark2Step", nil, nil, "n") -- switch for showing y-axis-2 secondary marks
Parms["YAxis2Color"] = checkColor(getParm("YAxis2Color", nil, nil, "s", "black"))
-- grid lines
Parms["XGrid"] = getParm("XGrid", nil, nil, "s", Parms["XAxisValueStep"]) if Parms["XGrid"] ~= KeyWords["none"] then Parms["XGrid"] = tonumber(Parms["XGrid"]) end
Parms["YGrid"] = getParm("YGrid", nil, nil, "s", Parms["YAxisValueStep"]) if Parms["YGrid"] ~= KeyWords["none"] then Parms["YGrid"] = tonumber(Parms["YGrid"]) end
-- legend
Parms["LegendType"] = getParm("LegendType", nil, nil, "s", KeyWords["vertical"]) i = 1 Parms["Series" .. i .. "Text"] = getParm("Series", i, "Text", "s") -- switch for showing a legend for series N while Parms["Series" .. i .. "Text"] ~= nil do i = i + 1 Parms["Series" .. i .. "Text"] = getParm("Series", i, "Text", "s") -- switch for showing a legend for series N end
Parms["LegendX"] = getParm("LegendX", nil, nil, "n") -- switch for moving the legend from the default position Parms["LegendY"] = getParm("LegendY", nil, nil, "n", 0) Parms["LegendTextWidth"] = getParm("LegendTextWidth", nil, nil, "n", 100) Parms["LegendBorder"] = checkColor(getParm("LegendBorder", nil, nil, "s", "black")) Parms["LegendSVG"] = getParm("LegendSVG", nil, nil, "s") -- switch for replacing all legend code
-- font sizes
Parms["FontSize"] = getParm("FontSize", nil, nil, "n", 100) Parms["TitleFontSize"] = getParm("TitleFontSize", nil, nil, "n", Parms["FontSize"]) Parms["FootnoteFontSize"] = getParm("FootnoteFontSize", nil, nil, "n", Parms["FontSize"]) Parms["LegendFontSize"] = getParm("LegendFontSize", nil, nil, "n", Parms["FontSize"]) Parms["XAxisTitleFontSize"] = getParm("XAxisTitleFontSize", nil, nil, "n", Parms["FontSize"]) Parms["YAxisTitleFontSize"] = getParm("YAxisTitleFontSize", nil, nil, "n", Parms["FontSize"]) Parms["YAxis2TitleFontSize"] = getParm("YAxis2TitleFontSize", nil, nil, "n", Parms["FontSize"]) Parms["XAxisValuesFontSize"] = getParm("XAxisValuesFontSize", nil, nil, "n", Parms["FontSize"]) Parms["YAxisValuesFontSize"] = getParm("YAxisValuesFontSize", nil, nil, "n", Parms["FontSize"]) Parms["YAxis2ValuesFontSize"] = getParm("YAxis2ValuesFontSize", nil, nil, "n", Parms["FontSize"]) Parms["ChartTextFontSize"] = getParm("ChartTextFontSize", nil, nil, "n", Parms["FontSize"]) Parms["LabelsFontSize"] = getParm("LabelsFontSize", nil, nil, "n", Parms["FontSize"])
-- bar width & spacing
Parms["BarWidth"] = getParm("BarWidth", nil, nil, "n", 20) Parms["BarSpace"] = getParm("BarSpace", nil, nil, "n", 0) -- % of bar width
-- general graph line width Parms["GraphLineWidth"] = getParm("GraphLineWidth", nil, nil, "n", 100)
-- pie chart
Parms["PieRadius"] = getParm("PieRadius", nil, nil, "n", 200) Parms["Explode"] = getParm("Explode", nil, nil, "s") -- switch for exploding some or all pie segments if Parms["Explode"] ~= nil then if tonumber(Parms["Explode"]) ~= nil then Parms["Explode"] = tonumber(Parms["Explode"]) -- default explode radius is 20% Parms["ExplodeRadius"] = getParm("ExplodeRadius", nil, nil, "n", 20) else -- any non-numeric value = all, default explode radius is 10% Parms["ExplodeRadius"] = getParm("ExplodeRadius", nil, nil, "n", 10) end end -- Parms[] SegmentNColor, SegmentNPattern and SegmentNPatternColor are done in pieChart
Parms["SegmentText"] = getParm("SegmentText", nil, nil, "s") -- switch for showing series text and/or values on pie segments Parms["SegmentTextWidth"] = getParm("SegmentTextWidth", nil, nil, "n", 100) Parms["SegmentTextRadius"] = getParm("SegmentTextRadius", nil, nil, "n", 105)
Parms["DoughnutHole"] = getParm("DoughnutHole", nil, nil, "s") -- switch for a doughnut chart if Parms["DoughnutHole"] ~= nil then if tonumber(Parms["DoughnutHole"]) ~= nil then -- if DoughnutHole is a number, ensure it is of numeric type Parms["DoughnutHole"] = tonumber(Parms["DoughnutHole"]) else -- any other value, hole size is 50% Parms["DoughnutHole"] = 50 end end Parms["PieStartAngle"] = getParm("PieStartAngle", nil, nil, "n", 0) -- move the start angle
Parms["PieSweepDir"] = getParm("PieSweepDir", nil, nil, "s", "AntiClockwise") -- any value not "AntiClockwise" will switch the sweep direction
-- bar and pie-segment borders
Parms["BorderColor"] = checkColor(getParm("BorderColor", nil, nil, "s")) -- switch for showing borders on bars Parms["BorderWidth"] = getParm("BorderWidth", nil, nil, "n", 100) -- % of standard line width
-- series
i = 1 Parms["Series" .. i .. "Values"] = getParm("Series", i, "Values", "s") -- switch for showing a series while Parms["Series" .. i .. "Values"] ~= nil do Parms["Series" .. i .. "Type"] = getParm("Series", i, "Type", "s") Parms["Series" .. i .. "YAxis2"] = getParm("Series", i, "YAxis2") -- switch for series to use YAxis2 as scale for Y values Parms["Series" .. i .. "Labels"] = getParm("Series", i, "Labels") -- switch for series to show data labels Parms["Series" .. i .. "Color"] = checkColor(getParm("Series", i, "Color", "s", iif(Parms["GrayScale"] ~= nil, GrayColor[i], DefColor[i])))
Parms["Series" .. i .. "Line"] = getParm("Series", i, "Line", "s") Parms["Series" .. i .. "Width"] = getParm("Series", i, "Width", "n", Parms["GraphLineWidth"]) Parms["Series" .. i .. "Dash"] = getParm("Series", i, "Dash", "s", KeyWords["none"]) t = tonumber(Parms["Series" .. i .. "Dash"]) if t ~= nil and t <= #DashPattern then -- if SeriesNDash is a number in the table of default dashes, use the dash pattern for that number Parms["Series" .. i .. "Dash"] = DashPattern[t] end
Parms["Series" .. i .. "Marker"] = getParm("Series", i, "Marker", "s") -- switch for markers on the graph if tonumber(Parms["Series" .. i .. "Marker"]) ~= nil then -- if SeriesNMarker is a number, ensure it is of numeric type Parms["Series" .. i .. "Marker"] = tonumber(Parms["Series" .. i .. "Marker"]) end Parms["Series" .. i .. "MarkerSize"] = getParm("Series", i, "MarkerSize", "n", 100) Parms["Series" .. i .. "MarkerFill"] = checkColor(getParm("Series", i, "MarkerFill", "s"))
Parms["Series" .. i .. "Pattern"] = getParm("Series", i, "Pattern", "n") if Parms["Series" .. i .. "Pattern"] ~= nil then Parms["Series" .. i .. "PatternColor"] = checkColor(getParm("Series", i, "PatternColor", "s", iif(Parms["GrayScale"] ~= nil, "white", "black"))) end SeriesCount = SeriesCount + 1
i = i + 1 Parms["Series" .. i .. "Values"] = getParm("Series", i, "Values", "s") -- switch for showing a series end
-- groups
i = 1 Parms["Group" .. i .. "Text"] = getParm("Group", i, "Text", "s") -- switch for grouping values while Parms["Group" .. i .. "Text"] ~= nil do GroupsCount = GroupsCount + 1
i = i + 1 Parms["Group" .. i .. "Text"] = getParm("Group", i, "Text", "s") end if GroupsCount > 0 then Parms["XMax"] = GroupsCount end -- chart texts
i = 1 Parms["ChartText" .. i] = getParm("ChartText", i, nil, "s") -- switch for showing chart text N while Parms["ChartText" .. i] ~= nil do Parms["ChartText" .. i .. "X"] = getParm("ChartText", i, "X", "n", 0) Parms["ChartText" .. i .. "Y"] = getParm("ChartText", i, "Y", "n", 0) ChartTextCount = ChartTextCount + 1
i = i + 1 Parms["ChartText" .. i] = getParm("ChartText", i, nil, "s") -- switch for showing chart text N end
Parms["IncludeOriginalData"] = getParm("IncludeOriginalData", nil, nil, "s", KeyWords["auto"])
-- debug
Parms["Debug"] = getParm("Debug", nil, nil, "s") -- switch to run debug code
end
function getParm(s1, num, s2, typ, def, subst, with) -- returns the value of a named parameter (of a specified type) or a default -- the name may be built from multiple parts (s1, num, s2)
local s = KeyWords[s1] if num ~= nil then s = s .. num end if s2 ~= nil then s = s .. KeyWords[s2] end
local result if Args[s] ~= nil then result = Args[s] if typ
nil then table.insert(Msgs, string.format(MessageTexts.NotNumeric, s, Args[s])) end end else result = def end -- optional substitution of characters within the parameter value if result ~= nil and subst and with then result = mw.ustring.gsub(result, subst, with) end return resultend
function checkColor(p) -- checks if p is numeric and in the colors table(s) -- if so returns the color from the table -- otherwise returns the original parameter value
if p
function transfer(Parm, Tab, SetSize) -- transfer values from a space-delimited parameter to a table local i = 0 Parm = mw.text.trim(Parm) if SetSize > 1 then local x = SetSize + 1 -- to force new table for first time for s in mw.text.gsplit(Parm, "%s+") do if x > SetSize then i = i + 1 Tab[i] = x = 1 end Tab[i][x] = s x = x + 1 end else for s in mw.text.gsplit(Parm, "%s+") do i = i + 1 Tab[i] = s end end SeriesMaxLen = math.max(SeriesMaxLen, i)end
function build(ParmStart, ParmEnd, Size, Tab, DefaultValue, UseI, DefaultTable) -- builds table of specified size from a series of StartNEnd parameters -- any nil parameters in the sequence may be filled with a default value, or i (with a prefix if set), or a value from a default table for i = 1, Size do if Parms[ParmStart .. i .. ParmEnd] ~= nil then Tab[i] = Parms[ParmStart .. i .. ParmEnd] elseif DefaultValue ~= nil then Tab[i] = DefaultValue elseif type(UseI)
"string" then Tab[i] = UseI .. i elseif DefaultTable ~= nil then Tab[i] = DefaultTable[i] else -- nothing end endend
function calcStack -- calculate stacked totals for series
-- the current accumlated total overwrites the SeriesData Y value
local PosTotal, NegTotal, NegFlag =,, false
for j = 1, #GroupText do PosTotal[j] = 0 NegTotal[j] = 0 for i = 1, #SeriesData do if SeriesData[i][j] ~= nil and tonumber(SeriesData[i][j][1])
function checkParms -- check for parameter issues
-- unknown parameters local regexps =
-- add patterns to regexps table.insert(regexps, "Series[%d][%d]*Values") table.insert(regexps, "Series[%d][%d]*Type") table.insert(regexps, "Series[%d][%d]*YAxis2") table.insert(regexps, "Series[%d][%d]*Labels") table.insert(regexps, "Series[%d][%d]*Color") table.insert(regexps, "Series[%d][%d]*Line") table.insert(regexps, "Series[%d][%d]*Width") table.insert(regexps, "Series[%d][%d]*Dash") table.insert(regexps, "Series[%d][%d]*Marker") table.insert(regexps, "Series[%d][%d]*MarkerSize") table.insert(regexps, "Series[%d][%d]*MarkerFill") table.insert(regexps, "Series[%d][%d]*Pattern") table.insert(regexps, "Series[%d][%d]*PatternColor") table.insert(regexps, "Series[%d][%d]*Text")
table.insert(regexps, "Group[%d][%d]*Text")
table.insert(regexps, "Segment[%d][%d]*Color") table.insert(regexps, "Segment[%d][%d]*Pattern") table.insert(regexps, "Segment[%d][%d]*PatternColor")
table.insert(regexps, "ChartText[%d][%d]*") table.insert(regexps, "ChartText[%d][%d]*X") table.insert(regexps, "ChartText[%d][%d]*Y")
local unknowns = unknownParameters(KeyWords, regexps) if #unknowns > 0 then -- ensure parms-not-found are at the top of the list of messages local tmp = copyTable(Msgs, tmp) Msgs = table.insert(Msgs, MessageTexts.ParmsNotFound) for k, v in spairs(unknowns) do table.insert(Msgs, " " .. unknowns[k]) end for i = 1, #tmp do table.insert(Msgs, tmp[i]) end end
if #GroupText > 0 then for k1, v1 in ipairs(SeriesData) do for k2, v2 in ipairs(v1) do local t = tonumber(v2[1]) if t ~= math.floor(t) or t < 1 or t > #GroupText then table.insert(Msgs, string.format(MessageTexts.NotInGroup, k1, k2, v2[1])) end end end end if not DoYAxis2 then for k, v in pairs(YAxis2) do table.insert(Msgs, string.format(MessageTexts.NoYAxis2, k)) end end
for k, v in pairs(Marker) do local t = tonumber(v) if v
nil then table.insert(Msgs, string.format(MessageTexts.NotInMarkers, k, v)) elseif (t >= 1 and t <= 7) then -- OK else table.insert(Msgs, string.format(MessageTexts.NotInMarkers, k, v)) end end
for k, v in pairs(LineDash) do local t = tonumber(v) if v
nil then -- non-numerics are OK for LineDash elseif (t >= 1 and t <= #DashPattern) then -- OK else table.insert(Msgs, string.format(MessageTexts.NotInDashPatterns, k, v)) end end
for k, v in pairs(FillPattern) do local t = tonumber(v) if v
nil then table.insert(Msgs, string.format(MessageTexts.NotInFillPatterns, k, v)) elseif (t >= 1 and t <= 8) or (t >= 11 and t <= 18) or (t >= 21 and t <= 24) or (t >= 31 and t <= 34) or (t >= 41 and t <= 49) or (t >= 51 and t <= 56) or (t >= 61 and t <= 64) then -- OK else table.insert(Msgs, string.format(MessageTexts.NotInFillPatterns, k, v)) end end
if #Msgs > 0 then -- add messages to output table.insert(r, " " .. MessageTexts.MsgHeading) for i = 1, math.min(#Msgs, 10) do table.insert(r, " " .. Msgs[i]) end return false end
return trueend
function prelimsCommon
Siz.ImagePadding = Siz.Legend = Siz.Text =
-- the base unit for font sizes can be changed by the user BaseFontSize = BaseFontSize * (Parms["FontSize"] / 100)
FontSiz.Title = 7 * BaseFontSize * Parms["TitleFontSize"] / 100
FontSiz.LegendText = 5 * BaseFontSize * Parms["LegendFontSize"] / 100
FontSiz.Labels = 4 * BaseFontSize * Parms["LabelsFontSize"] / 100
FontSiz.ChartText = 4 * BaseFontSize * Parms["ChartTextFontSize"] / 100
FontSiz.Footnote = 3 * BaseFontSize * Parms["FootnoteFontSize"] / 100
-- the base line width can be changed by the user BaseLineWidth = BaseLineWidth * Parms["LineWidth"] / 100
-- define sizes of other image components from base spacing units
Siz.ImagePadding.Top = 2 * BaseUnit if Parms["ImagePaddingTop"] ~= nil then Siz.ImagePadding.Top = Parms["ImagePaddingTop"] end Siz.ImagePadding.Bottom = 2 * BaseUnit if Parms["ImagePaddingBottom"] ~= nil then Siz.ImagePadding.Bottom = Parms["ImagePaddingBottom"] end Siz.ImagePadding.Left = 2 * BaseUnit if Parms["ImagePaddingLeft"] ~= nil then Siz.ImagePadding.Left = Parms["ImagePaddingLeft"] end Siz.ImagePadding.Right = 2 * BaseUnit if Parms["ImagePaddingRight"] ~= nil then Siz.ImagePadding.Right = Parms["ImagePaddingRight"] end
Siz.ChartMargin = 2 * BaseUnit
Siz.Text.Interline = 2 * BaseFontSize
Siz.Text.Labels = FontSiz.Labels
Siz.Text.Title = iif(Parms["Title"] ~= nil and Parms["TitleX"]
Siz.Footnote = iif(Parms["Footnote"] ~= nil and Parms["FootnoteX"]
Siz.Legend.Text = FontSiz.LegendText Siz.Legend.TextWidth = 5 * FontSiz.LegendText * Parms["LegendTextWidth"] / 100 Siz.Legend.Offset = 3 * BaseUnit
Siz.Text.Chart = FontSiz.ChartTextend
function prelimsAxes
Pos.XAxis = Pos.YAxis = Pos.YAxis2 = Pos.Origin =
Siz.Space = Siz.XAxis = Siz.YAxis = Siz.YAxis2 =
Siz.ChartWidth = Parms["ChartWidth"] Siz.ChartHeight = Parms["ChartHeight"] if DoChartAdjust then allChartAdjust end if DoHorizontal then Siz.XAxis.Length = Siz.ChartHeight Siz.YAxis.Length = Siz.ChartWidth Siz.YAxis2.Length = Siz.ChartWidth else Siz.XAxis.Length = Siz.ChartWidth Siz.YAxis.Length = Siz.ChartHeight Siz.YAxis2.Length = Siz.ChartHeight end
prelimsLegend
BarWidth = Parms["BarWidth"] BarSpace = Parms["BarSpace"] / 100
-- The Mult values are the number of pixels per 1 unit on each axis
if #GroupText > 0 then if BarSeriesCount > 0 then GroupWidth = Siz.XAxis.Length / #GroupText if DoStack or DoStack100 then UnitWidth = GroupWidth / 2 else UnitWidth = GroupWidth / (BarSeriesCount + 1) end BarWidth = UnitWidth / (1 + BarSpace) BarSpace = UnitWidth - BarWidth Mult.x = Siz.XAxis.Length / #GroupText else GroupWidth = Siz.XAxis.Length / #GroupText Mult.x = GroupWidth end else Mult.x = Siz.XAxis.Length / (math.abs(Parms["XMax"] - Parms["XMin"])) end
Mult.y = Siz.YAxis.Length / (math.abs(Parms["YMax"] - Parms["YMin"]))
if DoYAxis2 then Mult.y2 = Siz.YAxis2.Length / (math.abs(Parms["Y2Max"] - Parms["Y2Min"])) else Mult.y2 = 1 end --
FontSiz.XAxisTitle = 5 * BaseFontSize * Parms["XAxisTitleFontSize"] / 100 FontSiz.XAxisValues = 4 * BaseFontSize * Parms["XAxisValuesFontSize"] / 100
FontSiz.YAxisTitle = 5 * BaseFontSize * Parms["YAxisTitleFontSize"] / 100 FontSiz.YAxisValues = 4 * BaseFontSize * Parms["YAxisValuesFontSize"] / 100
FontSiz.YAxis2Title = 5 * BaseFontSize * Parms["YAxis2TitleFontSize"] / 100 FontSiz.YAxis2Values = 4 * BaseFontSize * Parms["YAxis2ValuesFontSize"] / 100
Siz.AxisMark = 2 * BaseUnit Siz.AxisMark2 = 1 * BaseUnit
Siz.XAxis.Title = iif(Parms["XAxisTitle"] ~= nil, FontSiz.XAxisTitle + Siz.Text.Interline, 0) Siz.YAxis.Title = iif(Parms["YAxisTitle"] ~= nil, FontSiz.YAxisTitle + Siz.Text.Interline, 0)
if DoHorizontal then Siz.XAxis.Values = iif(Parms["XAxisValueSpace"] ~= nil, Parms["XAxisValueSpace"], 3 * FontSiz.XAxisValues) Siz.YAxis.Values = iif(Parms["YAxisValueSpace"] ~= nil, Parms["YAxisValueSpace"], FontSiz.YAxisValues + Siz.Text.Interline) else Siz.XAxis.Values = iif(Parms["XAxisValueSpace"] ~= nil, Parms["XAxisValueSpace"], FontSiz.XAxisValues + Siz.Text.Interline) Siz.YAxis.Values = iif(Parms["YAxisValueSpace"] ~= nil, Parms["YAxisValueSpace"], 3 * FontSiz.YAxisValues) end
Siz.YAxis2.Title = 0 Siz.YAxis2.Values = 0 if DoYAxis2 then if Parms["YAxis2Title"] ~= nil then Siz.YAxis2.Title = FontSiz.YAxis2Title + Siz.Text.Interline end if DoHorizontal then Siz.YAxis2.Values = iif(Parms["YAxis2ValueSpace"] ~= nil, Parms["YAxis2ValueSpace"], FontSiz.YAxis2Values + Siz.Text.Interline) else Siz.YAxis2.Values = iif(Parms["YAxis2ValueSpace"] ~= nil, Parms["YAxis2ValueSpace"], 3 * FontSiz.YAxis2Values) end end -- spaces around the chart (working out from the chart): -- AxisMarks -- ChartMargin -- AxisValues -- AxisTitle -- Title -- Legend -- Footnote -- ImagePadding -- Top Space Siz.Space.Top = Siz.ImagePadding.Top Pos.Title = Siz.ImagePadding.Top + FontSiz.Title Siz.Space.Top = Siz.Space.Top + Siz.Text.Title if DoHorizontal and DoYAxis2 then Pos.YAxis2.Title = Siz.Space.Top + FontSiz.YAxis2Title Siz.Space.Top = Siz.Space.Top + Siz.YAxis2.Title Siz.Space.Top = Siz.Space.Top + Siz.YAxis2.Values Siz.Space.Top = Siz.Space.Top + Siz.ChartMargin + Siz.AxisMark
Pos.YAxis2.Line = -Parms["XMax"] * Mult.x Pos.YAxis2.Values = Pos.YAxis2.Line - Siz.AxisMark - Siz.ChartMargin -- pos is bottom edge of values box else Siz.Space.Top = Siz.Space.Top + Siz.ChartMargin end
-- Bottom Space if DoHorizontal then if Parms["XMin"] < 0 and #GroupText
KeyWords["horizontal"] and Parms["LegendX"]
-- Left Space Siz.Space.Left = Siz.ImagePadding.Left if DoHorizontal then if Parms["XAxisTitle"] ~= nil then Pos.XAxis.Title = Siz.Space.Left + FontSiz.XAxisTitle -- pos is right edge of title Siz.Space.Left = Siz.Space.Left + Siz.XAxis.Title end if Parms["YMin"] < 0 then -- X axis line is within chart Siz.Space.Left = Siz.Space.Left + Siz.ChartMargin Pos.XAxis.Line = 0 Pos.XAxis.Values = Pos.XAxis.Line - Siz.AxisMark - Siz.ChartMargin -- pos is right edge of values box else -- X axis line is at left of chart Siz.Space.Left = Siz.Space.Left + Siz.XAxis.Values + Siz.ChartMargin + Siz.AxisMark Pos.XAxis.Line = Parms["YMin"] * Mult.y Pos.XAxis.Values = Pos.XAxis.Line - Siz.AxisMark - Siz.ChartMargin -- pos is right edge of values box end else if Parms["YAxisTitle"] ~= nil then Pos.YAxis.Title = Siz.Space.Left + FontSiz.YAxisTitle -- pos is right edge of title Siz.Space.Left = Siz.Space.Left + Siz.YAxis.Title end if Parms["XMin"] < 0 and #GroupText
-- Right Space Siz.Space.Right = 0 if DoHorizontal then Siz.Space.Right = Siz.Space.Right + Siz.ChartMargin else if DoYAxis2 then Pos.YAxis2.Line = Parms["XMax"] * Mult.x Siz.Space.Right = Siz.Space.Right + Siz.AxisMark Pos.YAxis2.Values = Pos.YAxis2.Line + Siz.Space.Right + Siz.ChartMargin -- pos is left edge of values box Siz.Space.Right = Siz.Space.Right + Siz.ChartMargin + Siz.YAxis2.Values if Parms["YAxis2Title"] ~= nil then Pos.YAxis2.Title = Siz.Space.Left + Siz.ChartWidth + Siz.Space.Right + FontSiz.YAxis2Title Siz.Space.Right = Siz.Space.Right + Siz.YAxis2.Title end else Siz.Space.Right = Siz.Space.Right + Siz.ChartMargin end end if Parms["LegendType"]
nil then Pos.Legend = Siz.Space.Left + Siz.ChartWidth + Siz.Space.Right + Siz.Legend.Offset Siz.Space.Right = Siz.Space.Right + Siz.Legend.Offset + Siz.Legend.Width end Siz.Space.Right = Siz.Space.Right + Siz.ImagePadding.Right if DoHorizontal then Pos.XAxis.Zero = Siz.Space.Top + Siz.ChartHeight + (iif(#GroupText
0, Parms["XMin"], 0) * Mult.x) Pos.YAxis.Zero = Siz.Space.Top + Siz.ChartHeight + (Parms["YMin"] * Mult.y) end
-- Chart Origin offsets if DoHorizontal then Pos.Origin.v = round(Siz.Space.Top + (Parms["XMax"] * Mult.x), 2) Pos.Origin.h = round(Siz.Space.Left + ((0 - Parms["YMin"]) * Mult.y), 2) else Pos.Origin.v = round(Siz.Space.Top + (Parms["YMax"] * Mult.y), 2) Pos.Origin.h = round(Siz.Space.Left + ((0 - Parms["XMin"]) * Mult.x), 2) end -- Image size ImageWidth = round(Siz.Space.Left + Siz.ChartWidth + Siz.Space.Right, 0) ImageHeight = round(Siz.Space.Top + Siz.ChartHeight + Siz.Space.Bottom, 0)end
function allChartAdjust
Adjusts.ChartWidth = Adjusts.ChartWidth.Old = Siz.ChartWidth Adjusts.ChartHeight = Adjusts.ChartHeight.Old = Siz.ChartHeight Adjusts.XMax = Adjusts.XMax.Old = Parms["XMax"] Adjusts.XMin = Adjusts.XMin.Old = Parms["XMin"] Adjusts.YMax = Adjusts.YMax.Old = Parms["YMax"] Adjusts.YMin = Adjusts.YMin.Old = Parms["YMin"] if DoHorizontal then Siz.ChartWidth = chartAdjust(Siz.ChartWidth, "YAxisValueStep", "YAxisMark2Step", "YMax", "YMin") if #GroupText
0 then Siz.ChartWidth = chartAdjust(Siz.ChartWidth, "XAxisValueStep", "XAxisMark2Step", "XMax", "XMin") end Siz.ChartHeight = chartAdjust(Siz.ChartHeight, "YAxisValueStep", "YAxisMark2Step", "YMax", "YMin") end Adjusts.ChartWidth.New = Siz.ChartWidth Adjusts.ChartHeight.New = Siz.ChartHeight Adjusts.XMax.New = Parms["XMax"] Adjusts.XMin.New = Parms["XMin"] Adjusts.YMax.New = Parms["YMax"] Adjusts.YMin.New = Parms["YMin"]end
function chartAdjust(userlength, mark, mark2, max, min)
local unit = Parms[mark] if Parms[mark2] ~= nil then unit = Parms[mark2] end Parms[max] = round((Parms[max] / unit) + 0.49, 0) * unit Parms[min] = round((Parms[min] / unit) - 0.49, 0) * unit local count = (Parms[max] - Parms[min]) / unit return round(userlength / count, 0) * countend function outputAdjusts
table.insert(r, " <!-- Adjustments made by Charts SVG:") table.insert(r, " ChartWidth: " .. Adjusts.ChartWidth.Old .. " : " .. Adjusts.ChartWidth.New) table.insert(r, " ChartHeight: " .. Adjusts.ChartHeight.Old .. " : " .. Adjusts.ChartHeight.New) table.insert(r, " XMax: " .. Adjusts.XMax.Old .. " : " .. Adjusts.XMax.New) table.insert(r, " XMin: " .. Adjusts.XMin.Old .. " : " .. Adjusts.XMin.New) table.insert(r, " YMax: " .. Adjusts.YMax.Old .. " : " .. Adjusts.YMax.New) table.insert(r, " YMin: " .. Adjusts.YMin.Old .. " : " .. Adjusts.YMin.New) table.insert(r, " -->") table.insert(r, " ")end
function prelimsPie
Siz.Space =
-- pie chart radius & origin PieRadius = Parms["PieRadius"] -- not local PieOriginX, PieOriginY = PieRadius, PieRadius -- not local
if Parms["Explode"] ~= nil then PieOriginX = PieOriginX + (PieRadius * Parms["ExplodeRadius"] / 100) PieOriginY = PieOriginY + (PieRadius * Parms["ExplodeRadius"] / 100) end if Parms["SegmentText"] ~= nil then -- possibly add for segment texts outside the pie local TextSpaceX = (PieRadius * Parms["SegmentTextRadius"] / 100) + (5 * Siz.Text.Chart * Parms["SegmentTextWidth"] / 100) if Parms["Explode"] ~= nil then TextSpaceX = TextSpaceX + (PieRadius * Parms["ExplodeRadius"] / 100) end PieOriginX = math.max(PieOriginX, TextSpaceX)
local TextSpaceY = (PieRadius * Parms["SegmentTextRadius"] / 100) + (Siz.Text.Chart) if Parms["Explode"] ~= nil then TextSpaceY = TextSpaceY + (PieRadius * Parms["ExplodeRadius"] / 100) end PieOriginY = math.max(PieOriginY, TextSpaceY) end
-- for pie charts, chart size is calculated, not user-defined Siz.ChartWidth = PieOriginX * 2 Siz.ChartHeight = PieOriginY * 2
prelimsLegend
-- Top Space Siz.Space.Top = Siz.ImagePadding.Top + Siz.Text.Title + Siz.ChartMargin Pos.Title = Siz.ImagePadding.Top + FontSiz.Title
-- Bottom Space Siz.Space.Bottom = Siz.ChartMargin
if Parms["LegendType"]
nil then Pos.Legend = Siz.Space.Top + Siz.ChartHeight + Siz.Space.Bottom + Siz.Legend.Offset Siz.Space.Bottom = Siz.Space.Bottom + Siz.Legend.Offset + Siz.Legend.Height + Siz.Text.Interline end if Parms["Footnote"] ~= nil then Pos.Footnote = Siz.Space.Top + Siz.ChartHeight + Siz.Space.Bottom + Siz.Footnote Siz.Space.Bottom = Siz.Space.Bottom + Siz.Footnote end Siz.Space.Bottom = Siz.Space.Bottom + Siz.ImagePadding.Bottom
-- Left Space Siz.Space.Left = Siz.ImagePadding.Left + Siz.ChartMargin
-- Right Space Siz.Space.Right = Siz.ChartMargin if Parms["LegendType"]
nil then Pos.Legend = Siz.Space.Left + Siz.ChartWidth + Siz.Space.Right + Siz.Legend.Offset Siz.Space.Right = Siz.Space.Right + Siz.Legend.Offset + Siz.Legend.Width end Siz.Space.Right = Siz.Space.Right + Siz.ImagePadding.Right
-- Image size
ImageWidth = round(Siz.Space.Left + Siz.ChartWidth + Siz.Space.Right, 0) ImageHeight = round(Siz.Space.Top + Siz.ChartHeight + Siz.Space.Bottom, 0)
-- Mult.x, Mult.y and Pos.Axis.Zero are needed for positioning of Legends and Title, Footnote and Chart texts
Mult.x = Siz.ChartWidth / 100 Mult.y = Siz.ChartHeight / 100 Pos.XAxis = Pos.YAxis = Pos.XAxis.Zero = Siz.Space.Left Pos.YAxis.Zero = Siz.Space.Top + Siz.ChartHeightend
function prelimsLegend -- legend height and width
Siz.Legend.ElementWidth = Siz.ChartMargin + (2 * Siz.Legend.Text) + Siz.ChartMargin + Siz.Legend.TextWidth Siz.Legend.ElementHeight = Siz.Legend.Text + Siz.Text.Interline LegendElementsInWidth = 1 -- not local if #SeriesText < 1 then Parms["LegendType"] = KeyWords["none"] elseif Parms["LegendType"]
Siz.Legend.Width = (LegendElementsInWidth * Siz.Legend.ElementWidth) + Siz.ChartMargin Siz.Legend.Height = Siz.ChartMargin + (LegendLines * Siz.Legend.ElementHeight) + Siz.ChartMargin else Siz.Legend.Width = Siz.Legend.ElementWidth + Siz.ChartMargin Siz.Legend.Height = Siz.ChartMargin + (#SeriesText * Siz.Legend.ElementHeight) + Siz.ChartMargin endend
function commonTop
-- output header
table.insert(r, MessageTexts.CopyText) table.insert(r, " ")
-- SVG header stuff
table.insert(r, " ") table.insert(r, " <!-- Generator: commons.wikipedia.org/wiki/" .. mw.getCurrentFrame:getTitle .. " -->") table.insert(r, " <!-- Generator Version: 3.0 -->") table.insert(r, "
table.insert(r, "
local DT = os.date("*t") -- returns a table with the current date & time
table.insert(r, "
if not DoPie and DoChartAdjust then outputAdjusts end
table.insert(r, " <!--
-->") table.insert(r, " ")
-- image background rectangle table.insert(r, " <!-- image background -->") table.insert(r, "
if Parms["ImageBackgroundSVG"] ~= nil then table.insert(r, " <!-- Image Background SVG -->") table.insert(r, " " .. Parms["ImageBackgroundSVG"] .. "") table.insert(r, " ") end
-- chart background
if Parms["ChartBackgroundColor"] ~= nil then table.insert(r, " <!-- chart background -->") table.insert(r, "
function codeAxisGrids
table.insert(r, " <!--
-->") table.insert(r, "
-- the 'horizontal' grid is on the horizontal axis, and has vertical lines -- and vice versa
local HGridInterval, VGridInterval local HLabel = 'x' local VLabel = 'y' if DoHorizontal then HLabel = 'y' VLabel = 'x' end if (HLabel
KeyWords["none"] then -- no horizontal grid else HGridInterval = Parms[string.upper(HLabel) .. "Grid"] * Mult[HLabel] end
if (VLabel
KeyWords["none"] then -- no vertical grid else VGridInterval = Parms[string.upper(VLabel) .. "Grid"] * Mult[VLabel] end
if Parms["XGrid"] ~= KeyWords["none"] or Parms["YGrid"] ~= KeyWords["none"] then table.insert(r, " <!--
-->") table.insert(r, " ")
table.insert(r, " ") table.insert(r, " ")
table.insert(r, "
if (HLabel
KeyWords["none"] then -- no horizontal grid else table.insert(r, " <!-- " .. HLabel .. "-axis grid, vertical lines -->") table.insert(r, "
if (VLabel
KeyWords["none"] then -- no vertical grid else table.insert(r, " <!-- " .. VLabel .. "-axis grid, horizontal lines -->") table.insert(r, "
if (HLabel
KeyWords["none"] then -- no horizontal grid else table.insert(r, "
if (VLabel
KeyWords["none"] then -- no vertical grid else table.insert(r, "
function stylesAreas
table.insert(r, " <!--
-->") table.insert(r, " ")
table.insert(r, " ") table.insert(r, " ")end
function codeStyleArea(SeriesNumber, Color, Pattern) -- area style for a series
-- SeriesNumber, numeric -- Color, text -- Pattern, text: 'none' or nil means a pattern is not defined
table.insert(r, " /*-- series " .. SeriesNumber .. " --*/") table.insert(r, " .series" .. SeriesNumber .. " ")end
function defsAreas
local exists = false for i = 1, SeriesCount do if FillPattern[i] ~= nil and FillPattern[i] ~= KeyWords["none"] then exists = true end end
if exists then table.insert(r, " <!--
-->") table.insert(r, " ") table.insert(r, "
function codeDefFillPattern(SeriesNumber, PatternType, FillColor, PatternColor) -- definition of a fill pattern for a series
-- SeriesNumber, numeric -- PatternType, numeric -- FillColor, text -- PatternColor, text
local l, t = "", ""
if PatternType
table.insert(r, " <!-- Series " .. SeriesNumber .. "-->")
-- pattern groups: -- 1-8: line hatch, close, rotated 0 (horizontal), 90 (vertical), -45, 45, -22.5, 22,5, -67.5, 67.5 -- 11-18: line hatch, wide, rotated 0 (horizontal), 90 (vertical), -45, 45, -22.5, 22,5, -67.5, 67.5 -- 21-24: cross hatch, close, rotated 0, 45, -22.5, -67.5 -- 31-34: cross hatch, wide, rotated 0, 45, -22.5, -67.5
-- addition of fill patterns will require changes in checkParms to allow them
if PatternType >= 1 and PatternType <= 8 then -- line hatches, close l = " 2 then l = l .. "90" elseif PatternType 4 then l = l .. "45" elseif PatternType 6 then l = l .. "22.5" elseif PatternType 8 then l = l .. "67.5" end table.insert(r, l .. ")\">") table.insert(r, " 12 then l = l .. "90" elseif PatternType 14 then l = l .. "45" elseif PatternType 16 then l = l .. "22.5" elseif PatternType 18 then l = l .. "67.5" end table.insert(r, l .. ")\">") table.insert(r, " 22 then l = l .. "45" elseif PatternType 24 then l = l .. "-67.5" end table.insert(r, l .. ")\">") table.insert(r, " 32 then l = l .. "45" elseif PatternType 34 then l = l .. "-67.5" end table.insert(r, l .. ")\">") table.insert(r, " 1 then l = l .. "0" elseif PatternType
3 then l = l .. "-45" elseif PatternType
5 then l = l .. "-22.5" elseif PatternType
7 then l = l .. "-67.5" elseif PatternType
11 then l = l .. "0" elseif PatternType
13 then l = l .. "-45" elseif PatternType
15 then l = l .. "-22.5" elseif PatternType
17 then l = l .. "-67.5" elseif PatternType
21 then l = l .. "0" elseif PatternType
23 then l = l .. "-22.5" elseif PatternType
31 then l = l .. "0" elseif PatternType
33 then l = l .. "-22.5" elseif PatternType
42 then table.insert(r, "
44 then table.insert(r, "
46 then table.insert(r, "
48 then table.insert(r, "
51 then table.insert(r, "
53 then table.insert(r, "
55 then table.insert(r, "
61 then table.insert(r, "
63 then table.insert(r, "
function stylesLines
table.insert(r, " <!--
-->") table.insert(r, " ")
table.insert(r, " ") table.insert(r, " ")end
function codeStyleLineMarker(SeriesNumber, LineRequired, LineColor, LineWidth, LineDash, MarkerRequired, MarkerFill) -- line and marker styles for a series
-- SeriesNumber, numeric -- LineRequired -- LineColor, text -- LineWidth, numeric -- LineDash, text -- MarkerRequired -- MarkerFill, text
if LineRequired ~= nil or MarkerRequired ~= nil then table.insert(r, " /*-- series " .. SeriesNumber .. " --*/") -- line defined if either line or marker required table.insert(r, " .series" .. SeriesNumber .. " ") else -- note: markers are set on the lines when they are created, not here in the line style -- this enables the line in the legend to have only a mid-marker table.insert(r, " }") -- define the marker stroke and fill table.insert(r, " .series" .. SeriesNumber .. "-marker ") end endend
function defsMarkers
local exists = false for i = 1, #SeriesData do if Marker[i] ~= nil and Marker[i] ~= KeyWords["none"] then exists = true end end
if exists then table.insert(r, " <!--
-->") table.insert(r, " ") table.insert(r, "
function codeDefMarkerShape(SeriesNumber, MarkerType, MarkerSize, MarkerFill) -- definition of a shape for a series
-- SeriesNumber, numeric -- MarkerType, numeric or text, if text (eg: 'yes') the default is SeriesNumber -- MarkerSize, numeric -- MarkerFill, text
-- addition of further markers will require changes in checkParms to allow them
local l, t = "", ""
table.insert(r, "
-- all shapes are defined around a centre point of 0,0
if MarkerType
3 then -- triangle (point up) t = round(MarkerSize / 2, 2) table.insert(r, " <!-- triangle, point up -->") l = "
5 then -- tilted triangle (point down) t = round(MarkerSize / 2, 2) table.insert(r, " <!-- triangle, point down -->") l = "
7 then -- plus t = round(MarkerSize / 2, 2) table.insert(r, " <!-- plus -->") table.insert(r, "
function codeDefMarkerCreate(SeriesNumber, MarkerSize) -- definition of a marker for a series
-- SeriesNumber, numeric -- MarkerSize, numeric
local l = ""
l = "
function elementsGraphs
local BarNumber = BarSeriesCount + 1 local DoLabels = false local LabelPos =
table.insert(r, " <!--
-->") table.insert(r, " ")
for i = #SeriesData, 1, -1 do if SeriesText[i]
KeyWords["no"] or (Parms["IncludeOriginalData"]
for k, v in ipairs(OriginalData[i]) do table.insert(r, " " .. v[1] .. " " .. v[2]) end table.insert(r, " -->") end LabelPos[i] = if SType[i]
table.insert(r, "
table.insert(r, "
local py = tonumber(v[2]) if DoYAxis2 and YAxis2[i] ~= nil then py = py * Mult.y2 else py = py * Mult.y end if DoHorizontal then if DoArea and k
1 then table.insert(r, " " .. round(px, 2) .. ", " .. round(iif(Parms["YMin"] > 0, -Parms["YMin"] * Mult.y, 0), 2) .. "") end table.insert(r, " " .. round(px, 2) .. ", " .. round(-py, 2) .. "") lastx = px if Labels[i] ~= nil then LabelPos[i][k] = LabelPos[i][k]["x"] = px + Siz.Text.Interline LabelPos[i][k]["y"] = py + Siz.Text.Interline DoLabels = true end end end if DoArea then if DoHorizontal then table.insert(r, " " .. round(iif(Parms["YMin"] > 0, Parms["YMin"] * Mult.y, 0), 2) .. ", " .. round(-lastx, 2) .. "") else table.insert(r, " " .. round(lastx, 2) .. ", " .. round(iif(Parms["YMin"] > 0, -Parms["YMin"] * Mult.y, 0), 2) .. "") end end table.insert(r, " \"/>") end table.insert(r, " ") end
if not DoStack100 and DoLabels then table.insert(r, " <!--
-->") table.insert(r, " ")
table.insert(r, " ") table.insert(r, " ")
for i = #SeriesData, 1, -1 do if Labels[i] ~= nil then for k, v in ipairs(SeriesData[i]) do if SType[i]
function elementsPie
table.insert(r, " <!--
-->") table.insert(r, " ")
table.insert(r, "
if Parms["IncludeOriginalData"]
KeyWords["auto"] and DataPointsCount > AutoDataPointsLimit) then table.insert(r, " <!-- original data: not included -->") else table.insert(r, " <!-- original data:")
for k, v in ipairs(OriginalData) do table.insert(r, " " .. v[1] .. " " .. v[2]) end table.insert(r, " -->") end
local Total = 0 for k, v in ipairs(SeriesData) do Total = Total + v[2] end
local TwoPi = math.pi * 2 local Sweep = 0 if Parms["PieSweepDir"] ~= "AntiClockwise" then -- change arcs to clockwise Sweep = 1 end
local Radian = 0
if Parms["PieStartAngle"] ~= 0 then Radian = math.rad(Parms["PieStartAngle"]) end
local InnerX, InnerY, InnerRadius = 0, 0, 0 if Parms["DoughnutHole"]
InnerX = math.cos(Radian) * InnerRadius InnerY = math.sin(Radian) * InnerRadius end
-- outer arcs start at the pie radius local OuterX = math.cos(Radian) * PieRadius local OuterY = math.sin(Radian) * PieRadius local Mid =
for k, v in ipairs(SeriesData) do local Arc = 0 -- default is short arc (<= 180 degrees) if (v[2] / Total * TwoPi) > math.pi then Arc = 1 -- long arc end
Mid[k] = Radian + ((v[2] / Total * TwoPi) / 2 * iif(Sweep
-- adjust X and Y for explode local DX, DY = 0, 0 if Parms["Explode"]
"number" and k > Parms["Explode"]) then -- no explode for this segment else DX = math.cos(Mid[k]) * (PieRadius * Parms["ExplodeRadius"] / 100) DY = math.sin(Mid[k]) * (PieRadius * Parms["ExplodeRadius"] / 100) end
Radian = Radian + (v[2] / Total * TwoPi * iif(Sweep
t = " t = t .. " l " .. round(NextInnerX - NextOuterX, 2).. ", " .. -round(NextInnerY - NextOuterY, 2) .. " a " .. InnerRadius .. ", " .. InnerRadius .. " 0 " .. Arc .. " " .. iif(Sweep InnerX = NextInnerX InnerY = NextInnerY end table.insert(r, t) OuterX = NextOuterX OuterY = NextOuterY end table.insert(r, " ") table.insert(r, " ") if Parms["SegmentText"] ~= nil then -- pie segment texts table.insert(r, " <!-- -->") table.insert(r, " ") table.insert(r, " ") table.insert(r, " ") table.insert(r, " for k, v in ipairs(SeriesData) do -- adjust X and Y for explode local DX, DY = 0, 0 if Parms["Explode"] "number" and k > Parms["Explode"]) then -- no explode for this segment else DX = math.cos(Mid[k]) * (PieRadius * Parms["ExplodeRadius"] / 100) DY = math.sin(Mid[k]) * (PieRadius * Parms["ExplodeRadius"] / 100) end local TextX = math.cos(Mid[k]) * (PieRadius * Parms["SegmentTextRadius"] / 100) local TextY = math.sin(Mid[k]) * (PieRadius * Parms["SegmentTextRadius"] / 100) if TextY < 0 then TextY = TextY - FontSiz.LegendText end t = " local tt = "" if string.find(Parms["SegmentText"], KeyWords["text"], 1, true) ~= nil then tt = SeriesText[k] end if string.find(Parms["SegmentText"], KeyWords["value"], 1, true) ~= nil then tt = tt .. iif(string.len(tt) > 0, " ", "") .. v[2] end if string.find(Parms["SegmentText"], KeyWords["percent"], 1, true) ~= nil then tt = tt .. iif(string.len(tt) > 0, " ", "") .. round(v[2] / Total * 100, 0) .. "%" end t = t .. tt table.insert(r, t .. "") end table.insert(r, " ") table.insert(r, " ") endend function codeAxes if Parms["Debug"] ~= nil and string.find(Parms["Debug"], "pos") ~= nil then -- show positions tables table.insert(r, "<!--") listTable(Mult, " ", "Mult") listTable(FontSiz, " ", "FontSiz") table.sort(Pos) listTable(Pos, " ", "Pos") table.sort(Siz) listTable(Siz, " ", "Siz") table.insert(r, "-->") end -- axis styles table.insert(r, " <!-- -->") table.insert(r, " ") table.insert(r, " ") table.insert(r, " ") table.insert(r, " <!-- -->") table.insert(r, " ") if DoHorizontal then codeAxisMarks('x', 'y', -1, Pos.XAxis.Line, -Parms["XMax"], Siz.ChartHeight) codeAxisMarks('y', 'x', 0, Pos.YAxis.Line, Parms["YMin"], Siz.ChartWidth) if DoYAxis2 then codeAxisMarks('y2', 'x', -1, Pos.YAxis2.Line, Parms["Y2Min"], Siz.ChartWidth) end else codeAxisMarks('x', 'x', 0, Pos.XAxis.Line, Parms["XMin"], Siz.ChartWidth) codeAxisMarks('y', 'y', -1, Pos.YAxis.Line, -Parms["YMax"], Siz.ChartHeight) if DoYAxis2 then codeAxisMarks('y2', 'y', 0, Pos.YAxis2.Line, -Parms["Y2Max"], Siz.ChartHeight) end end if Parms["XAxisArrows"] ~= nil or Parms["YAxisArrows"] ~= nil then table.insert(r, " <!-- -->") table.insert(r, " ") local ArrowSize = BaseLineWidth * 12 table.insert(r, " table.insert(r, " l = " l = " -->") table.insert(r, " ") if DoHorizontal then codeAxisLine('x', 'y', Pos.XAxis.Line, -Parms["XMin"], -Siz.ChartHeight) codeAxisLine('y', 'x', Pos.YAxis.Line, Parms["YMin"], Siz.ChartWidth) if DoYAxis2 then codeAxisLine('y2', 'x', Pos.YAxis2.Line, Parms["Y2Min"], Siz.ChartWidth) end else codeAxisLine('x', 'x', Pos.XAxis.Line, Parms["XMin"], Siz.ChartWidth) codeAxisLine('y', 'y', Pos.YAxis.Line, -Parms["YMin"], -Siz.ChartHeight) if DoYAxis2 then codeAxisLine('y2', 'y', Pos.YAxis2.Line, -Parms["Y2Min"], -Siz.ChartHeight) end end table.insert(r, " ") Format = if #GroupText 'none') end end Format.y = (Parms["YMax"] >= 10000) if Parms["YAxisValueFormat"] ~= nil then Format.y = not (Parms["YAxisValueFormat"] 'none') end end table.insert(r, " <!-- -->") table.insert(r, " ") if DoHorizontal then codeAxisValues('x', 'y', Parms["XAxisValueStep"], -1, -1, Parms["XMin"], Parms["XMax"], FontSiz["XAxisValues"] / 3, Pos.XAxis.Values, Parms["XAxisValueMultiplier"], Parms["XAxisValueRound"], Parms["XAxisValueAbsolute"], Parms["XAxisValuePrefix"], Parms["XAxisValueSuffix"], Format.x) codeAxisValues('y', 'x', Parms["YAxisValueStep"], 0, 1, Parms["YMin"], Parms["YMax"], 0, Pos.YAxis.Values, Parms["YAxisValueMultiplier"], Parms["YAxisValueRound"], Parms["YAxisValueAbsolute"], Parms["YAxisValuePrefix"], Parms["YAxisValueSuffix"], Format.y) if DoYAxis2 then codeAxisValues('y2', 'x', Parms["YAxis2ValueStep"], -1, 1, Parms["Y2Min"], Parms["Y2Max"], 0, Pos.YAxis2.Values, Parms["YAxis2ValueMultiplier"], Parms["YAxis2ValueRound"], Parms["YAxis2ValueAbsolute"], Parms["YAxis2ValuePrefix"], Parms["YAxis2ValueSuffix"], Format.y2) end else codeAxisValues('x', 'x', Parms["XAxisValueStep"], 0, 1, Parms["XMin"], Parms["XMax"], 0, Pos.XAxis.Values, Parms["XAxisValueMultiplier"], Parms["XAxisValueRound"], Parms["XAxisValueAbsolute"], Parms["XAxisValuePrefix"], Parms["XAxisValueSuffix"], Format.x) codeAxisValues('y', 'y', Parms["YAxisValueStep"], -1, -1, Parms["YMin"], Parms["YMax"], FontSiz["YAxisValues"] / 3, Pos.YAxis.Values, Parms["YAxisValueMultiplier"], Parms["YAxisValueRound"], Parms["YAxisValueAbsolute"], Parms["YAxisValuePrefix"], Parms["YAxisValueSuffix"], Format.y) if DoYAxis2 then codeAxisValues('y2', 'y', Parms["YAxis2ValueStep"], 0, -1, Parms["Y2Min"], Parms["Y2Max"], FontSiz["YAxis2Values"] / 3, Pos.YAxis2.Values, Parms["YAxis2ValueMultiplier"], Parms["YAxis2ValueRound"], Parms["YAxis2ValueAbsolute"], Parms["YAxis2ValuePrefix"], Parms["YAxis2ValueSuffix"], Format.y2) end end table.insert(r, " <!-- End Axis Chart Translate Axis Titles if DoHorizontal then if Parms["XAxisTitle"] ~= nil then codeAxisTitle('x', 'y', Siz.Space.Top + (Siz.ChartHeight * 0.5)) end if Parms["YAxisTitle"] ~= nil then codeAxisTitle('y', 'x', Siz.Space.Left + (Siz.ChartWidth * 0.5)) end if DoYAxis2 and Parms["YAxis2Title"] ~= nil then codeAxisTitle('y2', 'x', Siz.Space.Left + (Siz.ChartWidth * 0.5)) end else if Parms["XAxisTitle"] ~= nil then codeAxisTitle('x', 'x', Siz.Space.Left + (Siz.ChartWidth * 0.5)) end if Parms["YAxisTitle"] ~= nil then codeAxisTitle('y', 'y', Siz.Space.Top + (Siz.ChartHeight * 0.5)) end if DoYAxis2 and Parms["YAxis2Title"] ~= nil then codeAxisTitle('y2', 'y', Siz.Space.Top + (Siz.ChartHeight * 0.5)) end end table.insert(r, " ") endend function codeAxisMarks(AxisName, ChangeDim, MarkOffset, AxisLinePos, AxisStart, LineLength) -- AxisName, text: the name of the axis being coded, "x", "y" or "y2" -- ChangeDim, text: 'x' or 'y' - the dimension the axis actually changes in -- MarkOffset, numeric: direction of mark start away from the line, -1 or 0 -- AxisLinePos, numeric: position of the axis, ie: distance of the line from the chart origin point -- AxisStart, numeric: position of the start of the axis line -- LineLength, numeric: the length of the axis line local OtherDim = "y" if ChangeDim local AxisParmName = string.upper(AxisName) .. "Axis" if AxisName "y" then MarkGapDim = "height" MarkSizDim = "width" end if AxisName if Parms[AxisParmName .. "Mark2Step"] ~= nil then table.insert(r, " if Parms[AxisParmName .. "Mark2Step"] ~= nil then table.insert(r, " table.insert(r, " table.insert(r, " ") endend function codeAxisLine(AxisName, ChangeDim, AxisLinePos, AxisStart, LineLength) local OtherDim = "y" if ChangeDim table.insert(r, l)end function codeAxisValues(AxisName, ChangeDim, ValueStep, MarkOffset, PosChangeSign, ValueMin, ValueMax, TextShift, OtherValuesPos, ValueMultiplier, ValueRound, ValueAbsolute, Prefix, Suffix, Format) -- AxisName, text: the name of the axis being coded, "x", "y" or "y2" -- Parms for location -- ChangeDim, text: 'x' or 'y' - the dimension the axis actually changes in -- ValueStep, numeric: the step between values (and major marks) -- MarkOffset, numeric: direction of mark start away from the line, -1 or 0 -- PosChangeSign, numeric: the position direction for +ve changes in value, 1 or -1 -- ValueMin, numeric: the minimum value shown -- ValueMax, numeric: the maximum value shown -- TextShift, numeric: amount to shift text, to get it centered with its marks -- OtherValuesPos, numeric: alignment position for all values -- Parms for values format -- ValueMultiplier, numeric -- ValueRound, numeric -- ValueAbsulute -- Prefix, text -- Suffix, text -- Format, boolean local AxisParmName = string.upper(AxisName) .. "Axis" if AxisName "y" then if MarkOffset "x" then Anchor = "start" if Parms[AxisParmName .. "ValueRotate"] < 0 and MarkOffset -1 then Anchor = "end" end end end local Format = (Parms[string.upper(AxisName) .. "Max"] >= 10000) if Parms[AxisParmName .. "ValueFormat"] ~= nil then Format = not (Parms[AxisParmName .. "ValueFormat"] local l = "" local Position = 0 if AxisName ~= 'x' or #GroupText "x", 0, round(OtherValuesPos, 2)) .. ", " .. iif(ChangeDim local ValueStart = 0 if ValueMin ~= 0 then ValueStart = math.ceil(ValueMin / ValueStep) * ValueStep end local Value = ValueStart while Value <= ValueMax do Position = Value * Mult[AxisName] * PosChangeSign l = " "x", 0, round(OtherValuesPos, 2)) .. ", " .. iif(ChangeDim for k, v in ipairs(GroupText) do Position = ((iif(DoGroupsTopDown, #GroupText - k, k - 1) * GroupWidth) + (GroupWidth / 2)) * PosChangeSign l = " table.insert(r, l) end end table.insert(r, " ") table.insert(r, " ")end function codeAxisTitle(AxisName, ChangeDim, TitleCentre) local OtherDim = "y" if ChangeDim local AxisParmName = string.upper(AxisName) .. "Axis" if AxisName 'y' then l = l .. " transform = \"rotate(-90, " .. round(Pos[AxisParmName]["Title"], 2) .. ", " .. round(TitleCentre, 2) .. ")\"" end l = l .. " text-anchor=\"middle\">" .. Parms[AxisParmName .. "Title"] .. "nil then t = t .. " z\" />" else local NextInnerX = math.cos(Radian) * InnerRadius local NextInnerY = math.sin(Radian) * InnerRadius
0, 1, 0) .. " " .. round(InnerX - NextInnerX, 2) .. ", " .. -round(InnerY - NextInnerY, 2) .. "\" />"
Pie Segment Texts
nil or (type(Parms["Explode"])
Axis Styles
Axis Marks
Axis Arrows
Axis Lines
0 then Format.x = (Parms["XMax"] >= 10000) if Parms["XAxisValueFormat"] ~= nil then Format.x = not (Parms["XAxisValueFormat"]
'none') end if DoYAxis2 then Format.y2 = (Parms["Y2Max"] >= 10000) if Parms["YAxis2ValueFormat"] ~= nil then Format.y2 = not (Parms["YAxis2ValueFormat"]
Axis Values
-->") table.insert(r, " ") table.insert(r, " ") -- axis titles are always outside the chart, so are outside the chart translate if Parms["XAxisTitle"] ~= nil or Parms["YAxisTitle"] ~= nil or (DoYAxis2 and Parms["YAxis2Title"] ~= nil) then table.insert(r, " <!--
-->") table.insert(r, " ")
"y" then OtherDim = "x" end
"y2" then AxisParmName = "YAxis2" end local MarkGapDim = "width" local MarkSizDim = "height" if ChangeDim
"x" and #GroupText ~= 0 then -- no marks for x axis with groups else table.insert(r, "
"y" then OtherDim = "x" end local l = "
"y2" then AxisParmName = "YAxis2" end local Anchor = "middle" local RotateText = "" if ChangeDim
-1 then -- text is left of line Anchor = "end" else -- text is right of line Anchor = "start" end end if Parms[AxisParmName .. "ValueRotate"] ~= nil then RotateText = " transform=\"rotate(" .. Parms[AxisParmName .. "ValueRotate"] .. ", " if ChangeDim
0 then Anchor = "end" elseif Parms[AxisParmName .. "ValueRotate"] >= 0 and MarkOffset
'none') end
0 then -- numeric values table.insert(r, "
"x", round(OtherValuesPos, 2), TextShift) .. ")\"" .. ">")
'x' then if string.len(RotateText) > 0 then l = l .. RotateText .. round(Position, 2) .. ", 0)\"" end else if string.len(RotateText) > 0 then l = l .. RotateText .. "0, " .. round(Position - TextShift, 2) .. ")\"" end end l = l .. ">" l = l .. Prefix local v = Value if ValueAbsolute ~= nil then v = math.abs(v) end if Format then l = l .. mw.getContentLanguage:formatNum(round(v * ValueMultiplier, ValueRound)) else l = l .. round(v * ValueMultiplier, ValueRound) end l = l .. Suffix .. "
"x", round(OtherValuesPos, 2), TextShift) .. ")\"" .. ">")
'x' then if string.len(RotateText) > 0 then l = l .. RotateText .. round(Position, 2) .. ", 0)\"" end else if string.len(RotateText) > 0 then l = l .. RotateText .. "0, " .. round(Position - TextShift, 2) .. ")\"" end end l = l .. ">" .. v .. "
"y" then OtherDim = "x" end
"y2" then AxisParmName = "YAxis2" end local l = "
function commonBottom
if Parms["LegendType"]
nil and Parms["Footnote"]
Common-element Styles
table.insert(r, " ") table.insert(r, " ") end
-- legend
if Parms["LegendType"]
Legend
KeyWords["horizontal"] then -- horizontal legend if Parms["LegendX"] ~= nil then if DoHorizontal then tr = tr .. round(Pos.YAxis.Zero + (Parms["LegendY"] * Mult.y), 2) .. "" .. ", " .. round(Pos.XAxis.Zero - (Parms["LegendX"] * Mult.x), 2) else tr = tr .. round(Pos.XAxis.Zero + (Parms["LegendX"] * Mult.x), 2) .. "" .. ", " .. round(Pos.YAxis.Zero - (Parms["LegendY"] * Mult.y), 2) end else tr = tr .. round(Siz.Space.Left, 2) .. ", " .. round(Pos.Legend, 2) end table.insert(r, tr .. ")\">")
table.insert(r, "
local PosX, PosY = Siz.ChartMargin, Siz.ChartMargin for k, v in ipairs(SeriesText) do table.insert(r, " ") codeLegendElement((SType[k]
0 then -- new line of legend elements PosX = Siz.ChartMargin PosY = PosY + Siz.Legend.ElementHeight end end else -- vertical legend if Parms["LegendX"] ~= nil then if DoHorizontal then tr = tr .. round(Pos.YAxis.Zero + (Parms["LegendY"] * Mult.y), 2) .. ", " .. round(Pos.XAxis.Zero - (Parms["LegendX"] * Mult.x), 2) else tr = tr .. round(Pos.XAxis.Zero + (Parms["LegendX"] * Mult.x), 2) .. " " .. round(Pos.YAxis.Zero - (Parms["LegendY"] * Mult.y), 2) end else tr = tr .. round(Pos.Legend, 2) .. ", " .. round(Siz.Space.Top + (0.1 * Siz.ChartHeight), 2) end table.insert(r, tr .. ")\">")
table.insert(r, "
local PosX, PosY = Siz.ChartMargin, Siz.ChartMargin for k, v in ipairs(SeriesText) do table.insert(r, " ") codeLegendElement((SType[k]
-- chart texts
if #ChartText > 0 then table.insert(r, " <!--
-->") table.insert(r, " ")
table.insert(r, "
if Parms["Title"] ~= nil then table.insert(r, " <!--
-->") tr = "
if Parms["Footnote"] ~= nil then table.insert(r, " <!--
-->")
tr = "
if Parms["ImageForegroundSVG"] ~= nil then table.insert(r, " <!-- Image Foreground SVG -->") table.insert(r, " " .. Parms["ImageForegroundSVG"] .. "") table.insert(r, " ") end
table.insert(r, " ") table.insert(r, " ")end
function codeLegendElement(Lines, SeriesNumber, PosX, PosY, Padding, TextSize, Text, Marker) -- one entry in the legend
-- Lines, boolean -- SeriesNumber, numeric -- PosX, numeric -- PosY, numeric (= the top of the legend element) -- Padding, numeric -- TextSize, numeric -- Text, text -- Marker, text
local l = ""
if Lines then local lineY = round(PosY + TextSize / 2, 2)
l = "
function outputDebugInfo
if Parms["Debug"] ~= nil and string.find(Parms["Debug"], KeyWords["parms"]) ~= nil then -- list all parameters table.insert(r, " All Parameters :") for k, v in pairs(Args) do table.insert(r, " " .. k .. "=" .. v) end table.insert(r, " ")
-- list all recognised parameters sorted by key table.insert(r, " Parameters :") for k, v in spairs(Parms) do table.insert(r, " " .. k .. "=" .. v .. " (" .. type(v) .. ")") end table.insert(r, " ")
if string.find(Parms["Debug"], "parmsstop") ~= nil then return false end end
if Parms["Debug"] ~= nil and string.find(Parms["Debug"], "tables") ~= nil then -- list contents of internal tables
if DoStack or DoStack100 then listTable(OriginalData, " ", "OriginalData") end listTable(SeriesData, " ", "SeriesData") listTable(SType, " ", "SType") listTable(YAxis2, " ", "YAxis2") listTable(Labels, " ", "Labels") listTable(Color, " ", "Color") listTable(LineShow, " ", "LineShow") listTable(LineWidth, " ", "LineWidth") listTable(LineDash, " ", "LineDash") listTable(Marker, " ", "Marker") listTable(MarkerFill, " ", "MarkerFill") listTable(MarkerSize, " ", "MarkerSize") listTable(FillPattern, " ", "FillPattern") listTable(FillPatternColor, " ", "FillPatternColor") listTable(SeriesText, " ", "SeriesText") listTable(GroupText, " ", "GroupText") listTable(ChartText, " ", "ChartText") -- listTable(FontSiz, " ", "FontSiz") -- listTable(Siz, " ", "Siz") -- listTable(Pos, " ", "Pos") -- listTable(Mult, " ", "Mult") -- listTable(Adjusts, " ", "Adjusts") table.insert(r, " ")
if string.find(Parms["Debug"], "tablesstop") ~= nil then return false end end
table.insert(r, "----------") table.insert(r, " ") return trueend
function listTable(Tab, Indent, Title) -- lists all contents of a table, iterating over all sub-tables
if Title ~= nil then table.insert(r, " " .. Title .. ":") end for k, v in pairs(Tab) do if type(v)
function unknownParameters(knowns, regexps) -- outputs a table of parameters either not in knowns, or not matching a pattern in regexps
-- knowns - a table where the values are the known parameters -- regexps - a table where the values are lua patterns that parameters may match
-- modified from module at en.wikipedia.org/wiki/Module:Check_for_unknown_parameters
local knownparms =
local output =
-- create the lists of known parms and regular expressions for k, v in spairs(knowns) do v = trim(v) knownparms[v] = 1 end for k, v in pairs(regexps) do regexps[k] = '^' .. v .. '$' end -- check each entry in Args for k, v in pairs(Args) do if type(k)
nil then local knownflag = false for i, regexp in ipairs(regexps) do if mw.ustring.match(k, regexp) then knownflag = true break end end if not knownflag then local vlen = mw.ustring.len(v) v = mw.ustring.sub(v, 1, (vlen < 25) and vlen or 25) table.insert(output, k .. "=" .. v .. ((vlen >= 25) and ' ...' or )) end elseif type(k)
nil then local vlen = mw.ustring.len(v) v = mw.ustring.sub(v, 1, (vlen < 25) and vlen or 25) table.insert(output, k .. "=" .. v .. ((vlen >= 25) and ' ...' or )) end end return outputend
-- utility functions
function iif(test, tret, fret) -- if test is true, returns tret if defined, otherwise returns true -- if test is false, returns fret if defined, otherwise returns false
-- note that this function cannot be used to avoid evaluating either tret or fret, as they are both evaluated in the function call
if test then if tret
nil then return false end return fretend
function trim(s) -- the match this pattern returns is the string with any spaces (%s) at the start(^) and end ($) not included return s:match('^%s*(.-)%s*$') end function round(x, p) -- round number x to precision p
-- p > 0 rounds to p decimal places, eg: round(4.57, 1) = 4.6 -- p = 0 (or not given) rounds to nearest integer, eg: round(6.6) = 7, round(6.5) = 6 -- p < 0 rounds to p places above zero, eg: round(147, -1) = 150
local res
if type(x) ~= "number" then return nil end if type(p)
nil or p
function decPlaces(val) -- returns the number of decimal places in a numeric value, or a string convertible to a number val = tonumber(val) if val
0 then return 0 end return string.len(val) - pointend
function copyTable(from, to) -- copies a table to another table local k, v for k, v in ipairs(from) do if type(v)
function spairs(t, order) -- returns an iterator function that in turn returns table t in the order of the keys -- sort is done using an optional order function
-- collect the keys local keys = for k in pairs(t) do keys[#keys+1] = k end
-- if order function given, sort by it by passing the table and keys a, b, -- otherwise just sort the keys if order then table.sort(keys, function(a,b) return order(t, a, b) end) else table.sort(keys) end
-- return the iterator function local i = 0 return function i = i + 1 if keys[i] then return keys[i], t[keys[i]] end endend
return