in tfjs-vis/src/render/linechart.ts [60:207]
export async function linechart(
container: Drawable, data: XYPlotData,
opts: XYPlotOptions = {}): Promise<void> {
// Nest data if necessary before further processing
const _data = Array.isArray(data.values[0]) ? data.values as Point2D[][] :
[data.values] as Point2D[][];
const numValues = _data[0].length;
// Create series names if none were passed in.
const _series: string[] =
data.series ? data.series : _data.map((_, i) => `Series ${i + 1}`);
assert(
_series.length === _data.length,
'Must have an equal number of series labels as there are data series');
if (opts.seriesColors != null) {
assert(
opts.seriesColors.length === _data.length,
'Must have an equal number of series colors as there are data series');
}
const vlChartValues: VLChartValue[] = [];
for (let valueIdx = 0; valueIdx < numValues; valueIdx++) {
const v: VLChartValue = {
x: valueIdx,
};
_series.forEach((seriesName, seriesIdx) => {
const seriesValue = _data[seriesIdx][valueIdx].y;
v[seriesName] = seriesValue;
v[`${seriesName}-name`] = seriesName;
});
vlChartValues.push(v);
}
const options = Object.assign({}, defaultOpts, opts);
const yScale = (): {}|undefined => {
if (options.zoomToFit) {
return {'zero': false};
} else if (options.yAxisDomain != null) {
return {'domain': options.yAxisDomain};
}
return undefined;
};
const sharedEncoding = {
x: {
field: 'x',
type: options.xType,
title: options.xLabel,
},
tooltip: [
{field: 'x', type: 'quantitative'},
..._series.map(seriesName => {
return {
field: seriesName,
type: 'quantitative',
};
}),
]
};
const lineLayers: TopLevelSpec[] = _series.map((seriesName) => {
return {
// data will be defined at the chart level.
'data': undefined,
'mark': {'type': 'line', 'clip': true},
'encoding': {
// Note: the encoding for 'x' is shared
// Add a y encoding for this series
'y': {
'field': seriesName,
'type': options.yType,
'title': options.yLabel,
'scale': yScale(),
},
'color': {
'field': `${seriesName}-name`,
'type': 'nominal',
'legend': {'values': _series, title: null},
'scale': {
'range': options.seriesColors,
}
},
}
};
});
const tooltipLayer = {
'mark': 'rule',
'selection': {
'hover': {
'type': 'single',
'on': 'mouseover',
'nearest': true,
clear: 'mouseout',
}
},
'encoding': {
'color': {
'value': 'grey',
'condition': {
'selection': {'not': 'hover'},
'value': 'transparent',
}
}
}
};
const drawArea = getDrawArea(container);
const spec = {
'width': options.width || getDefaultWidth(drawArea),
'height': options.height || getDefaultHeight(drawArea),
'padding': 0,
'autosize': {
'type': 'fit',
'contains': 'padding',
'resize': true,
},
'config': {
'axis': {
'labelFontSize': options.fontSize,
'titleFontSize': options.fontSize,
},
'text': {'fontSize': options.fontSize},
'legend': {
'labelFontSize': options.fontSize,
'titleFontSize': options.fontSize,
}
},
'data': {'values': vlChartValues},
'encoding': sharedEncoding,
'layer': [
...lineLayers,
tooltipLayer,
],
};
const embedOpts = {
actions: false,
mode: 'vega-lite' as Mode,
defaultStyle: false,
};
await embed(drawArea, spec as VisualizationSpec, embedOpts);
return Promise.resolve();
}