570
редагувань
ɪ (обговорення | внесок) (кут нахилу тексту, примусове вирівнювання) |
Yurik (обговорення | внесок) (updated from de.wikipedia.org) |
||
--
-- This way all wiki languages can stay in sync. Thank you!
--
-- Version History:
-- 2016-01-09 _PLEASE UPDATE when modifying anything_
-- 2016-01-28
-- 2016-03-20 Allow omitted data for charts, labels for line charts with string (ordinal) scale at point location
-- 2016-05-16 Added encodeTitleForPath() to help all path-based APIs graphs like pageviews
local p = {}
local isInteger = true
for i = 1, #list do
else
if not result[i] then return end
if isInteger then
local int, frac = math.modf(result[i])
isInteger = frac == 0.0
end
end
end
function p.map(frame)
-- map path data for geographic objects
local basemap = frame.args.basemap or "WorldMap-iso2.json"
-- scaling factor
local scale = tonumber(frame.args.scale) or 100
--
local projection = frame.args.projection or "equirectangular"
-- defaultValue for geographic objects without data
local defaultValue = frame.args.defaultValue
local scaleType = frame.args.scaleType or "linear"
-- minimaler Wertebereich (nur für numerische Daten)
local domainMin = tonumber(frame.args.domainMin)
-- maximaler Wertebereich (nur für numerische Daten)
local domainMax = tonumber(frame.args.domainMax)
-- Farbwerte der Farbskala (nur für numerische Daten)
local colorScale = frame.args.colorScale or "category10"
-- show legend
local legend = frame.args.legend
-- format JSON output
local formatJson = frame.args.formatjson
-- map data are key-value pairs: keys are non-lowercase strings (ideally ISO codes) which need to match the "id" values of the map path data
local values = {}
local isNumbers = nil
end
-- create highlight scale
local scales
if isNumbers then
if domainMax then scales[1].domainMax = domainMax end
local exponent = string.match(scaleType, "pow%s+(%d+%.?%d+)") --
if exponent then
scales[1].type = "pow"
end
-- create legend
if legend then
legend =
end
-- get map url
local basemapUrl
if (string.sub(basemap, 1, 10) == "wikiraw://") then
basemapUrl = basemap
else
-- if not a (supported) url look for a colon as namespace separator. If none prepend default map directory name.
if not string.find(basemap, ":") then basemap = baseMapDirectory .. basemap end
basemapUrl = "wikiraw:///" .. mw.uri.encode(mw.title.new(basemap).prefixedText, "PATH")
{
version = 2,
width = 1, -- generic value as output size depends solely on map size and scaling factor
height = 1, --
data =
{
{
-- data source for the highlights
name = "highlights",
values = values
},
{
-- data source for map paths data
name = "countries",
url = basemapUrl,
{
{
-- geographic transformation ("geopath") of map paths data
type = "geopath",
value = "data", --
scale = scale,
translate = { 0, 0 },
},
{
-- join ("zip") of mutiple data source: here map paths data and highlights
type = "lookup",
keys = { "id" }, --
on = "highlights", --
onKey = "id", --
as = { "zipped" }, --
default = { v = defaultValue } --
}
}
marks =
{
-- output markings (map paths and highlights)
{
type = "path",
}
for i = 1, #y do
local yLen = table.maxn(y[i])
for j = 1, #x do
if j <=
end
end
type = "linear",
range = "width",
zero = false, --
nice = true, --
domain = { data = "chart", field = "x" }
}
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"
xscale.points = true
end
end
type = "linear",
range = "height",
--
zero = chartType ~= "line",
nice = true
local function getAlphaColorScale(colors, y)
local alphaScale
-- if there is at least one color in the format "#aarrggbb", create a transparency (alpha) scale
if isTable(colors) then
local alphas = {}
local function addInteractionToChartVisualisation(plotMarks, colorField, dataField)
-- initial setup
if not plotMarks.properties.enter then plotMarks.properties.enter = {} end
plotMarks.properties.enter[colorField] = { scale = "color", field = dataField }
-- action when cursor is over plot mark: highlight
if not plotMarks.properties.hover then plotMarks.properties.hover = {} end
plotMarks.properties.hover[colorField] = { value = "red" }
-- action when cursor leaves plot mark: reset to initial setup
if not plotMarks.properties.update then plotMarks.properties.update = {} end
plotMarks.properties.update[colorField] = { scale = "color", field = dataField }
properties =
{
-- chart creation event handler
enter =
{
if alphaScale then chartvis.properties.update[colorField .. "Opacity"] = { scale = "transparency" } end
-- for bars and area charts set the lower bound of their areas
if chartType == "rect" or chartType == "area" then
if stacked then
-- for stacked charts this lower bound is the end of the last stacking element
chartvis.properties.enter.y2 = { scale = "y", field = "layout_end" }
else
--[[
for non-stacking charts the lower bound is y=0
For the similar behavior "y2" should actually be set to where y axis crosses the x axis,
if there are only positive or negative values in the data ]]
chartvis.properties.enter.y2 = { scale = "y", value = 0 }
end
end
-- for bar charts ...
if chartType == "rect" then
-- set 1 pixel width between the bars
chartvis.properties.enter.width = { scale = "x", band = true, offset = -1 }
-- for multiple series the bar marking needs to use the "inner" series scale, whereas the "outer" x scale is used by the grouping
if not stacked and yCount > 1 then
chartvis.properties.enter.x.scale = "series"
end
end
-- stacked charts have their own (stacked) y values
if stacked then chartvis.properties.enter.y.field = "layout_start" end
-- if there are multiple series group these together
if yCount == 1 then
chartvis.from = { data = "chart" }
else
-- if there are multiple series, connect colors to series
chartvis.properties.update[colorField].field = "series"
if alphaScale then chartvis.properties.update[colorField .. "Opacity"].field = "series" end
-- apply a grouping (facetting) transformation
chartvis =
{
}
}
-- for stacked charts apply a stacking transformation
if stacked then
table.insert(chartvis.from.transform, 1, { type = "stack", groupby = { "x" }, sortby = { "series" }, field = "y" } )
else
-- for bar charts the series are side-by-side grouped by x
if chartType == "rect" then
-- for bar charts with multiple series: each serie is grouped by the x value, therefore the series need their own scale within each x group
local groupScale =
{
local properties
if chartType == "rect" then
{
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 =
fontSize = { value = tonumber(showValues.fontsize) or 11 }
}
if properties.y.offset >= 0 then
properties.align.value =
properties.fill.value = showValues.fontcolor or "white"
else
properties.align.value =
properties.fill.value = showValues.fontcolor or "black"
end
elseif chartType == "pie" then
properties =
end
elseif tonumber(showValues.angle) then
-- qunatize scale for aligning text left on right half-circle and right on left half-circle
local alignScale = { name = "align", type = "quantize", domainMin = 0.0, domainMax = math.pi * 2, range = { "left", "right" } }
table.insert(scales, alignScale)
}
if chartType == "pie" then
-- move legend from center position to top
legend.properties = { legend = { y = { value = -outerRadius } } }
end
function p.chart(frame)
-- chart width and height
local graphwidth = tonumber(frame.args.width) or 200
local graphheight = tonumber(frame.args.height) or 200
-- chart type
local chartType = frame.args.type or "line"
--
local interpolate = frame.args.interpolate
-- mark colors (if no colors are given, the default 10 color palette is used)
local colors = stringArray(frame.args.colors)
-- for line charts, the thickness of the line; for pie charts the gap between each slice
local linewidth = tonumber(frame.args.linewidth)
-- x and y axis caption
local xTitle = frame.args.xAxisTitle
local yTitle = frame.args.yAxisTitle
-- x and y value types
local xType = frame.args.xType
local yType = frame.args.yType
-- override x and y axis minimum and maximum
local xMin = frame.args.xAxisMin
local xMax = frame.args.xAxisMax
local yMin = frame.args.yAxisMin
local yMax = frame.args.yAxisMax
-- override x and y axis label formatting
local xAxisFormat = frame.args.xAxisFormat
local yAxisFormat = frame.args.yAxisFormat
-- show legend with given title
local legendTitle = frame.args.legend
-- show values as text
local showValues = frame.args.showValues
-- pie chart radiuses
local innerRadius = tonumber(frame.args.innerRadius) or 0
local outerRadius = math.min(graphwidth, graphheight)
-- format JSON output
local formatJson = frame.args.formatjson
-- get x values
local x
x, xType, xMin, xMax = deserializeXData(frame.args.x, xType, xMin, xMax)
-- get y values (series)
local yValues = {}
local seriesTitles = {}
if yNum then
yValues[yNum] = value
--
seriesTitles[yNum] = frame.args["y" .. yNum .. "Title"] or name
end
end
y, yType, yMin, yMax = deserializeYData(yValues, yType, yMin, yMax)
-- create data tuples, consisting of series index, x value, y value
local data
if chartType == "pie" then
-- for pie charts the second second series is merged into the first series as radius values
data = convertXYToSingleSeries(x, y, xType, yType, { "y", "r" })
else
end
-- configure stacked charts
local stacked = false
local stats
if string.sub(chartType, 1, 7) == "stacked" then
chartType = string.sub(chartType, 8)
if #y > 1 then --
-- aggregate data by cumulative y values
{
name = "stats", source = "chart", transform =
{
{
type = "aggregate",
groupby = { "x" },
summarize = { y = "sum" }
}
}
}
end
end
-- create scales
local scales = {}
end
-- decide if lines (strokes) or areas (fills) should be drawn
local colorField
if chartType == "line" then colorField = "stroke" else colorField = "fill" end
-- create chart markings
local chartvis = getChartVisualisation(chartType, stacked, colorField, #y, innerRadius, outerRadius, linewidth, alphaScale, radiusScale, interpolate)
-- text marks
local textmarks
if showValues then
if type(showValues) == "string" then --
local keyValues = mw.text.split(showValues, "%s*,%s*")
showValues = {}
end
--
local xAxis, yAxis = getAxes(xTitle, xAxisFormat, xType, yTitle, yAxisFormat, yType, chartType)
--
local legend
if legendTitle then legend = getLegend(legendTitle, chartType, outerRadius) end
-- construct final output object
local output =
{
function p.chartWrapper(frame)
return p.chart(frame:getParent())
end
-- Given an HTML-encoded title as first argument, e.g. one produced with {{ARTICLEPAGENAME}},
-- convert it into a properly URL path-encoded string
-- This function is critical for any graph that uses path-based APIs, e.g. PageViews graph
function p.encodeTitleForPath(frame)
return mw.uri.encode(mw.text.decode(mw.text.trim(frame.args[1])), 'PATH')
end
|
редагувань