Rabu, 28 Oktober 2026
08:11 WIB
TERKINI
Bandar Lampung Harga Singkong Lampung Meroket, Petani Nikmati Keuntungan Rp45 Juta per Hektare Bandar Lampung BSU 2026 Segera Disalurkan: Rp900 Ribu dan Analisis Pakar Terkait Tantangan Pekerja Bandar Lampung Optimisme Harga Singkong Lampung 2027: Bibit dan Pupuk Kunci Utama Bandar Lampung Krisis Asap dan Ribuan Titik Panas: WALHI Desak Audit Ekologis TNWK Bandar Lampung Polda Lampung Luncurkan Operasi Patuh Krakatau 2026: 702 Personel Siap Tingkatkan Disiplin Lalu Lintas Bandar Lampung Pembakaran Sampah Diduga Picu Kebakaran Lahan di Panjang Bandar Lampung Bandar Lampung Waspada Hujan Ringan: Prakiraan Cuaca Lampung Sepanjang 24 Oktober 2026 Bandar Lampung Penundaan Pembahasan APBD Lampung 2027: Berkas dan Data Terkini Jadi Kunci Bandar Lampung Fokus Pendidikan dan Ekonomi Desa, Lampung Matangkan APBD 2027 Bandar Lampung Polisi Hadir di Jalanan: Ratusan Makan Siang Gratis Eratkan Hubungan Warga Bandar Lampung Bandar Lampung Harga Singkong Lampung Meroket, Petani Nikmati Keuntungan Rp45 Juta per Hektare Bandar Lampung BSU 2026 Segera Disalurkan: Rp900 Ribu dan Analisis Pakar Terkait Tantangan Pekerja Bandar Lampung Optimisme Harga Singkong Lampung 2027: Bibit dan Pupuk Kunci Utama Bandar Lampung Krisis Asap dan Ribuan Titik Panas: WALHI Desak Audit Ekologis TNWK Bandar Lampung Polda Lampung Luncurkan Operasi Patuh Krakatau 2026: 702 Personel Siap Tingkatkan Disiplin Lalu Lintas Bandar Lampung Pembakaran Sampah Diduga Picu Kebakaran Lahan di Panjang Bandar Lampung Bandar Lampung Waspada Hujan Ringan: Prakiraan Cuaca Lampung Sepanjang 24 Oktober 2026 Bandar Lampung Penundaan Pembahasan APBD Lampung 2027: Berkas dan Data Terkini Jadi Kunci Bandar Lampung Fokus Pendidikan dan Ekonomi Desa, Lampung Matangkan APBD 2027 Bandar Lampung Polisi Hadir di Jalanan: Ratusan Makan Siang Gratis Eratkan Hubungan Warga Bandar Lampung

Modul:Graph

Module with auxilary functions for Graph extension

