Модуль:Graph: відмінності між версіями
Перейти до навігації
Перейти до пошуку
[перевірена версія] | [перевірена версія] |
Вилучено вміст Додано вміст
Ɪ (обговорення | внесок) Створена сторінка: -- УВАГА: Будь ласка, редагуйте цей код на https://de.wikipedia.org/wiki/Modul:Graph -- Таким чином код в усіх... |
Ɪ (обговорення | внесок) Немає опису редагування |
||
Рядок 169: | Рядок 169: | ||
on = "highlights", -- ім’я джерела даних про виділення |
on = "highlights", -- ім’я джерела даних про виділення |
||
onKey = "id", -- ключ для джерела даних про виділення |
onKey = "id", -- ключ для джерела даних про виділення |
||
as = { "zipped" }, -- ім’я |
as = { "zipped" }, -- ім’я вихідної таблиці |
||
default = { v = defaultValue } -- значення за замовчанням для географічних об’єктів, які не вдалося поєднати |
default = { v = defaultValue } -- значення за замовчанням для географічних об’єктів, які не вдалося поєднати |
||
} |
} |
||
Рядок 672: | Рядок 672: | ||
-- тип діаграми |
-- тип діаграми |
||
local chartType = frame.args.type or "line" |
local chartType = frame.args.type or "line" |
||
-- режим |
-- режим перекриття кольорів для лінійних і площинних діаграм: linear, step-before, step-after, basis, basis-open, basis-closed (type=line only), bundle (type=line only), cardinal, cardinal-open, cardinal-closed (type=line only), monotone |
||
local interpolate = frame.args.interpolate |
local interpolate = frame.args.interpolate |
||
-- позначити кольори (якщо кольори не задані, використовується 10-кольорова палітра за замовчанням) |
-- позначити кольори (якщо кольори не задані, використовується 10-кольорова палітра за замовчанням) |
||
Рядок 724: | Рядок 724: | ||
local data |
local data |
||
if chartType == "pie" then |
if chartType == "pie" then |
||
-- для секторних діаграм |
-- для секторних діаграм: друга серія розміщується всередині першої серії за значеннями радіусів |
||
data = convertXYToSingleSeries(x, y, xType, yType, { "y", "r" }) |
data = convertXYToSingleSeries(x, y, xType, yType, { "y", "r" }) |
||
else |
else |
||
Рядок 772: | Рядок 772: | ||
end |
end |
||
-- вирішити, що саме необхідно |
-- вирішити, що саме необхідно вивести: лінії (контури) чи площини (заливки) |
||
local colorField |
local colorField |
||
if chartType == "line" then colorField = "stroke" else colorField = "fill" end |
if chartType == "line" then colorField = "stroke" else colorField = "fill" end |
||
Рядок 782: | Рядок 782: | ||
local textmarks |
local textmarks |
||
if showValues then |
if showValues then |
||
if type(showValues) == "string" then -- |
if type(showValues) == "string" then -- десеріалізуати як таблицю |
||
local keyValues = mw.text.split(showValues, "%s*,%s*") |
local keyValues = mw.text.split(showValues, "%s*,%s*") |
||
showValues = {} |
showValues = {} |
Версія за 02:27, 15 березня 2016
Документація модуля[перегляд] [редагувати] [історія] [очистити кеш]
Обслуговує шаблон для створення діаграм {{graph:Chart}}.
Документація вище включена з Модуль:Graph/документація. (ред. | історія) Дописувачі можуть експериментувати на підсторінках пісочниці (ред. | різн.) та тести (створити) цього шаблону. Будь ласка, додавайте категорії до підсторінки /документація. Підсторінки цієї сторінки. |
-- УВАГА: Будь ласка, редагуйте цей код на https://de.wikipedia.org/wiki/Modul:Graph
-- Таким чином код в усіх мовних розділах буде ідентичним. Дякуємо!
--
-- Історія версій:
-- 2016-01-09 _БУДЬ ЛАСКА, ПОНОВІТЬ ДАТУ при внесенні будь-яких змін_
-- 2016-01-28 Для карт завжди використовуйте протокол wikiraw://. Протокол https:// невдовзі буде деактивований.
local p = {}
local baseMapDirectory = "Module:Graph/"
local function numericArray(csv)
if not csv then return end
local list = mw.text.split(csv, "%s*,%s*")
local result = {}
local isInteger = true
for i = 1, #list do
result[i] = tonumber(list[i])
if not result[i] then return end
if isInteger then
local int, frac = math.modf(result[i])
isInteger = frac == 0.0
end
end
return result, isInteger
end
local function stringArray(csv)
if not csv then return end
return mw.text.split(csv, "%s*,%s*")
end
local function isTable(t) return type(t) == "table" end
local function copy(x)
if type(x) == "table" then
local result = {}
for key, value in pairs(x) do result[key] = copy(value) end
return result
else
return x
end
end
function p.map(frame)
-- дані контурів карт для географічних об’єктів
local basemap = frame.args.basemap or "WorldMap-iso2.json"
-- масштабний коефіцієнт
local scale = tonumber(frame.args.scale) or 100
-- проекція карти, див. https://github.com/mbostock/d3/wiki/Geo-Projections
local projection = frame.args.projection or "equirectangular"
-- значення за замовчанням для географічних об’єктів без даних
local defaultValue = frame.args.defaultValue
local scaleType = frame.args.scaleType or "linear"
-- мінімальний діапазон значень (тільки для цифрових даних)
local domainMin = tonumber(frame.args.domainMin)
-- максимальний діапазон значень (тільки для цифрових даних)
local domainMax = tonumber(frame.args.domainMax)
-- кольорові координати шкали кольорів (тільки для цифрових даних)
local colorScale = frame.args.colorScale or "category10"
-- показати легенду
local legend = frame.args.legend
-- вивести в форматі JSON
local formatJson = frame.args.formatjson
-- картографічні дані — пари ключ-значення: ключі — рядки тільки з великих літер (бажано ISO-коди), які мають відповідати значенням "id" даних контурів карт
local values = {}
local isNumbers = nil
for name, value in pairs(frame.args) do
if mw.ustring.find(name, "^[^%l]+$") then
if isNumbers == nil then isNumbers = tonumber(value) end
local data = { id = name, v = value }
if isNumbers then data.v = tonumber(data.v) end
table.insert(values, data)
end
end
if not defaultValue then
if isNumbers then defaultValue = 0 else defaultValue = "silver" end
end
-- створити шкалу виділення
local scales
if isNumbers then
if colorScale == "category10" or colorScale == "category20" then else colorScale = stringArray(colorScale) end
scales =
{
{
name = "color",
type = scaleType,
domain = { data = "highlights", field = "v" },
range = colorScale,
nice = true
}
}
if domainMin then scales[1].domainMin = domainMin end
if domainMax then scales[1].domainMax = domainMax end
local exponent = string.match(scaleType, "pow%s+(%d+%.?%d+)") -- перевірка експоненти
if exponent then
scales[1].type = "pow"
scales[1].exponent = exponent
end
end
-- створити легенду
if legend then
legend =
{
{
fill = "color",
offset = 120,
properties =
{
title = { fontSize = { value = 14 } },
labels = { fontSize = { value = 12 } },
legend =
{
stroke = { value = "silver" },
strokeWidth = { value = 1.5 }
}
}
}
}
end
-- отримати url карти
local basemapUrl
if (string.sub(basemap, 1, 10) == "wikiraw://") then
basemapUrl = basemap
else
-- якщо url відсутній або не підтримується, шукати двокрапку як роздільник між простором назв і заголовком. Якщо двокрапки немає, подати першою директорію карти за замовчанням.
if not string.find(basemap, ":") then basemap = baseMapDirectory .. basemap end
basemapUrl = "wikiraw:///" .. mw.uri.encode(mw.title.new(basemap).prefixedText, "PATH")
end
local output =
{
version = 2,
width = 1, -- загальне значення, оскільки розмір виводу залежить виключно від розміру карти і масштабного коефіцієнту
height = 1, -- див. вище
data =
{
{
-- джерело даних для виділень
name = "highlights",
values = values
},
{
-- джерело даних для контурів карт
name = "countries",
url = basemapUrl,
format = { type = "topojson", feature = "countries" },
transform =
{
{
-- географічна трансформація («геоконтур») даних про контури карт
type = "geopath",
value = "data", -- джерело даних
scale = scale,
translate = { 0, 0 },
projection = projection
},
{
-- об’єднання («зіп») кількох джерел даних: тут — контурів карт і виділень
type = "lookup",
keys = { "id" }, -- ключ для даних про контури карт
on = "highlights", -- ім’я джерела даних про виділення
onKey = "id", -- ключ для джерела даних про виділення
as = { "zipped" }, -- ім’я вихідної таблиці
default = { v = defaultValue } -- значення за замовчанням для географічних об’єктів, які не вдалося поєднати
}
}
}
},
marks =
{
-- вивідна розмітка (контури карт і виділення)
{
type = "path",
from = { data = "countries" },
properties =
{
enter = { path = { field = "layout_path" } },
update = { fill = { field = "zipped.v" } },
hover = { fill = { value = "darkgrey" } }
}
}
},
legends = legend
}
if (scales) then
output.scales = scales
output.marks[1].properties.update.fill.scale = "color"
end
local flags
if formatJson then flags = mw.text.JSON_PRETTY end
return mw.text.jsonEncode(output, flags)
end
local function deserializeXData(serializedX, xType, xMin, xMax)
local x
if not xType or xType == "integer" or xType == "number" then
local isInteger
x, isInteger = numericArray(serializedX)
if x then
xMin = tonumber(xMin)
xMax = tonumber(xMax)
if not xType then
if isInteger then xType = "integer" else xType = "number" end
end
else
if xType then error("Numbers expected for parameter 'x'") end
end
end
if not x then
x = stringArray(serializedX)
if not xType then xType = "string" end
end
return x, xType, xMin, xMax
end
local function deserializeYData(serializedYs, yType, yMin, yMax)
local y = {}
local areAllInteger = true
for yNum, value in pairs(serializedYs) do
local yValues
if not yType or yType == "integer" or yType == "number" then
local isInteger
yValues, isInteger = numericArray(value)
if yValues then
areAllInteger = areAllInteger and isInteger
else
if yType then
error("Numbers expected for parameter '" .. name .. "'")
else
return deserializeYData(serializedYs, "string", yMin, yMax)
end
end
end
if not yValues then yValues = stringArray(value) end
y[yNum] = yValues
end
if not yType then
if areAllInteger then yType = "integer" else yType = "number" end
end
if yType == "integer" or yType == "number" then
yMin = tonumber(yMin)
yMax = tonumber(yMax)
end
return y, yType, yMin, yMax
end
local function convertXYToManySeries(x, y, xType, yType, seriesTitles)
local data =
{
name = "chart",
format =
{
type = "json",
parse = { x = xType, y = yType }
},
values = {}
}
for i = 1, #y do
for j = 1, #x do
if j <= #y[i] then table.insert(data.values, { series = seriesTitles[i], x = x[j], y = y[i][j] }) end
end
end
return data
end
local function convertXYToSingleSeries(x, y, xType, yType, yNames)
local data = { name = "chart", format = { type = "json", parse = { x = xType } }, values = {} }
for j = 1, #y do data.format.parse[yNames[j]] = yType end
for i = 1, #x do
local item = { x = x[i] }
for j = 1, #y do item[yNames[j]] = y[j][i] end
table.insert(data.values, item)
end
return data
end
local function getXScale(chartType, stacked, xMin, xMax, xType)
if chartType == "pie" then return end
local xscale =
{
name = "x",
type = "linear",
range = "width",
zero = false, -- не включає нульове значення
nice = true, -- примусово виводити круглі числа для шкали y
domain = { data = "chart", field = "x" }
}
if xMin then xscale.domainMin = xMin end
if xMax then xscale.domainMax = xMax end
if xMin or xMax then xscale.clamp = true end
if chartType == "rect" then
xscale.type = "ordinal"
if not stacked then xscale.padding = 0.2 end -- доповнити пробілам кожну з груп
else
if xType == "date" then xscale.type = "time"
elseif xType == "string" then xscale.type = "ordinal" end
end
return xscale
end
local function getYScale(chartType, stacked, yMin, yMax, yType)
if chartType == "pie" then return end
local yscale =
{
name = "y",
type = "linear",
range = "height",
-- нижнє граничне значення заливки областей діаграми y=0 (див. marks.properties.enter.y2), тому вони мають починатися з нуля
zero = chartType ~= "line",
nice = true
}
if yMin then yscale.domainMin = yMin end
if yMax then yscale.domainMax = yMax end
if yMin or yMax then yscale.clamp = true end
if yType == "date" then yscale.type = "time"
elseif yType == "string" then yscale.type = "ordinal" end
if stacked then
yscale.domain = { data = "stats", field = "sum_y" }
else
yscale.domain = { data = "chart", field = "y" }
end
return yscale
end
local function getColorScale(colors, chartType, xCount, yCount)
if not colors then
if (chartType == "pie" and xCount > 10) or yCount > 10 then colors = "category20" else colors = "category10" end
end
local colorScale =
{
name = "color",
type = "ordinal",
range = colors,
domain = { data = "chart", field = "series" }
}
if chartType == "pie" then colorScale.domain.field = "x" end
return colorScale
end
local function getAlphaColorScale(colors, y)
local alphaScale
-- якщо є принаймні один колір формату "#aarrggbb", створити (альфа-) шкалу прозорості
if isTable(colors) then
local alphas = {}
local hasAlpha = false
for i = 1, #colors do
local a, rgb = string.match(colors[i], "#(%x%x)(%x%x%x%x%x%x)")
if a then
hasAlpha = true
alphas[i] = tostring(tonumber(a, 16) / 255.0)
colors[i] = "#" .. rgb
else
alphas[i] = "1"
end
end
for i = #colors + 1, #y do alphas[i] = "1" end
if hasAlpha then alphaScale = { name = "transparency", type = "ordinal", range = alphas } end
end
return alphaScale
end
local function getValueScale(fieldName, min, max, type)
local valueScale =
{
name = fieldName,
type = type or "linear",
domain = { data = "chart", field = fieldName },
range = { min, max }
}
return valueScale
end
local function addInteractionToChartVisualisation(plotMarks, colorField, dataField)
-- первісне налаштування
if not plotMarks.properties.enter then plotMarks.properties.enter = {} end
plotMarks.properties.enter[colorField] = { scale = "color", field = dataField }
-- дія, коли курсор над позначкою діаграми
if not plotMarks.properties.hover then plotMarks.properties.hover = {} end
plotMarks.properties.hover[colorField] = { value = "red" }
-- дія, коли курсор залишає позначку діаграми: повернення до первісного налаштування
if not plotMarks.properties.update then plotMarks.properties.update = {} end
plotMarks.properties.update[colorField] = { scale = "color", field = dataField }
end
local function getPieChartVisualisation(yCount, innerRadius, outerRadius, linewidth, radiusScale)
local chartvis =
{
type = "arc",
from = { data = "chart", transform = { { field = "y", type = "pie" } } },
properties =
{
enter = {
innerRadius = { value = innerRadius },
outerRadius = { },
startAngle = { field = "layout_start" },
endAngle = { field = "layout_end" },
stroke = { value = "white" },
strokeWidth = { value = linewidth or 1 }
}
}
}
if radiusScale then
chartvis.properties.enter.outerRadius.scale = radiusScale.name
chartvis.properties.enter.outerRadius.field = radiusScale.domain.field
else
chartvis.properties.enter.outerRadius.value = outerRadius
end
addInteractionToChartVisualisation(chartvis, "fill", "x")
return chartvis
end
local function getChartVisualisation(chartType, stacked, colorField, yCount, innerRadius, outerRadius, linewidth, alphaScale, radiusScale, interpolate)
if chartType == "pie" then return getPieChartVisualisation(yCount, innerRadius, outerRadius, linewidth, radiusScale) end
local chartvis =
{
type = chartType,
properties =
{
-- хендлер подій створення діаграм
enter =
{
x = { scale = "x", field = "x" },
y = { scale = "y", field = "y" }
}
}
}
addInteractionToChartVisualisation(chartvis, colorField, "series")
if colorField == "stroke" then
chartvis.properties.enter.strokeWidth = { value = linewidth or 2.5 }
end
if interpolate then chartvis.properties.enter.interpolate = { value = interpolate } end
if alphaScale then chartvis.properties.update[colorField .. "Opacity"] = { scale = "transparency" } end
-- для стовпчастих і площинних діаграм установити нижнє граничне значення їхніх площин
if chartType == "rect" or chartType == "area" then
if stacked then
-- для стосових діаграм це нижнє граничне значення є кінцем останнього елемента діаграми
chartvis.properties.enter.y2 = { scale = "y", field = "layout_end" }
else
--[[
для нестосових діаграм нижнє граничне значення y=0
ЗРОБИТИ: значення "yscale.zero" встановлене як "true" для цього випадку, але як "false" для всіх інших випадків.
Для аналогічної поведінки "y2" треба насправді розмістити там, де вісь y перетинає вісь x,
якщо дані мають тільки додатні або від’ємні значення ]]
chartvis.properties.enter.y2 = { scale = "y", value = 0 }
end
end
-- для стовпчастих діаграм…
if chartType == "rect" then
-- задати ширину просвіту між стовпцями в 1 піксел
chartvis.properties.enter.width = { scale = "x", band = true, offset = -1 }
-- для серій діаграм маркування стовпців має використовувати «внутрішню» шкалу серії, тоді як «зовнішня» шкала x використувується для групування
if not stacked and yCount > 1 then
chartvis.properties.enter.x.scale = "series"
chartvis.properties.enter.x.field = "series"
chartvis.properties.enter.width.scale = "series"
end
end
-- стосові діаграми мають власні (стосові) значення y
if stacked then chartvis.properties.enter.y.field = "layout_start" end
-- якщо є множинні серії, згрупувати їх
if yCount == 1 then
chartvis.from = { data = "chart" }
else
-- якщо є множинні серії, прив’язати до них кольори
chartvis.properties.update[colorField].field = "series"
if alphaScale then chartvis.properties.update[colorField .. "Opacity"].field = "series" end
-- застосувати групувальну (поєднальну) трансформацію
chartvis =
{
type = "group",
marks = { chartvis },
from =
{
data = "chart",
transform =
{
{
type = "facet",
groupby = { "series" }
}
}
}
}
-- для стосових діаграм застосувати трансформацію накладення
if stacked then
table.insert(chartvis.from.transform, 1, { type = "stack", groupby = { "x" }, sortby = { "series" }, field = "y" } )
else
-- для стовпчастих діаграм серії групуються впритул по x
if chartType == "rect" then
-- для стовпчастих діаграм з багаторазовими серіями: кожна серія групується за значенням x, отже, серії потребують власної шкали в кожній групі x
local groupScale =
{
name = "series",
type = "ordinal",
range = "width",
domain = { field = "series" }
}
chartvis.from.transform[1].groupby = "x"
chartvis.scales = { groupScale }
chartvis.properties = { enter = { x = { field = "key", scale = "x" }, width = { scale = "x", band = true } } }
end
end
end
return chartvis
end
local function getTextMarks(chartvis, chartType, outerRadius, scales, radiusScale, yType, showValues)
local properties
if chartType == "rect" then
properties =
{
x = { scale = chartvis.properties.enter.x.scale, field = chartvis.properties.enter.x.field },
y = { scale = chartvis.properties.enter.y.scale, field = chartvis.properties.enter.y.field, offset = -(tonumber(showValues.offset) or -4) },
--dx = { scale = chartvis.properties.enter.x.scale, band = true, mult = 0.5 }, -- для горизонтального тексту
dy = { scale = chartvis.properties.enter.x.scale, band = true, mult = 0.5 }, -- для вертикального тексту
align = { },
baseline = { value = "middle" },
fill = { },
angle = { value = -90 },
fontSize = { value = tonumber(showValues.fontsize) or 11 }
}
if properties.y.offset >= 0 then
properties.align.value = "right"
properties.fill.value = showValues.fontcolor or "white"
else
properties.align.value = "left"
properties.fill.value = showValues.fontcolor or "black"
end
elseif chartType == "pie" then
properties =
{
x = { group = "width", mult = 0.5 },
y = { group = "height", mult = 0.5 },
radius = { offset = tonumber(showValues.offset) or -4 },
theta = { field = "layout_mid" },
fill = { value = showValues.fontcolor or "black" },
baseline = { },
angle = { },
fontSize = { value = tonumber(showValues.fontsize) or math.ceil(outerRadius / 10) }
}
if (showValues.angle or "midangle") == "midangle" then
properties.align = { value = "center" }
properties.angle = { field = "layout_mid", mult = 180.0 / math.pi }
if properties.radius.offset >= 0 then
properties.baseline.value = "bottom"
else
if not showValues.fontcolor then properties.fill.value = "white" end
properties.baseline.value = "top"
end
elseif tonumber(showValues.angle) then
-- квантизувати шкалу для вирівнювання тексту вліво на правому півколі і вправо на лівому півколі
local alignScale = { name = "align", type = "quantize", domainMin = 0.0, domainMax = math.pi * 2, range = { "left", "right" } }
table.insert(scales, alignScale)
properties.align = { scale = alignScale.name, field = "layout_mid" }
properties.angle = { value = tonumber(showValues.angle) }
properties.baseline.value = "middle"
if not tonumber(showValues.offset) then properties.radius.offset = 4 end
end
if radiusScale then
properties.radius.scale = radiusScale.name
properties.radius.field = radiusScale.domain.field
else
properties.radius.value = outerRadius
end
end
if properties then
if showValues.format then
local template = "datum.y"
if yType == "integer" or yType == "number" then template = template .. "|number:'" .. showValues.format .. "'"
elseif yType == "date" then template = template .. "|time:" .. showValues.format .. "'"
end
properties.text = { template = "{{" .. template .. "}}" }
else
properties.text = { field = "y" }
end
local textmarks =
{
type = "text",
properties =
{
enter = properties
}
}
if chartvis.from then textmarks.from = copy(chartvis.from) end
return textmarks
end
end
local function getAxes(xTitle, xAxisFormat, xType, yTitle, yAxisFormat, yType, chartType)
local xAxis, yAxis
if chartType ~= "pie" then
if xType == "integer" and not xAxisFormat then xAxisFormat = "d" end
xAxis =
{
type = "x",
scale = "x",
title = xTitle,
format = xAxisFormat
}
if yType == "integer" and not yAxisFormat then yAxisFormat = "d" end
yAxis =
{
type = "y",
scale = "y",
title = yTitle,
format = yAxisFormat
}
end
return xAxis, yAxis
end
local function getLegend(legendTitle, chartType, outerRadius)
local legend =
{
fill = "color",
stroke = "color",
title = legendTitle,
}
if chartType == "pie" then
-- перемістити легенду з центральної позиції наверх
legend.properties = { legend = { y = { value = -outerRadius } } }
end
return legend
end
function p.chart(frame)
-- ширина й висота діаграми
local graphwidth = tonumber(frame.args.width) or 200
local graphheight = tonumber(frame.args.height) or 200
-- тип діаграми
local chartType = frame.args.type or "line"
-- режим перекриття кольорів для лінійних і площинних діаграм: linear, step-before, step-after, basis, basis-open, basis-closed (type=line only), bundle (type=line only), cardinal, cardinal-open, cardinal-closed (type=line only), monotone
local interpolate = frame.args.interpolate
-- позначити кольори (якщо кольори не задані, використовується 10-кольорова палітра за замовчанням)
local colors = stringArray(frame.args.colors)
-- для лінійних діаграм — товщина лінії; для секторних діаграм — ширина проміжку між секторами
local linewidth = tonumber(frame.args.linewidth)
-- підпис під осями x та y
local xTitle = frame.args.xAxisTitle
local yTitle = frame.args.yAxisTitle
-- типи значень x та y
local xType = frame.args.xType
local yType = frame.args.yType
-- подавити мінімум і максимум осей x та y
local xMin = frame.args.xAxisMin
local xMax = frame.args.xAxisMax
local yMin = frame.args.yAxisMin
local yMax = frame.args.yAxisMax
-- подавити форматування ярликів осей x та y
local xAxisFormat = frame.args.xAxisFormat
local yAxisFormat = frame.args.yAxisFormat
-- показати легенду з заданим заголовком
local legendTitle = frame.args.legend
-- показати значення як текст
local showValues = frame.args.showValues
-- радіуси кругової діаграми
local innerRadius = tonumber(frame.args.innerRadius) or 0
local outerRadius = math.min(graphwidth, graphheight)
-- вивести у форматі JSON
local formatJson = frame.args.formatjson
-- отримати значення x
local x
x, xType, xMin, xMax = deserializeXData(frame.args.x, xType, xMin, xMax)
-- отримати значення y (серії)
local yValues = {}
local seriesTitles = {}
for name, value in pairs(frame.args) do
local yNum
if name == "y" then yNum = 1 else yNum = tonumber(string.match(name, "^y(%d+)$")) end
if yNum then
yValues[yNum] = value
-- назвати серію: за замовчанням — "y<number>". Може бути переписана з використанням параметрів "y<number>Title".
seriesTitles[yNum] = frame.args["y" .. yNum .. "Title"] or name
end
end
local y
y, yType, yMin, yMax = deserializeYData(yValues, yType, yMin, yMax)
-- створити кортеж даних: індекс серії, значення x та y
local data
if chartType == "pie" then
-- для секторних діаграм: друга серія розміщується всередині першої серії за значеннями радіусів
data = convertXYToSingleSeries(x, y, xType, yType, { "y", "r" })
else
data = convertXYToManySeries(x, y, xType, yType, seriesTitles)
end
-- сконфігурувати стосові діаграми
local stacked = false
local stats
if string.sub(chartType, 1, 7) == "stacked" then
chartType = string.sub(chartType, 8)
if #y > 1 then -- ігнорувати стосові діаграми, якщо є тільки одна серія
stacked = true
-- зібрати дані за кумулятивними значеннями y
stats =
{
name = "stats", source = "chart", transform =
{
{
type = "aggregate",
groupby = { "x" },
summarize = { y = "sum" }
}
}
}
end
end
-- створити шкали
local scales = {}
local xscale = getXScale(chartType, stacked, xMin, xMax, xType)
table.insert(scales, xscale)
local yscale = getYScale(chartType, stacked, yMin, yMax, yType)
table.insert(scales, yscale)
local colorScale = getColorScale(colors, chartType, #x, #y)
table.insert(scales, colorScale)
local alphaScale = getAlphaColorScale(colors, y)
table.insert(scales, alphaScale)
local radiusScale
if chartType == "pie" and #y > 1 then
radiusScale = getValueScale("r", 0, outerRadius)
table.insert(scales, radiusScale)
end
-- вирішити, що саме необхідно вивести: лінії (контури) чи площини (заливки)
local colorField
if chartType == "line" then colorField = "stroke" else colorField = "fill" end
-- створити мітки діаграм
local chartvis = getChartVisualisation(chartType, stacked, colorField, #y, innerRadius, outerRadius, linewidth, alphaScale, radiusScale, interpolate)
-- текстові позначки
local textmarks
if showValues then
if type(showValues) == "string" then -- десеріалізуати як таблицю
local keyValues = mw.text.split(showValues, "%s*,%s*")
showValues = {}
for _, kv in ipairs(keyValues) do
local key, value = mw.ustring.match(kv, "^%s*(.-)%s*:%s*(.-)%s*$")
if key then showValues[key] = value end
end
end
local chartmarks = chartvis
if chartmarks.marks then chartmarks = chartmarks.marks[1] end
textmarks = getTextMarks(chartmarks, chartType, outerRadius, scales, radiusScale, yType, showValues)
if chartmarks ~= chartvis then
table.insert(chartvis.marks, textmarks)
textmarks = nil
end
end
-- осі
local xAxis, yAxis = getAxes(xTitle, xAxisFormat, xType, yTitle, yAxisFormat, yType, chartType)
-- легенда
local legend
if legendTitle then legend = getLegend(legendTitle, chartType, outerRadius) end
-- створити об’єкт для фінального виводу
local output =
{
version = 2,
width = graphwidth,
height = graphheight,
data = { data, stats },
scales = scales,
axes = { xAxis, yAxis },
marks = { chartvis, textmarks },
legends = { legend }
}
local flags
if formatJson then flags = mw.text.JSON_PRETTY end
return mw.text.jsonEncode(output, flags)
end
function p.mapWrapper(frame)
return p.map(frame:getParent())
end
function p.chartWrapper(frame)
return p.chart(frame:getParent())
end
return p