Module:Graph
- Alemannisch
- Anarâškielâ
- العربية
- অসমীয়া
- Asturianu
- Azərbaycanca
- تۆرکجه
- Basa Bali
- বাংলা
- Башҡортса
- Беларуская
- Беларуская (тарашкевіца)
- भोजपुरी
- Български
- Bosanski
- Буряад
- Català
- Cebuano
- Čeština
- Cymraeg
- Dansk
- الدارجة
- Deutsch
- Eesti
- Ελληνικά
- Español
- Esperanto
- Euskara
- فارسی
- Føroyskt
- Français
- ગુજરાતી
- 한국어
- Հայերեն
- हिन्दी
- Hrvatski
- Ilokano
- Bahasa Indonesia
- Ирон
- Italiano
- עברית
- ಕನ್ನಡ
- Қазақша
- Kurdî
- Ladin
- Latviešu
- Lëtzebuergesch
- Lietuvių
- Magyar
- Македонски
- മലയാളം
- मराठी
- مصرى
- Bahasa Melayu
- Мокшень
- Монгол
- မြန်မာဘာသာ
- Nederlands
- नेपाली
- 日本語
- Nordfriisk
- Norsk bokmål
- Norsk nynorsk
- ଓଡ଼ିଆ
- Oʻzbekcha / ўзбекча
- Papiamentu
- پښتو
- Polski
- Português
- Qaraqalpaqsha
- Română
- Русский
- Shqip
- සිංහල
- Simple English
- Slovenčina
- Slovenščina
- کوردی
- Српски / srpski
- Suomi
- Svenska
- Tagalog
- தமிழ்
- တႆး
- ไทย
- ತುಳು
- Türkçe
- Українська
- اردو
- Vèneto
- Tiếng Việt
- Yorùbá
- 粵語
- 中文
See the protection policy and protection log for more details. Please discuss any changes on the talk page; you may submit an edit request to ask an administrator to make an edit if it is uncontroversial or supported by consensus. You may also request that this page be unprotected.
{{Graph}}-based diagrams to the newer Chart Extension format, you can use the simple script featured on MediaWiki at Commons: graphDataImport.js.| Related pages |
|---|
Module with helper functions for the Graph extension to display graphs and maps. From de:Modul:Graph.
Functions for templates
map
Creates a JSON object for <graph> to display a political map with colored highlights. In the article namespace the template {{Graph:Map }} should be used instead. See its page for use cases.
Maps can be found at Special:Prefixindex/Template:Graph:Map/Inner/ (for example Worldmap2c-json with country borders) and new maps should also be saved under Module:Graph/.
Parameters:
- basemap: sets the base map. The map definitions must follow the TopoJSON format and if saved in Wikipedia are available for this module. Maps in the default directory Special:Prefixindex/Template:Graph:Map/Inner/ like Worldmap2c-json should only be referenced by their name while omitting the Module:Graph/ prefix to allow better portability. The parameter also accepts URLs, e.g. maps from other Wikipedia versions (the link should follow the scheme of
//en.wikipedia.org/w/index.php?title=mapname&action=raw, i.e. protocol-relative without leading http/s and a trailing action=raw to fetch the raw content only). URLs to maps on external sites should be avoided for the sake of link stability, performance, security, and she be assumed to be blocked by the software or browser anyway. - scale: the scaling factor of the map (default: 100)
- projection: the map projection to use. Supported values are listed at https://github.com/d3/d3-geo-projection. The default value is
equirectangularfor an equirectangular projection. - center: map center (corresponds in the map data to both comma-separated values of the
scalefield) - feature: which geographic objects should be displayed (corresponds in the map data to the name of the field under the
objectsfield). The default is valuecountries. - ids of geographic entities: The actual parameter names depend on the base map and the selected feature. For example, for the above mentioned world map the ids are ISO country codes. The values can be either colors or numbers in case the geographic entities should be associated with numeric data:
DE=lightbluemarks Germany in light blue color, andDE=80.6assigns Germany the value 80.6 (population in millions). In the latter case, the actual color depends on the following parameters. - colorScale: the color palette to use for the color scale. The palette must be provided as a comma-separated list of color values. The color values must be given either as
#rgb/#rrggbbor by a CSS color name. Instead of a list, the built-in color palettescategory10andcategory20can also be used. - scaleType: supported values are
linearfor a linear mapping between the data values and the color scale,logfor a log mapping,powfor a power mapping (the exponent can be provided aspow 0.5),sqrtfor a square-root mapping, andquantizefor a quantized scale, i.e. the data is grouped in as many classes as the color palette has colors. - domainMin: lower boundary of the data values, i.e. smaller data values are mapped to the lower boundary
- domainMax: upper boundary of the data values, i.e. larger data values are mapped to the upper boundary
- legend: show color legend (does not work with
quantize) - defaultValue: default value for unused geographic entities. In case the id values are colors the default value is
silver, in case of numbers it is 0. - formatjson: format JSON object for better legibility
- title: The title of the graph, used by User:GraphBot when converting.
- name: The name of the graph, required by User:GraphBot to convert the graph.
chart
Creates a JSON object for <graph> to display charts. In the article namespace the template Template:Graph:Chart should be used instead. See its page for use cases.
Parameters:
- width: width of the chart
- height: height of the chart
- type: type of the chart:
linefor line charts,areafor area charts, andrectfor (column) bar charts, andpiefor pie charts. Multiple series can stacked using thestackedprefix, e.g.stackedarea. - interpolate: interpolation method for line and area charts. It is recommended to use
monotonefor a monotone cubic interpolation – further supported values are listed at https://github.com/nyurik/vega/wiki/Marks#line. - colors: color palette of the chart as a comma-separated list of colors. The color values must be given either as
#rgb/#rrggbb/#aarrggbbor by a CSS color name. For#aarrggbbtheaacomponent denotes the alpha channel, i.e. FF=100% opacity, 80=50% opacity/transparency, etc. (The default color palette if n <= 10 is Category10: else is Category20: ). See Template:ChartColors for details. - xAxisTitle and yAxisTitle: captions of the x and y axes
- xAxisMin, xAxisMax, yAxisMin, and yAxisMax: minimum and maximum values of the x and y axes (not yet supported for bar charts). These parameters can be used to invert the scale of a numeric axis by setting the lowest value to the Max and highest value to the Min.
- xAxisFormat and yAxisFormat: changes the formatting of the axis labels. Supported values are listed at https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#numbers for numbers. For example, the format
%can be used to output percentages. For date/time specification of supported values is https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md , e.g.xAxisFormat=%d-%m-%Yfor result 13-01-1977. - xAxisAngle: rotates the x axis labels by the specified angle. Recommended values are: -45, +45, -90, +90
- xType and yType: data types of the values, e.g.
integerfor integers,numberfor real numbers,datefor dates (e.g. YYYY-MM-DD), andstringfor ordinal values (usestringto prevent axis values from being repeated when there are only a few values). Remarks:Datetype doesn't work for bar graphs. Fordatedata input please use ISO date format (e.g. YYYY-MM-DD) acc. to date and time formats used in HTML. Other date formats may work but not in all browsers. Date is unfortunately displayed only in en-US format for all Wikipedia languages. Workaround is to use xAxisFormat and yAxisFormat with numerical dates format. - xScaleType and yScaleType: scale types of the x and y axes, e.g.
linearfor linear scale (default),logfor logarithmic scale andsqrtfor square root scale.- A logarithmic chart allows only positive values to be plotted. A square root scale chart cannot show negative values.
- x: the x-values as a comma-separated list, for dates and time see remark in xType and yType
- y or y1, y2, ...: the y-values for one or several data series, respectively. For pie charts
y2denotes the radius of the corresponding sectors. For dates and time see remark in xType and yType - legend: show legend (only works in case of multiple data series)
- y1Title, y2Title, ...: defines the label of the respective data series in the legend
- linewidth: line width for line charts or distance between the pie segments for pie charts. Setting to 0 with
type=linecreates a scatter plot. - linewidths: different line widths may be defined for each series of data with csv, if set to 0 with "showSymbols" results with points graph, eg.:
linewidths=1, 0, 5, 0.2 - showSymbols: show symbol on data point for line graphs, if a number is provided, the symbol size (default 2.5) may be defined for each data series, eg.:
showSymbols=1, 2, 3, 4 - symbolsShape: custom shape for symbol: circle, x, square, cross, diamond, triangle_up, triangle_down, triangle_right, triangle_left. May be defined for each series of data with csv, eg.:
symbolsShape= circle, cross, square - symbolsNoFill: if true symbol will be without fill (only stroke),
- symbolsStroke: if "x" symbol is used or option "symbolsNoFill" symbol stroke width, default 2.5
- showValues: Additionally, output the y values as text. (Currently, only (non-stacked) bar and pie charts are supported.) The output can be configured used the following parameters provided as
name1:value1, name2:value2(e.g.|showValues=fontcolor:blue,angle:0).- format: Format the output according to https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#numbers for numbers and https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Formatting.md for date/time.
- fontcolor: text color
- fontsize: text size
- offset: move text by the given offset. For bar charts and pie charts with
midanglethis also defines if the text is inside or outside the chart. - angle (pie charts only): text angle in degrees or
midangle(default) for dynamic angles based on the mid-angle of the pie sector.
- innerRadius: For pie charts: defines the inner radius to create a doughnut chart.
- xGrid and yGrid: display grid lines on the x and y axes.
- Annotations
- vAnnotatonsLine and hAnnotatonsLine: display vertical or horizontal annotation lines on specific values e.g.
hAnnotatonsLine=4, 5, 6 - vAnnotatonsLabel and hAnnotatonsLabel: display vertical or horizontal annotation labels for lines e.g.
hAnnotatonsLabel = label1, label2, label3
- vAnnotatonsLine and hAnnotatonsLine: display vertical or horizontal annotation lines on specific values e.g.
- formatjson: format JSON object for better legibility
- title: The title of the graph, used by User:GraphBot when converting.
- name: The name of the graph, required by User:GraphBot to convert the graph.
Template wrappers
The functions mapWrapper and chartWrapper are wrappers to pass all parameters of the calling template to the respective map and chart functions.
Note: In the editor preview the graph extension creates a canvas element with vector graphics. However, when saving the page a PNG raster graphics is generated instead. {{#invoke:Graph|function_wrapper_name}}
Editors can experiment in this module's sandbox (edit | diff) and testcases (create) pages.
Subpages of this module.
-- 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"[[Category:Graphs to Port]]" else return"[[Category:Graphs to be ported needing names]]" end end end returnp