Bagikan:
Dokumentasi modulbuatsegarkan
Anda dapat membuat halaman dokumentasi untuk buat pada modul Scribunto ini.
Penyunting dapat melakukan uji coba pada halaman bak pasir (buat | cermin) dan kasus uji (buat) modul ini.
Subhalaman modul ini.
-- ATTENTION:	Please edit this code at https://de.wikipedia.org/wiki/Modul:Graph
--          	This way all wiki languages can stay in sync. Thank you!
--
--	BUGS:	X-Axis label format bug? (xAxisFormat =) https://en.wikipedia.org/wiki/Template_talk:Graph:Chart#X-Axis_label_format_bug?_(xAxisFormat_=)
--			linewidths - doesnt work for two values (eg 0, 1) but work if added third value of both are zeros? Same for marksStroke - probably bug in Graph extension
--			clamp - "clamp" used to avoid marks outside marks area, "clip" should be use instead but not working in Graph extension, see https://phabricator.wikimedia.org/T251709
--	TODO:  
--			marks:
--				- line strokeDash + serialization,
--				- symStroke serialization
--				- symbolsNoFill serialization
--				- arbitrary SVG path symbol shape as symbolsShape argument
--				- annotations
--					- vertical / horizontal line at specific values [DONE] 2020-09-01
--					- rectangle shape for x,y data range
--				- graph type serialization (deep rebuild reqired)
--	     - second axis (deep rebuild required - assignment of series to one of two axies) 
-- Version History (_PLEASE UPDATE when modifying anything_):
--   2020-09-01 Vertical and horizontal line annotations
--   2020-08-08 New logic for "nice" for x axis (problem with scale when xType = "date") and grid
--   2020-06-21 Serializes symbol size
--              transparent symbosls (from line colour) - buggy (incorrect opacity on overlap with line)
--              Linewidth serialized with "linewidths"
--              Variable symbol size and shape of symbols on line charts, default showSymbols = 2, default symbolsShape = circle, symbolsStroke = 0
--              p.chartDebuger(frame) for easy debug and JSON output 
--   2020-06-07 Allow lowercase variables for use with [[Template:Wikidata list]]
--   2020-05-27 Map: allow specification which feature to display and changing the map center
--   2020-04-08 Change default showValues.fontcolor from black to persistentGrey
--   2020-04-06 Logarithmic scale outputs wrong axis labels when "nice"=true
--   2020-03-11 Allow user-defined scale types, e.g. logarithmic scale
--   2019-11-08 Apply color-inversion-friendliness to legend title, labels, and xGrid
--   2019-01-24 Allow comma-separated lists to contain values with commas
--   2018-10-13 Fix browser color-inversion issues via #54595d per [[mw:Template:Graph:PageViews]]
--   2018-09-16 Allow disabling the legend for templates
--   2018-09-10 Allow grid lines
--   2018-08-26 Use user-defined order for stacked charts
--   2018-02-11 Force usage of explicitely provided x minimum and/or maximum values, rotation of x labels
--   2017-08-08 Added showSymbols param to show symbols on line charts
--   2016-05-16 Added encodeTitleForPath() to help all path-based APIs graphs like pageviews
--   2016-03-20 Allow omitted data for charts, labels for line charts with string (ordinal) scale at point location
--   2016-01-28 For maps, always use wikiraw:// protocol. https:// will be disabled soon.
localp={}
--add debug text to this string with eg. 	debuglog = debuglog .. "" .. "\n\n"  .. "- " .. debug.traceback() .. "result type: ".. type(result) ..  " result: \n\n" .. mw.dumpObject(result) 
--invoke chartDebuger() to get graph JSON and this string
debuglog="Debug ".."\n\n"
localbaseMapDirectory="Module:Graph/"
localpersistentGrey="#54595d"
localshapes={}
shapes={
circle="circle",x="M-.5,-.5L.5,.5M.5,-.5L-.5,.5",square="square",
cross="cross",diamond="diamond",triangle_up="triangle-up",
triangle_down="triangle-down",triangle_right="triangle-right",
triangle_left="triangle-left",
banana="m -0.5281,0.2880 0.0020,0.0192 m 0,0 c 0.1253,0.0543 0.2118,0.0679 0.3268,0.0252 0.1569,-0.0582 0.3663,-0.1636 0.4607,-0.3407 0.0824,-0.1547 0.1202,-0.2850 0.0838,-0.4794 l 0.0111,-0.1498 -0.0457,-0.0015 c -0.0024,0.3045 -0.1205,0.5674 -0.3357,0.7414 -0.1409,0.1139 -0.3227,0.1693 -0.5031,0.1856 m 0,0 c 0.1804,-0.0163 0.3622,-0.0717 0.5031,-0.1856 0.2152,-0.1739 0.3329,-0.4291 0.3357,-0.7414 l -0.0422,0.0079 c 0,0 -0.0099,0.1111 -0.0227,0.1644 -0.0537,0.1937 -0.1918,0.3355 -0.3349,0.4481 -0.1393,0.1089 -0.2717,0.2072 -0.4326,0.2806 l -0.0062,0.0260"
}
localfunctionnumericArray(csv)
ifnotcsvthenreturnend
locallist=mw.text.split(csv,"%s*,%s*")
localresult={}
localisInteger=true
fori=1,#listdo
iflist[i]==""then
result[i]=nil
else
result[i]=tonumber(list[i])
ifnotresult[i]thenreturnend
ifisIntegerthen
localint,frac=math.modf(result[i])
isInteger=frac==0.0
end
end
end
returnresult,isInteger
end
localfunctionstringArray(text)
ifnottextthenreturnend
locallist=mw.text.split(mw.ustring.gsub(tostring(text),"\\,","<COMMA>"),",",true)
fori=1,#listdo
list[i]=mw.ustring.gsub(mw.text.trim(list[i]),"<COMMA>",",")
end
returnlist
end
localfunctionisTable(t)returntype(t)=="table"end
localfunctioncopy(x)
iftype(x)=="table"then
localresult={}
forkey,valueinpairs(x)doresult[key]=copy(value)end
returnresult
else
returnx
end
end
functionp.map(frame)
-- map path data for geographic objects
localbasemap=frame.args.basemapor"Template:Graph:Map/Inner/Worldmap2c-json"-- WorldMap name and/or location may vary from wiki to wiki
-- scaling factor
localscale=tonumber(frame.args.scale)or100
-- map projection, see https://github.com/mbostock/d3/wiki/Geo-Projections
localprojection=frame.args.projectionor"equirectangular"
-- defaultValue for geographic objects without data
localdefaultValue=frame.args.defaultValueorframe.args.defaultvalue
localscaleType=frame.args.scaleTypeorframe.args.scaletypeor"linear"
-- minimaler Wertebereich (nur für numerische Daten)
localdomainMin=tonumber(frame.args.domainMinorframe.args.domainmin)
-- maximaler Wertebereich (nur für numerische Daten)
localdomainMax=tonumber(frame.args.domainMaxorframe.args.domainmax)
-- Farbwerte der Farbskala (nur für numerische Daten)
localcolorScale=frame.args.colorScaleorframe.args.colorscaleor"category10"
-- show legend
locallegend=frame.args.legend
-- the map feature to display
localfeature=frame.args.featureor"countries"
-- map center
localcenter=numericArray(frame.args.center)
-- format JSON output
localformatJson=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
localvalues={}
localisNumbers=nil
forname,valueinpairs(frame.args)do
ifmw.ustring.find(name,"^[^%l]+$")andvalueandvalue~=""then
ifisNumbers==nilthenisNumbers=tonumber(value)end
localdata={id=name,v=value}
ifisNumbersthendata.v=tonumber(data.v)end
table.insert(values,data)
end
end
ifnotdefaultValuethen
ifisNumbersthendefaultValue=0elsedefaultValue="silver"end
end
-- create highlight scale
localscales
ifisNumbersthen
ifcolorScalethencolorScale=string.lower(colorScale)end
ifcolorScale=="category10"orcolorScale=="category20"thenelsecolorScale=stringArray(colorScale)end
scales=
{
{
name="color",
type=scaleType,
domain={data="highlights",field="v"},
range=colorScale,
nice=true,
zero=false
}
}
ifdomainMinthenscales[1].domainMin=domainMinend
ifdomainMaxthenscales[1].domainMax=domainMaxend
localexponent=string.match(scaleType,"pow%s+(%d+%.?%d+)")-- check for exponent
ifexponentthen
scales[1].type="pow"
scales[1].exponent=exponent
end
end
-- create legend
iflegendthen
legend=
{
{
fill="color",
offset=120,
properties=
{
title={fontSize={value=14}},
labels={fontSize={value=12}},
legend=
{
stroke={value="silver"},
strokeWidth={value=1.5}
}
}
}
}
end
-- get map url
localbasemapUrl
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.
ifnotstring.find(basemap,":")thenbasemap=baseMapDirectory..basemapend
basemapUrl="wikiraw:///"..mw.uri.encode(mw.title.new(basemap).prefixedText,"PATH")
end
localoutput=
{
version=2,
width=1,-- generic value as output size depends solely on map size and scaling factor
height=1,-- ditto
data=
{
{
-- data source for the highlights
name="highlights",
values=values
},
{
-- data source for map paths data
name=feature,
url=basemapUrl,
format={type="topojson",feature=feature},
transform=
{
{
-- geographic transformation ("geopath") of map paths data
type="geopath",
value="data",-- data source
scale=scale,
translate={0,0},
center=center,
projection=projection
},
{
-- join ("zip") of mutiple data source: here map paths data and highlights
type="lookup",
keys={"id"},-- key for map paths data
on="highlights",-- name of highlight data source
onKey="id",-- key for highlight data source
as={"zipped"},-- name of resulting table
default={v=defaultValue}-- default value for geographic objects that could not be joined
}
}
}
},
marks=
{
-- output markings (map paths and highlights)
{
type="path",
from={data=feature},
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
localflags
ifformatJsonthenflags=mw.text.JSON_PRETTYend
returnmw.text.jsonEncode(output,flags)
end
localfunctiondeserializeXData(serializedX,xType,xMin,xMax)
localx
ifnotxTypeorxType=="integer"orxType=="number"then
localisInteger
x,isInteger=numericArray(serializedX)
ifxthen
xMin=tonumber(xMin)
xMax=tonumber(xMax)
ifnotxTypethen
ifisIntegerthenxType="integer"elsexType="number"end
end
else
ifxTypethenerror("Numbers expected for parameter 'x'")end
end
end
ifnotxthen
x=stringArray(serializedX)
ifnotxTypethenxType="string"end
end
returnx,xType,xMin,xMax
end
localfunctiondeserializeYData(serializedYs,yType,yMin,yMax)
localy={}
localareAllInteger=true
foryNum,valueinpairs(serializedYs)do
localyValues
ifnotyTypeoryType=="integer"oryType=="number"then
localisInteger
yValues,isInteger=numericArray(value)
ifyValuesthen
areAllInteger=areAllIntegerandisInteger
else
ifyTypethen
error("Numbers expected for parameter '"..name.."'")
else
returndeserializeYData(serializedYs,"string",yMin,yMax)
end
end
end
ifnotyValuesthenyValues=stringArray(value)end
y[yNum]=yValues
end
ifnotyTypethen
ifareAllIntegerthenyType="integer"elseyType="number"end
end
ifyType=="integer"oryType=="number"then
yMin=tonumber(yMin)
yMax=tonumber(yMax)
end
returny,yType,yMin,yMax
end
localfunctionconvertXYToManySeries(x,y,xType,yType,seriesTitles)
localdata=
{
name="chart",
format=
{
type="json",
parse={x=xType,y=yType}
},
values={}
}
fori=1,#ydo
localyLen=table.maxn(y[i])
forj=1,#xdo
ifj<=yLenandy[i][j]thentable.insert(data.values,{series=seriesTitles[i],x=x[j],y=y[i][j]})end
end
end
returndata
end
localfunctionconvertXYToSingleSeries(x,y,xType,yType,yNames)
localdata={name="chart",format={type="json",parse={x=xType}},values={}}
forj=1,#ydodata.format.parse[yNames[j]]=yTypeend
fori=1,#xdo
localitem={x=x[i]}
forj=1,#ydoitem[yNames[j]]=y[j][i]end
table.insert(data.values,item)
end
returndata
end
localfunctiongetXScale(chartType,stacked,xMin,xMax,xType,xScaleType)
ifchartType=="pie"thenreturnend
localxscale=
{
name="x",
range="width",
zero=false,-- do not include zero value
domain={data="chart",field="x"}
}
ifxScaleTypethenxscale.type=xScaleTypeelsexscale.type="linear"end
ifxMinthenxscale.domainMin=xMinend
ifxMaxthenxscale.domainMax=xMaxend
ifxMinorxMaxthen
xscale.clamp=true
xscale.nice=false
end
ifchartType=="rect"then
xscale.type="ordinal"
ifnotstackedthenxscale.padding=0.2end-- pad each bar group
else
ifxType=="date"then
xscale.type="time"
elseifxType=="string"then
xscale.type="ordinal"
xscale.points=true
end
end
ifxTypeandxType~="date"andxScaleType~="log"thenxscale.nice=trueend-- force round numbers for x scale, but "log" and "date" scale outputs a wrong "nice" scale
returnxscale
end
localfunctiongetYScale(chartType,stacked,yMin,yMax,yType,yScaleType)
ifchartType=="pie"thenreturnend
localyscale=
{
name="y",
--type = yScaleType or "linear",
range="height",
-- area charts have the lower boundary of their filling at y=0 (see marks.properties.enter.y2), therefore these need to start at zero
zero=chartType~="line",
nice=yScaleType~="log"-- force round numbers for y scale, but log scale outputs a wrong "nice" scale
}
ifyScaleTypethenyscale.type=yScaleTypeelseyscale.type="linear"end
ifyMinthenyscale.domainMin=yMinend
ifyMaxthenyscale.domainMax=yMaxend
ifyMinoryMaxthenyscale.clamp=trueend
ifyType=="date"thenyscale.type="time"
elseifyType=="string"thenyscale.type="ordinal"end
ifstackedthen
yscale.domain={data="stats",field="sum_y"}
else
yscale.domain={data="chart",field="y"}
end
returnyscale
end
localfunctiongetColorScale(colors,chartType,xCount,yCount)
ifnotcolorsthen
if(chartType=="pie"andxCount>10)oryCount>10thencolors="category20"elsecolors="category10"end
end
localcolorScale=
{
name="color",
type="ordinal",
range=colors,
domain={data="chart",field="series"}
}
ifchartType=="pie"thencolorScale.domain.field="x"end
returncolorScale
end
localfunctiongetAlphaColorScale(colors,y)
localalphaScale
-- if there is at least one color in the format "#aarrggbb", create a transparency (alpha) scale
ifisTable(colors)then
localalphas={}
localhasAlpha=false
fori=1,#colorsdo
locala,rgb=string.match(colors[i],"#(%x%x)(%x%x%x%x%x%x)")
ifathen
hasAlpha=true
alphas[i]=tostring(tonumber(a,16)/255.0)
colors[i]="#"..rgb
else
alphas[i]="1"
end
end
fori=#colors+1,#ydoalphas[i]="1"end
ifhasAlphathenalphaScale={name="transparency",type="ordinal",range=alphas}end
end
returnalphaScale
end
localfunctiongetLineScale(linewidths,chartType)
locallineScale={}
lineScale=
{
name="line",
type="ordinal",
range=linewidths,
domain={data="chart",field="series"}
}
returnlineScale
end
localfunctiongetSymSizeScale(symSize)
localSymSizeScale={}
SymSizeScale=
{
name="symSize",
type="ordinal",
range=symSize,
domain={data="chart",field="series"}
}
returnSymSizeScale
end
localfunctiongetSymShapeScale(symShape)
localSymShapeScale={}
SymShapeScale=
{
name="symShape",
type="ordinal",
range=symShape,
domain={data="chart",field="series"}
}
returnSymShapeScale
end
localfunctiongetValueScale(fieldName,min,max,type)
localvalueScale=
{
name=fieldName,
type=typeor"linear",
domain={data="chart",field=fieldName},
range={min,max}
}
returnvalueScale
end
localfunctionaddInteractionToChartVisualisation(plotMarks,colorField,dataField)
-- initial setup
ifnotplotMarks.properties.enterthenplotMarks.properties.enter={}end
plotMarks.properties.enter[colorField]={scale="color",field=dataField}
-- action when cursor is over plot mark: highlight
ifnotplotMarks.properties.hoverthenplotMarks.properties.hover={}end
plotMarks.properties.hover[colorField]={value="red"}
-- action when cursor leaves plot mark: reset to initial setup
ifnotplotMarks.properties.updatethenplotMarks.properties.update={}end
plotMarks.properties.update[colorField]={scale="color",field=dataField}
end
localfunctiongetPieChartVisualisation(yCount,innerRadius,outerRadius,linewidth,radiusScale)
localchartvis=
{
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=linewidthor1}
}
}
}
ifradiusScalethen
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")
returnchartvis
end
localfunctiongetChartVisualisation(chartType,stacked,colorField,yCount,innerRadius,outerRadius,linewidth,alphaScale,radiusScale,lineScale,interpolate)
ifchartType=="pie"thenreturngetPieChartVisualisation(yCount,innerRadius,outerRadius,linewidth,radiusScale)end
localchartvis=
{
type=chartType,
properties=
{
-- chart creation event handler
enter=
{
x={scale="x",field="x"},
y={scale="y",field="y"}
}
}
}
addInteractionToChartVisualisation(chartvis,colorField,"series")
ifcolorField=="stroke"then
chartvis.properties.enter.strokeWidth={value=linewidthor2.5}
iftype(lineScale)=="table"then
chartvis.properties.enter.strokeWidth.value=nil
chartvis.properties.enter.strokeWidth=
{
scale="line",
field="series"
}
end
end
ifinterpolatethenchartvis.properties.enter.interpolate={value=interpolate}end
ifalphaScalethenchartvis.properties.update[colorField.."Opacity"]={scale="transparency"}end
-- for bars and area charts set the lower bound of their areas
ifchartType=="rect"orchartType=="area"then
ifstackedthen
-- 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
			TODO: "yscale.zero" is currently set to "true" for this case, but "false" for all other cases.
			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 ...
