ag-charts-community
Version:
Advanced Charting / Charts supporting Javascript / Typescript / React / Angular / Vue
625 lines • 25.7 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spread = (this && this.__spread) || function () {
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
var group_1 = require("../../../scene/group");
var selection_1 = require("../../../scene/selection");
var rect_1 = require("../../../scene/shape/rect");
var text_1 = require("../../../scene/shape/text");
var series_1 = require("../series");
var label_1 = require("../../label");
var node_1 = require("../../../scene/node");
var cartesianSeries_1 = require("./cartesianSeries");
var chartAxis_1 = require("../../chartAxis");
var chart_1 = require("../../chart");
var array_1 = require("../../../util/array");
var observable_1 = require("../../../util/observable");
var ticks_1 = require("../../../util/ticks");
var sanitize_1 = require("../../../util/sanitize");
var value_1 = require("../../../util/value");
var HistogramSeriesNodeTag;
(function (HistogramSeriesNodeTag) {
HistogramSeriesNodeTag[HistogramSeriesNodeTag["Bin"] = 0] = "Bin";
HistogramSeriesNodeTag[HistogramSeriesNodeTag["Label"] = 1] = "Label";
})(HistogramSeriesNodeTag || (HistogramSeriesNodeTag = {}));
var HistogramSeriesLabel = /** @class */ (function (_super) {
__extends(HistogramSeriesLabel, _super);
function HistogramSeriesLabel() {
return _super !== null && _super.apply(this, arguments) || this;
}
__decorate([
observable_1.reactive('change')
], HistogramSeriesLabel.prototype, "formatter", void 0);
return HistogramSeriesLabel;
}(label_1.Label));
var defaultBinCount = 10;
var aggregationFunctions = {
count: function (bin) { return bin.data.length; },
sum: function (bin, yKey) { return bin.data.reduce(function (acc, datum) { return acc + datum[yKey]; }, 0); },
mean: function (bin, yKey) { return aggregationFunctions.sum(bin, yKey) / aggregationFunctions.count(bin, yKey); }
};
var HistogramBin = /** @class */ (function () {
function HistogramBin(_a) {
var _b = __read(_a, 2), domainMin = _b[0], domainMax = _b[1];
this.data = [];
this.aggregatedValue = 0;
this.frequency = 0;
this.domain = [domainMin, domainMax];
}
;
HistogramBin.prototype.addDatum = function (datum) {
this.data.push(datum);
this.frequency++;
};
Object.defineProperty(HistogramBin.prototype, "domainWidth", {
get: function () {
var _a = __read(this.domain, 2), domainMin = _a[0], domainMax = _a[1];
return domainMax - domainMin;
},
enumerable: true,
configurable: true
});
Object.defineProperty(HistogramBin.prototype, "relativeHeight", {
get: function () {
return this.aggregatedValue / this.domainWidth;
},
enumerable: true,
configurable: true
});
HistogramBin.prototype.calculateAggregatedValue = function (aggregationName, yKey) {
if (!yKey) {
// not having a yKey forces us into a frequency plot
aggregationName = 'count';
}
var aggregationFunction = aggregationFunctions[aggregationName];
this.aggregatedValue = aggregationFunction(this, yKey);
};
HistogramBin.prototype.getY = function (areaPlot) {
return areaPlot ? this.relativeHeight : this.aggregatedValue;
};
return HistogramBin;
}());
exports.HistogramBin = HistogramBin;
var HistogramSeriesTooltip = /** @class */ (function (_super) {
__extends(HistogramSeriesTooltip, _super);
function HistogramSeriesTooltip() {
return _super !== null && _super.apply(this, arguments) || this;
}
__decorate([
observable_1.reactive('change')
], HistogramSeriesTooltip.prototype, "renderer", void 0);
return HistogramSeriesTooltip;
}(series_1.SeriesTooltip));
exports.HistogramSeriesTooltip = HistogramSeriesTooltip;
var HistogramSeries = /** @class */ (function (_super) {
__extends(HistogramSeries, _super);
function HistogramSeries() {
var _a;
var _this = _super.call(this) || this;
// Need to put column and label nodes into separate groups, because even though label nodes are
// created after the column nodes, this only guarantees that labels will always be on top of columns
// on the first run. If on the next run more columns are added, they might clip the labels
// rendered during the previous run.
_this.rectGroup = _this.pickGroup.appendChild(new group_1.Group());
_this.textGroup = _this.group.appendChild(new group_1.Group());
_this.rectSelection = selection_1.Selection.select(_this.rectGroup).selectAll();
_this.textSelection = selection_1.Selection.select(_this.textGroup).selectAll();
_this.binnedData = [];
_this.xDomain = [];
_this.yDomain = [];
_this.label = new HistogramSeriesLabel();
_this.seriesItemEnabled = true;
_this.tooltip = new HistogramSeriesTooltip();
_this.fill = undefined;
_this.stroke = undefined;
_this.fillOpacity = 1;
_this.strokeOpacity = 1;
_this.lineDash = [0];
_this.lineDashOffset = 0;
_this.directionKeys = (_a = {},
_a[chartAxis_1.ChartAxisDirection.X] = ['xKey'],
_a[chartAxis_1.ChartAxisDirection.Y] = ['yKey'],
_a);
_this._xKey = '';
_this._areaPlot = false;
_this._bins = undefined;
_this._aggregation = 'count';
_this._binCount = undefined;
_this._xName = '';
_this._yKey = '';
_this._yName = '';
_this._strokeWidth = 1;
_this.label.enabled = false;
_this.label.addEventListener('change', _this.scheduleUpdate, _this);
return _this;
}
HistogramSeries.prototype.getKeys = function (direction) {
var _this = this;
var directionKeys = this.directionKeys;
var keys = directionKeys && directionKeys[direction];
var values = [];
if (keys) {
keys.forEach(function (key) {
var value = _this[key];
if (value) {
if (Array.isArray(value)) {
values.push.apply(values, __spread(value));
}
else {
values.push(value);
}
}
});
}
return values;
};
Object.defineProperty(HistogramSeries.prototype, "xKey", {
get: function () {
return this._xKey;
},
set: function (value) {
if (this._xKey !== value) {
this._xKey = value;
this.scheduleData();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(HistogramSeries.prototype, "areaPlot", {
get: function () {
return this._areaPlot;
},
set: function (c) {
this._areaPlot = c;
this.scheduleData();
},
enumerable: true,
configurable: true
});
Object.defineProperty(HistogramSeries.prototype, "bins", {
get: function () {
return this._bins;
},
set: function (bins) {
this._bins = bins;
this.scheduleData();
},
enumerable: true,
configurable: true
});
Object.defineProperty(HistogramSeries.prototype, "aggregation", {
get: function () {
return this._aggregation;
},
set: function (aggregation) {
this._aggregation = aggregation;
this.scheduleData();
},
enumerable: true,
configurable: true
});
Object.defineProperty(HistogramSeries.prototype, "binCount", {
get: function () {
return this._binCount;
},
set: function (binCount) {
this._binCount = binCount;
this.scheduleData();
},
enumerable: true,
configurable: true
});
Object.defineProperty(HistogramSeries.prototype, "xName", {
get: function () {
return this._xName;
},
set: function (value) {
if (this._xName !== value) {
this._xName = value;
this.scheduleUpdate();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(HistogramSeries.prototype, "yKey", {
get: function () {
return this._yKey;
},
set: function (yKey) {
this._yKey = yKey;
this.seriesItemEnabled = true;
this.scheduleData();
},
enumerable: true,
configurable: true
});
Object.defineProperty(HistogramSeries.prototype, "yName", {
get: function () {
return this._yName;
},
set: function (values) {
this._yName = values;
this.scheduleData();
},
enumerable: true,
configurable: true
});
Object.defineProperty(HistogramSeries.prototype, "strokeWidth", {
get: function () {
return this._strokeWidth;
},
set: function (value) {
if (this._strokeWidth !== value) {
this._strokeWidth = value;
this.scheduleUpdate();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(HistogramSeries.prototype, "shadow", {
get: function () {
return this._shadow;
},
set: function (value) {
if (this._shadow !== value) {
this._shadow = value;
this.scheduleUpdate();
}
},
enumerable: true,
configurable: true
});
HistogramSeries.prototype.onHighlightChange = function () {
this.updateRectNodes();
};
HistogramSeries.prototype.setColors = function (fills, strokes) {
this.fill = fills[0];
this.stroke = strokes[0];
};
// During processData phase, used to unify different ways of the user specifying
// the bins. Returns bins in format[[min1, max1], [min2, max2], ... ].
HistogramSeries.prototype.deriveBins = function () {
var _this = this;
var _a = this, bins = _a.bins, binCount = _a.binCount;
if (!this.data) {
return [];
}
if (bins) {
return bins;
}
var xData = this.data.map(function (datum) { return datum[_this.xKey]; });
var xDomain = this.fixNumericExtent(array_1.extent(xData, value_1.isContinuous), 'x');
var binStarts = ticks_1.default(xDomain[0], xDomain[1], this.binCount || defaultBinCount);
var binSize = ticks_1.tickStep(xDomain[0], xDomain[1], this.binCount || defaultBinCount);
var firstBinEnd = binStarts[0];
var expandStartToBin = function (n) { return [n, n + binSize]; };
return __spread([
[firstBinEnd - binSize, firstBinEnd]
], binStarts.map(expandStartToBin));
};
HistogramSeries.prototype.placeDataInBins = function (data) {
var _this = this;
var xKey = this.xKey;
var derivedBins = this.deriveBins();
// creating a sorted copy allows binning in O(n) rather than O(n²)
// but at the expense of more temporary memory
var sortedData = data.slice().sort(function (a, b) {
if (a[xKey] < b[xKey]) {
return -1;
}
if (a[xKey] > b[xKey]) {
return 1;
}
return 0;
});
var currentBin = 0;
var bins = [new HistogramBin(derivedBins[0])];
loop: for (var i = 0, ln = sortedData.length; i < ln; i++) {
var datum = sortedData[i];
while (datum[xKey] > derivedBins[currentBin][1]) {
currentBin++;
var bin = derivedBins[currentBin];
if (!bin) {
break loop;
}
bins.push(new HistogramBin(bin));
}
bins[currentBin].addDatum(datum);
}
bins.forEach(function (b) { return b.calculateAggregatedValue(_this._aggregation, _this.yKey); });
return bins;
};
Object.defineProperty(HistogramSeries.prototype, "xMax", {
get: function () {
var _this = this;
return this.data && this.data.reduce(function (acc, datum) {
return Math.max(acc, datum[_this.xKey]);
}, Number.NEGATIVE_INFINITY);
},
enumerable: true,
configurable: true
});
HistogramSeries.prototype.processData = function () {
var _this = this;
var _a = this, xKey = _a.xKey, data = _a.data;
this.binnedData = this.placeDataInBins(xKey && data ? data : []);
var yData = this.binnedData.map(function (b) { return b.getY(_this.areaPlot); });
var yMinMax = array_1.extent(yData, value_1.isContinuous);
this.yDomain = this.fixNumericExtent([0, yMinMax ? yMinMax[1] : 1], 'y');
var firstBin = this.binnedData[0];
var lastBin = this.binnedData[this.binnedData.length - 1];
var xMin = firstBin.domain[0];
var xMax = lastBin.domain[1];
this.xDomain = [xMin, xMax];
this.fireEvent({ type: 'dataProcessed' });
return true;
};
HistogramSeries.prototype.getDomain = function (direction) {
if (direction === chartAxis_1.ChartAxisDirection.X) {
return this.xDomain;
}
else {
return this.yDomain;
}
};
HistogramSeries.prototype.fireNodeClickEvent = function (event, datum) {
this.fireEvent({
type: 'nodeClick',
event: event,
series: this,
datum: datum.datum,
xKey: this.xKey
});
};
HistogramSeries.prototype.update = function () {
this.updatePending = false;
this.updateSelections();
this.updateNodes();
};
HistogramSeries.prototype.updateSelections = function () {
if (!this.nodeDataPending) {
return;
}
this.nodeDataPending = false;
var nodeData = this.createNodeData();
this.updateRectSelection(nodeData);
this.updateTextSelection(nodeData);
};
HistogramSeries.prototype.updateNodes = function () {
this.group.visible = this.visible;
this.updateRectNodes();
this.updateTextNodes();
};
HistogramSeries.prototype.createNodeData = function () {
var _this = this;
var _a = this, xAxis = _a.xAxis, yAxis = _a.yAxis;
if (!this.seriesItemEnabled || !xAxis || !yAxis) {
return [];
}
var xScale = xAxis.scale;
var yScale = yAxis.scale;
var _b = this, fill = _b.fill, stroke = _b.stroke, strokeWidth = _b.strokeWidth;
var nodeData = [];
var defaultLabelFormatter = function (params) { return String(params.value); };
var _c = this.label, _d = _c.formatter, labelFormatter = _d === void 0 ? defaultLabelFormatter : _d, labelFontStyle = _c.fontStyle, labelFontWeight = _c.fontWeight, labelFontSize = _c.fontSize, labelFontFamily = _c.fontFamily, labelColor = _c.color;
this.binnedData.forEach(function (binOfData) {
var total = binOfData.aggregatedValue, frequency = binOfData.frequency, _a = __read(binOfData.domain, 2), xDomainMin = _a[0], xDomainMax = _a[1], relativeHeight = binOfData.relativeHeight;
var xMinPx = xScale.convert(xDomainMin), xMaxPx = xScale.convert(xDomainMax),
// note: assuming can't be negative:
y = _this.areaPlot ? relativeHeight : (_this.yKey ? total : frequency), yZeroPx = yScale.convert(0), yMaxPx = yScale.convert(y), w = xMaxPx - xMinPx, h = Math.abs(yMaxPx - yZeroPx);
var selectionDatumLabel = y !== 0 ? {
text: labelFormatter({ value: binOfData.aggregatedValue }),
fontStyle: labelFontStyle,
fontWeight: labelFontWeight,
fontSize: labelFontSize,
fontFamily: labelFontFamily,
fill: labelColor,
x: xMinPx + w / 2,
y: yMaxPx + h / 2
} : undefined;
nodeData.push({
series: _this,
datum: binOfData,
// since each selection is an aggregation of multiple data.
x: xMinPx,
y: yMaxPx,
width: w,
height: h,
fill: fill,
stroke: stroke,
strokeWidth: strokeWidth,
label: selectionDatumLabel,
});
});
return nodeData;
};
HistogramSeries.prototype.updateRectSelection = function (nodeData) {
var updateRects = this.rectSelection.setData(nodeData);
updateRects.exit.remove();
var enterRects = updateRects.enter.append(rect_1.Rect).each(function (rect) {
rect.tag = HistogramSeriesNodeTag.Bin;
rect.crisp = true;
});
this.rectSelection = updateRects.merge(enterRects);
};
HistogramSeries.prototype.updateRectNodes = function () {
var _this = this;
if (!this.chart) {
return;
}
var _a = this, fillOpacity = _a.fillOpacity, strokeOpacity = _a.strokeOpacity, shadow = _a.shadow, highlightedDatum = _a.chart.highlightedDatum, _b = _a.highlightStyle, deprecatedFill = _b.fill, deprecatedStroke = _b.stroke, deprecatedStrokeWidth = _b.strokeWidth, _c = _b.item, _d = _c.fill, highlightedFill = _d === void 0 ? deprecatedFill : _d, _e = _c.stroke, highlightedStroke = _e === void 0 ? deprecatedStroke : _e, _f = _c.strokeWidth, highlightedDatumStrokeWidth = _f === void 0 ? deprecatedStrokeWidth : _f;
this.rectSelection.each(function (rect, datum, index) {
var isDatumHighlighted = datum === highlightedDatum;
var strokeWidth = isDatumHighlighted && highlightedDatumStrokeWidth !== undefined
? highlightedDatumStrokeWidth
: _this.getStrokeWidth(datum.strokeWidth, datum);
rect.x = datum.x;
rect.y = datum.y;
rect.width = datum.width;
rect.height = datum.height;
rect.fill = isDatumHighlighted && highlightedFill !== undefined ? highlightedFill : datum.fill;
rect.stroke = isDatumHighlighted && highlightedStroke !== undefined ? highlightedStroke : datum.stroke;
rect.fillOpacity = fillOpacity;
rect.strokeOpacity = strokeOpacity;
rect.strokeWidth = strokeWidth;
rect.lineDash = _this.lineDash;
rect.lineDashOffset = _this.lineDashOffset;
rect.fillShadow = shadow;
rect.zIndex = isDatumHighlighted ? series_1.Series.highlightedZIndex : index;
rect.visible = datum.height > 0; // prevent stroke from rendering for zero height columns
rect.opacity = _this.getOpacity(datum);
});
};
HistogramSeries.prototype.updateTextSelection = function (nodeData) {
var updateTexts = this.textSelection.setData(nodeData);
updateTexts.exit.remove();
var enterTexts = updateTexts.enter.append(text_1.Text).each(function (text) {
text.tag = HistogramSeriesNodeTag.Label;
text.pointerEvents = node_1.PointerEvents.None;
text.textAlign = 'center';
text.textBaseline = 'middle';
});
this.textSelection = updateTexts.merge(enterTexts);
};
HistogramSeries.prototype.updateTextNodes = function () {
var labelEnabled = this.label.enabled;
this.textSelection.each(function (text, datum) {
var label = datum.label;
if (label && labelEnabled) {
text.text = label.text;
text.x = label.x;
text.y = label.y;
text.fontStyle = label.fontStyle;
text.fontWeight = label.fontWeight;
text.fontSize = label.fontSize;
text.fontFamily = label.fontFamily;
text.fill = label.fill;
text.visible = true;
}
else {
text.visible = false;
}
});
};
HistogramSeries.prototype.getTooltipHtml = function (nodeDatum) {
var _a = this, xKey = _a.xKey, yKey = _a.yKey, xAxis = _a.xAxis, yAxis = _a.yAxis;
if (!xKey || !xAxis || !yAxis) {
return '';
}
var _b = this, xName = _b.xName, yName = _b.yName, color = _b.fill, tooltip = _b.tooltip, aggregation = _b.aggregation;
var tooltipRenderer = tooltip.renderer;
var bin = nodeDatum.datum;
var aggregatedValue = bin.aggregatedValue, frequency = bin.frequency, _c = __read(bin.domain, 2), rangeMin = _c[0], rangeMax = _c[1];
var title = sanitize_1.sanitizeHtml(xName || xKey) + ": " + xAxis.formatDatum(rangeMin) + " - " + xAxis.formatDatum(rangeMax);
var content = yKey ?
"<b>" + sanitize_1.sanitizeHtml(yName || yKey) + " (" + aggregation + ")</b>: " + yAxis.formatDatum(aggregatedValue) + "<br>" :
'';
content += "<b>Frequency</b>: " + frequency;
var defaults = {
title: title,
backgroundColor: color,
content: content
};
if (tooltipRenderer) {
return chart_1.toTooltipHtml(tooltipRenderer({
datum: bin,
xKey: xKey,
xValue: bin.domain,
xName: xName,
yKey: yKey,
yValue: bin.aggregatedValue,
yName: yName,
color: color
}), defaults);
}
return chart_1.toTooltipHtml(defaults);
};
HistogramSeries.prototype.listSeriesItems = function (legendData) {
var _a = this, id = _a.id, data = _a.data, yKey = _a.yKey, yName = _a.yName, seriesItemEnabled = _a.seriesItemEnabled, fill = _a.fill, stroke = _a.stroke, fillOpacity = _a.fillOpacity, strokeOpacity = _a.strokeOpacity;
if (data && data.length) {
legendData.push({
id: id,
itemId: yKey,
enabled: seriesItemEnabled,
label: {
text: yName || yKey || 'Frequency'
},
marker: {
fill: fill || 'rgba(0, 0, 0, 0)',
stroke: stroke || 'rgba(0, 0, 0, 0)',
fillOpacity: fillOpacity,
strokeOpacity: strokeOpacity
}
});
}
};
HistogramSeries.prototype.toggleSeriesItem = function (itemId, enabled) {
if (itemId === this.yKey) {
this.seriesItemEnabled = enabled;
}
this.scheduleData();
};
HistogramSeries.className = 'HistogramSeries';
HistogramSeries.type = 'histogram';
__decorate([
observable_1.reactive('dataChange')
], HistogramSeries.prototype, "fill", void 0);
__decorate([
observable_1.reactive('dataChange')
], HistogramSeries.prototype, "stroke", void 0);
__decorate([
observable_1.reactive('layoutChange')
], HistogramSeries.prototype, "fillOpacity", void 0);
__decorate([
observable_1.reactive('layoutChange')
], HistogramSeries.prototype, "strokeOpacity", void 0);
__decorate([
observable_1.reactive('update')
], HistogramSeries.prototype, "lineDash", void 0);
__decorate([
observable_1.reactive('update')
], HistogramSeries.prototype, "lineDashOffset", void 0);
return HistogramSeries;
}(cartesianSeries_1.CartesianSeries));
exports.HistogramSeries = HistogramSeries;
//# sourceMappingURL=histogramSeries.js.map