ifchartType=="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
ifnotstackedandyCount>1then
chartvis.properties.enter.x.scale="series"
chartvis.properties.enter.x.field="series"
chartvis.properties.enter.width.scale="series"
end
end
-- stacked charts have their own (stacked) y values
ifstackedthenchartvis.properties.enter.y.field="layout_start"end
-- if there are multiple series group these together
ifyCount==1then
chartvis.from={data="chart"}
else
-- if there are multiple series, connect colors to series
chartvis.properties.update[colorField].field="series"
ifalphaScalethenchartvis.properties.update[colorField.."Opacity"].field="series"end
-- if there are multiple series, connect linewidths to series
ifchartype=="line"then
chartvis.properties.update["strokeWidth"].field="series"
end
-- apply a grouping (facetting) transformation
chartvis=
{
type="group",
marks={chartvis},
from=
{
data="chart",
transform=
{
{
type="facet",
groupby={"series"}
}
}
}
}
-- for stacked charts apply a stacking transformation
ifstackedthen
table.insert(chartvis.from.transform,1,{type="stack",groupby={"x"},sortby={"-_id"},field="y"})
else
-- for bar charts the series are side-by-side grouped by x
ifchartType=="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
localgroupScale=
{
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
returnchartvis
end
localfunctiongetTextMarks(chartvis,chartType,outerRadius,scales,radiusScale,yType,showValues)
localproperties
ifchartType=="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 }, -- for horizontal text
dy={scale=chartvis.properties.enter.x.scale,band=true,mult=0.5},-- for vertical text
align={},
baseline={value="middle"},
fill={},
angle={value=-90},
fontSize={value=tonumber(showValues.fontsize)or11}
}
ifproperties.y.offset>=0then
properties.align.value="right"
properties.fill.value=showValues.fontcoloror"white"
else
properties.align.value="left"
properties.fill.value=showValues.fontcolororpersistentGrey
end
elseifchartType=="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.fontcolororpersistentGrey},
baseline={},
angle={},
fontSize={value=tonumber(showValues.fontsize)ormath.ceil(outerRadius/10)}
}
if(showValues.angleor"midangle")=="midangle"then
properties.align={value="center"}
properties.angle={field="layout_mid",mult=180.0/math.pi}
ifproperties.radius.offset>=0then
properties.baseline.value="bottom"
else
ifnotshowValues.fontcolorthenproperties.fill.value="white"end
properties.baseline.value="top"
end
elseiftonumber(showValues.angle)then
-- qunatize scale for aligning text left on right half-circle and right on left half-circle
localalignScale={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"
ifnottonumber(showValues.offset)thenproperties.radius.offset=4end
end
ifradiusScalethen
properties.radius.scale=radiusScale.name
properties.radius.field=radiusScale.domain.field
else
properties.radius.value=outerRadius
end
end
ifpropertiesthen
ifshowValues.formatthen
localtemplate="datum.y"
ifyType=="integer"oryType=="number"thentemplate=template.."|number:'"..showValues.format.."'"
elseifyType=="date"thentemplate=template.."|time:"..showValues.format.."'"
end
properties.text={template="{{"..template.."}}"}
else
properties.text={field="y"}
end
localtextmarks=
{
type="text",
properties=
{
enter=properties
}
}
ifchartvis.fromthentextmarks.from=copy(chartvis.from)end
returntextmarks
end
end
localfunctiongetSymbolMarks(chartvis,symSize,symShape,symStroke,noFill,alphaScale)
localsymbolmarks
symbolmarks=
{
type="symbol",
properties=
{
enter=
{
x={scale="x",field="x"},
y={scale="y",field="y"},
strokeWidth={value=symStroke},
stroke={scale="color",field="series"},
fill={scale="color",field="series"},
}
}
}
iftype(symShape)=="string"then
symbolmarks.properties.enter.shape={value=symShape}
end
iftype(symShape)=="table"then
symbolmarks.properties.enter.shape={scale="symShape",field="series"}
end
iftype(symSize)=="number"then
symbolmarks.properties.enter.size={value=symSize}
end
iftype(symSize)=="table"then
symbolmarks.properties.enter.size={scale="symSize",field="series"}
end
ifnoFillthen
symbolmarks.properties.enter.fill=nil
end
ifalphaScalethen
symbolmarks.properties.enter.fillOpacity=
{scale="transparency",field="series"}
symbolmarks.properties.enter.strokeOpacity=
{scale="transparency",field="series"}
end
ifchartvis.fromthensymbolmarks.from=copy(chartvis.from)end
returnsymbolmarks
end
localfunctiongetAnnoMarks(chartvis,stroke,fill,opacity)
localvannolines,hannolines,vannoLabels,vannoLabels
vannolines=
{
type="rule",
from={data="v_anno"},
properties=
{
update=
{
x={scale="x",field="x"},
y={value=0},
y2={field={group="height"}},
strokeWidth={value=stroke},
stroke={value=persistentGrey},
opacity={value=opacity}
}
}
}
vannolabels=
{
type="text",
from={data="v_anno"},
properties=
{
update=
{
x={scale="x",field="x",offset=3},
y={field={group="height"},offset=-3},
text={field="label"},
baseline={value="top"},
angle={value=-90},
fill={value=persistentGrey},
opacity={value=opacity}
}
}
}
hannolines=
{
type="rule",
from={data="h_anno"},
properties=
{
update=
{
y={scale="y",field="y"},
x={value=0},
x2={field={group="width"}},
strokeWidth={value=stroke},
stroke={value=persistentGrey},
opacity={value=opacity}
}
}
}
hannolabels=
{
type="text",
from={data="h_anno"},
properties=
{
update=
{
y={scale="y",field="y",offset=3},
x={value=0,offset=3},
text={field="label"},
baseline={value="top"},
angle={value=0},
fill={value=persistentGrey},
opacity={value=opacity}
}
}
}
returnvannolines,vannolabels,hannolines,hannolabels
end
localfunctiongetAxes(xTitle,xAxisFormat,xAxisAngle,xType,xGrid,yTitle,yAxisFormat,yType,yGrid,chartType)
localxAxis,yAxis
ifchartType~="pie"then
ifxType=="integer"andnotxAxisFormatthenxAxisFormat="d"end
xAxis=
{
type="x",
scale="x",
title=xTitle,
format=xAxisFormat,
grid=xGrid
}
ifxAxisAnglethen
localxAxisAlign
ifxAxisAngle<0thenxAxisAlign="right"elsexAxisAlign="left"end
xAxis.properties=
{
title=
{
fill={value=persistentGrey}
},
labels=
{
angle={value=xAxisAngle},
align={value=xAxisAlign},
fill={value=persistentGrey}
},
ticks=
{
stroke={value=persistentGrey}
},
axis=
{
stroke={value=persistentGrey},
strokeWidth={value=2}
},
grid=
{
stroke={value=persistentGrey}
}
}
else
xAxis.properties=
{
title=
{
fill={value=persistentGrey}
},
labels=
{
fill={value=persistentGrey}
},
ticks=
{
stroke={value=persistentGrey}
},
axis=
{
stroke={value=persistentGrey},
strokeWidth={value=2}
},
grid=
{
stroke={value=persistentGrey}
}
}
end
ifyType=="integer"andnotyAxisFormatthenyAxisFormat="d"end
yAxis=
{
type="y",
scale="y",
title=yTitle,
format=yAxisFormat,
grid=yGrid
}
yAxis.properties=
{
title=
{
fill={value=persistentGrey}
},
labels=
{
fill={value=persistentGrey}
},
ticks=
{
stroke={value=persistentGrey}
},
axis=
{
stroke={value=persistentGrey},
strokeWidth={value=2}
},
grid=
{
stroke={value=persistentGrey}
}
}
end
returnxAxis,yAxis
end
localfunctiongetLegend(legendTitle,chartType,outerRadius)
locallegend=
{
fill="color",
stroke="color",
title=legendTitle,
}
legend.properties={
title={
fill={value=persistentGrey},
},
labels={
fill={value=persistentGrey},
},
}
ifchartType=="pie"then
legend.properties={
-- move legend from center position to top
legend={
y={value=-outerRadius},
},
title={
fill={value=persistentGrey}
},
labels={
fill={value=persistentGrey},
},
}
end
returnlegend
end
functionp.chart(frame)
-- chart width and height
localgraphwidth=tonumber(frame.args.width)or200
localgraphheight=tonumber(frame.args.height)or200
-- chart type
localchartType=frame.args.typeor"line"
-- interpolation mode for line and area charts: 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
localinterpolate=frame.args.interpolate
-- mark colors (if no colors are given, the default 10 color palette is used)
localcolorString=frame.args.colors
ifcolorStringthencolorString=string.lower(colorString)end
localcolors=stringArray(colorString)
-- for line charts, the thickness of the line; for pie charts the gap between each slice
locallinewidth=tonumber(frame.args.linewidth)
locallinewidthsString=frame.args.linewidths
locallinewidths
iflinewidthsStringandlinewidthsString~=""thenlinewidths=numericArray(linewidthsString)orfalseend
-- x and y axis caption
localxTitle=frame.args.xAxisTitleorframe.args.xaxistitle
localyTitle=frame.args.yAxisTitleorframe.args.yaxistitle
-- x and y value types
localxType=frame.args.xTypeorframe.args.xtype
localyType=frame.args.yTypeorframe.args.ytype
-- override x and y axis minimum and maximum
localxMin=frame.args.xAxisMinorframe.args.xaxismin
localxMax=frame.args.xAxisMaxorframe.args.xaxismax
localyMin=frame.args.yAxisMinorframe.args.yaxismin
localyMax=frame.args.yAxisMaxorframe.args.yaxismax
-- override x and y axis label formatting
localxAxisFormat=frame.args.xAxisFormatorframe.args.xaxisformat
localyAxisFormat=frame.args.yAxisFormatorframe.args.yaxisformat
localxAxisAngle=tonumber(frame.args.xAxisAngle)ortonumber(frame.args.xaxisangle)
-- x and y scale types
localxScaleType=frame.args.xScaleTypeorframe.args.xscaletype
localyScaleType=frame.args.yScaleTypeorframe.args.yscaletype
-- log scale require minimum > 0, for now it's no possible to plot negative values on log - TODO see: https://www.mathworks.com/matlabcentral/answers/1792-log-scale-graphic-with-negative-value
--	if xScaleType == "log" then
--		if (not xMin or tonumber(xMin) <= 0) then xMin = 0.1 end
--		if not xType then xType = "number" end
--	end
--	if yScaleType == "log" then
--		if (not yMin or tonumber(yMin) <= 0) then yMin = 0.1 end
--		if not yType then yType = "number" end
--	end
-- show grid
localxGrid=frame.args.xGridorframe.args.xgridorfalse
localyGrid=frame.args.yGridorframe.args.ygridorfalse
-- for line chart, show a symbol at each data point
localshowSymbols=frame.args.showSymbolsorframe.args.showsymbols
localsymbolsShape=frame.args.symbolsShapeorframe.args.symbolsshape
localsymbolsNoFill=frame.args.symbolsNoFillorframe.args.symbolsnofill
localsymbolsStroke=tonumber(frame.args.symbolsStrokeorframe.args.symbolsstroke)
-- show legend with given title
locallegendTitle=frame.args.legend
-- show values as text
localshowValues=frame.args.showValuesorframe.args.showvalues
-- show v- and h-line annotations
localv_annoLineString=frame.args.vAnnotatonsLineorframe.args.vannotatonsline
localh_annoLineString=frame.args.hAnnotatonsLineorframe.args.hannotatonsline
localv_annoLabelString=frame.args.vAnnotatonsLabelorframe.args.vannotatonslabel
localh_annoLabelString=frame.args.hAnnotatonsLabelorframe.args.hannotatonslabel
-- decode annotations cvs
localv_annoLine,v_annoLabel,h_annoLine,h_annoLabel
ifv_annoLineStringandv_annoLineString~=""then
ifxType=="number"orxType=="integer"then
v_annoLine=numericArray(v_annoLineString)
else
v_annoLine=stringArray(v_annoLineString)
end
v_annoLabel=stringArray(v_annoLabelString)
end
ifh_annoLineStringandh_annoLineString~=""then
ifyType=="number"oryType=="integer"then
h_annoLine=numericArray(h_annoLineString)
else
h_annoLine=stringArray(h_annoLineString)
end
h_annoLabel=stringArray(h_annoLabelString)
end
-- pie chart radiuses
localinnerRadius=tonumber(frame.args.innerRadius)ortonumber(frame.args.innerradius)or0
localouterRadius=math.min(graphwidth,graphheight)
-- format JSON output
localformatJson=frame.args.formatjson
-- get x values
localx
x,xType,xMin,xMax=deserializeXData(frame.args.x,xType,xMin,xMax)
-- get y values (series)
localyValues={}
localseriesTitles={}
forname,valueinpairs(frame.args)do
localyNum
ifname=="y"thenyNum=1elseyNum=tonumber(string.match(name,"^y(%d+)$"))end
ifyNumthen
yValues[yNum]=value
-- name the series: default is "y<number>". Can be overwritten using the "y<number>Title" parameters.
seriesTitles[yNum]=frame.args["y"..yNum.."Title"]orframe.args["y"..yNum.."title"]orname
end
end
localy
y,yType,yMin,yMax=deserializeYData(yValues,yType,yMin,yMax)
-- create data tuples, consisting of series index, x value, y value
localdata
ifchartType=="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
data=convertXYToManySeries(x,y,xType,yType,seriesTitles)
end
-- configure stacked charts
localstacked=false
localstats
ifstring.sub(chartType,1,7)=="stacked"then
chartType=string.sub(chartType,8)
if#y>1then-- ignore stacked charts if there is only one series
stacked=true
-- aggregate data by cumulative y values
stats=
{
name="stats",source="chart",transform=
{
{
type="aggregate",
groupby={"x"},
summarize={y="sum"}
}
}
}
end
end
-- add annotations to data
localvannoData,hannoData
ifv_annoLinethen
vannoData={name="v_anno",format={type="json",parse={x=xType}},values={}}
fori=1,#v_annoLinedo
localitem={x=v_annoLine[i],label=v_annoLabel[i]}
table.insert(vannoData.values,item)
end
end
ifh_annoLinethen
hannoData={name="h_anno",format={type="json",parse={y=yType}},values={}}
fori=1,#h_annoLinedo
localitem={y=h_annoLine[i],label=h_annoLabel[i]}
table.insert(hannoData.values,item)
end
end
-- create scales
localscales={}
localxscale=getXScale(chartType,stacked,xMin,xMax,xType,xScaleType)
table.insert(scales,xscale)
localyscale=getYScale(chartType,stacked,yMin,yMax,yType,yScaleType)
table.insert(scales,yscale)
localcolorScale=getColorScale(colors,chartType,#x,#y)
table.insert(scales,colorScale)
localalphaScale=getAlphaColorScale(colors,y)
table.insert(scales,alphaScale)
locallineScale
if(linewidths)and(chartType=="line")then
lineScale=getLineScale(linewidths,chartType)
table.insert(scales,lineScale)
end
localradiusScale
ifchartType=="pie"and#y>1then
radiusScale=getValueScale("r",0,outerRadius)
table.insert(scales,radiusScale)
end
-- decide if lines (strokes) or areas (fills) should be drawn
localcolorField
ifchartType=="line"thencolorField="stroke"elsecolorField="fill"end
-- create chart markings
localchartvis=getChartVisualisation(chartType,stacked,colorField,#y,innerRadius,outerRadius,linewidth,alphaScale,radiusScale,lineScale,interpolate)
localmarks={chartvis}
-- text marks
ifshowValuesthen
iftype(showValues)=="string"then-- deserialize as table
localkeyValues=mw.text.split(showValues,"%s*,%s*")
showValues={}
for_,kvinipairs(keyValues)do
localkey,value=mw.ustring.match(kv,"^%s*(.-)%s*:%s*(.-)%s*$")
ifkeythenshowValues[key]=valueend
end
end
localchartmarks=chartvis
ifchartmarks.marksthenchartmarks=chartmarks.marks[1]end
localtextmarks=getTextMarks(chartmarks,chartType,outerRadius,scales,radiusScale,yType,showValues)
ifchartmarks~=chartvisthen
table.insert(chartvis.marks,textmarks)
else
table.insert(marks,textmarks)
end
end
-- grids
ifxGridthen
ifxGrid=="0"thenxGrid=false
elseifxGrid==0thenxGrid=false
elseifxGrid=="false"thenxGrid=false
elseifxGrid=="n"thenxGrid=false
elsexGrid=true
end
end
ifyGridthen
ifyGrid=="0"thenyGrid=false
elseifyGrid==0thenyGrid=false
elseifyGrid=="false"thenyGrid=false
elseifyGrid=="n"thenyGrid=false
elseyGrid=true
end
end
-- symbol marks
ifshowSymbolsandchartType~="rect"then
localchartmarks=chartvis
ifchartmarks.marksthenchartmarks=chartmarks.marks[1]end
iftype(showSymbols)=="string"then
ifshowSymbols==""thenshowSymbols=true
elseshowSymbols=numericArray(showSymbols)
end
else
showSymbols=tonumber(showSymbols)
end
-- custom size
localsymSize
iftype(showSymbols)=="number"then
symSize=tonumber(showSymbols*showSymbols*8.5)
elseiftype(showSymbols)=="table"then
symSize={}
fork,vinpairs(showSymbols)do
symSize[k]=v*v*8.5-- "size" acc to Vega syntax is area of symbol
end
else
symSize=50
end
-- symSizeScale 
localsymSizeScale={}
iftype(symSize)=="table"then
symSizeScale=getSymSizeScale(symSize)
table.insert(scales,symSizeScale)
end
-- custom shape
ifstringArray(symbolsShape)and#stringArray(symbolsShape)>1thensymbolsShape=stringArray(symbolsShape)end
localsymShape=" "
iftype(symbolsShape)=="string"andshapes[symbolsShape]then
symShape=shapes[symbolsShape]
elseiftype(symbolsShape)=="table"then
symShape={}
fork,vinpairs(symbolsShape)do
ifsymbolsShape[k]andshapes[symbolsShape[k]]then
symShape[k]=shapes[symbolsShape[k]]
else
symShape[k]="circle"
end
end
else
symShape="circle"
end
-- symShapeScale 
localsymShapeScale={}
iftype(symShape)=="table"then
symShapeScale=getSymShapeScale(symShape)
table.insert(scales,symShapeScale)
end
-- custom stroke
localsymStroke
if(type(symbolsStroke)=="number")then
symStroke=tonumber(symbolsStroke)
-- TODO symStroke serialization
--		elseif type(symbolsStroke) == "table" then 
--	 		symStroke = {}
--	 		for k, v in pairs(symbolsStroke) do
--                symStroke[k]=symbolsStroke[k]
--                		--always draw x with stroke
--				if symbolsShape[k] == "x" then symStroke[k] = 2.5 end
--always draw x with stroke
--				if symbolsNoFill[k] then symStroke[k] = 2.5 end
--            end
else
symStroke=0
--always draw x with stroke
ifsymbolsShape=="x"thensymStroke=2.5end
--always draw x with stroke
ifsymbolsNoFillthensymStroke=2.5end
end
--	TODO	-- symStrokeScale 
--	 	local symStrokeScale = {}
--		if type(symStroke) == "table" then
--			symStrokeScale = getSymStrokeScale(symStroke)
--			table.insert(scales, symStrokeScale)
--		end
localsymbolmarks=getSymbolMarks(chartmarks,symSize,symShape,symStroke,symbolsNoFill,alphaScale)
ifchartmarks~=chartvisthen
table.insert(chartvis.marks,symbolmarks)
else
table.insert(marks,symbolmarks)
end
end
localvannolines,vannolabels,hannolines,hannolabels=getAnnoMarks(chartmarks,persistentGrey,persistentGrey,0.75)
ifvannoDatathen
table.insert(marks,vannolines)
table.insert(marks,vannolabels)
end
ifhannoDatathen
table.insert(marks,hannolines)
table.insert(marks,hannolabels)
end
-- axes
localxAxis,yAxis=getAxes(xTitle,xAxisFormat,xAxisAngle,xType,xGrid,yTitle,yAxisFormat,yType,yGrid,chartType)
-- legend
locallegend
iflegendTitleandtonumber(legendTitle)~=0thenlegend=getLegend(legendTitle,chartType,outerRadius)end
-- construct final output object
localoutput=
{
version=2,
width=graphwidth,
height=graphheight,
data={data},
scales=scales,
axes={xAxis,yAxis},
marks=marks,
legends={legend}
}
ifvannoDatathentable.insert(output.data,vannoData)end
ifhannoDatathentable.insert(output.data,hannoData)end
ifstatsthentable.insert(output.data,stats)end
localflags
ifformatJsonthenflags=mw.text.JSON_PRETTYend
returnmw.text.jsonEncode(output,flags)
end
functionp.mapWrapper(frame)
returnp.map(frame:getParent())
end
functionp.chartWrapper(frame)
returnp.chart(frame:getParent())
end
functionp.chartDebuger(frame)
return"\n\nchart JSON\n "..p.chart(frame).." \n\n"..debuglog
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
functionp.encodeTitleForPath(frame)
returnmw.uri.encode(mw.text.decode(mw.text.trim(frame.args[1])),'PATH')
end
functionp.chartCompatible(frame)
localnot_chart_args={
"colors",
"xAxisMin",
"xAxisMax",
"yAxisMin",
"yAxisMax",
"xAxisAngle",
"xScaleType",
"yScaleType",
"linewidth",
"linewidths",
"showSymbols",
"symbolsShape",
"symbolsNoFill",
"symbolsStroke",
"showValues",
"innerRadius",
"xGrid",
"yGrid",
"annotations"
}
localpframe=frame:getParent()
localb=0
localt=0
fort=1,18,1do
ifpframe.args[not_chart_args[t]]then
break
else
b=b+1
end
t=t+1
end
ifb==18then
ifpframe.args["name"]then
return"[[Kategori:Grafik untuk Porting]]"
else
return"[[Kategori:Grafik yang perlu diporting dan membutuhkan nama]]"
end
end
end
returnp
Konten disalin dari Wikipedia Bahasa Indonesia (lisensi CC BY-SA) Lihat versi asli di Wikipedia

Rekomendasi Pilihan