mirror of https://github.com/ColorlibHQ/gentelella
1058 lines
30 KiB
JavaScript
1058 lines
30 KiB
JavaScript
/**
|
|
* List for data storage
|
|
* @module echarts/data/List
|
|
*/
|
|
define(function (require) {
|
|
|
|
var UNDEFINED = 'undefined';
|
|
var globalObj = typeof window === 'undefined' ? global : window;
|
|
var Float64Array = typeof globalObj.Float64Array === UNDEFINED
|
|
? Array : globalObj.Float64Array;
|
|
var Int32Array = typeof globalObj.Int32Array === UNDEFINED
|
|
? Array : globalObj.Int32Array;
|
|
|
|
var dataCtors = {
|
|
'float': Float64Array,
|
|
'int': Int32Array,
|
|
// Ordinal data type can be string or int
|
|
'ordinal': Array,
|
|
'number': Array,
|
|
'time': Array
|
|
};
|
|
|
|
var Model = require('../model/Model');
|
|
var DataDiffer = require('./DataDiffer');
|
|
|
|
var zrUtil = require('zrender/core/util');
|
|
var modelUtil = require('../util/model');
|
|
var isObject = zrUtil.isObject;
|
|
|
|
var IMMUTABLE_PROPERTIES = [
|
|
'stackedOn', '_nameList', '_idList', '_rawData'
|
|
];
|
|
|
|
var transferImmuProperties = function (a, b) {
|
|
zrUtil.each(IMMUTABLE_PROPERTIES.concat(b.__wrappedMethods || []), function (propName) {
|
|
if (b.hasOwnProperty(propName)) {
|
|
a[propName] = b[propName];
|
|
}
|
|
});
|
|
|
|
a.__wrappedMethods = b.__wrappedMethods;
|
|
};
|
|
|
|
/**
|
|
* @constructor
|
|
* @alias module:echarts/data/List
|
|
*
|
|
* @param {Array.<string>} dimensions
|
|
* Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
|
|
* @param {module:echarts/model/Model} hostModel
|
|
*/
|
|
var List = function (dimensions, hostModel) {
|
|
|
|
dimensions = dimensions || ['x', 'y'];
|
|
|
|
var dimensionInfos = {};
|
|
var dimensionNames = [];
|
|
for (var i = 0; i < dimensions.length; i++) {
|
|
var dimensionName;
|
|
var dimensionInfo = {};
|
|
if (typeof dimensions[i] === 'string') {
|
|
dimensionName = dimensions[i];
|
|
dimensionInfo = {
|
|
name: dimensionName,
|
|
stackable: false,
|
|
// Type can be 'float', 'int', 'number'
|
|
// Default is number, Precision of float may not enough
|
|
type: 'number'
|
|
};
|
|
}
|
|
else {
|
|
dimensionInfo = dimensions[i];
|
|
dimensionName = dimensionInfo.name;
|
|
dimensionInfo.type = dimensionInfo.type || 'number';
|
|
}
|
|
dimensionNames.push(dimensionName);
|
|
dimensionInfos[dimensionName] = dimensionInfo;
|
|
}
|
|
/**
|
|
* @readOnly
|
|
* @type {Array.<string>}
|
|
*/
|
|
this.dimensions = dimensionNames;
|
|
|
|
/**
|
|
* Infomation of each data dimension, like data type.
|
|
* @type {Object}
|
|
*/
|
|
this._dimensionInfos = dimensionInfos;
|
|
|
|
/**
|
|
* @type {module:echarts/model/Model}
|
|
*/
|
|
this.hostModel = hostModel;
|
|
|
|
/**
|
|
* @type {module:echarts/model/Model}
|
|
*/
|
|
this.dataType;
|
|
|
|
/**
|
|
* Indices stores the indices of data subset after filtered.
|
|
* This data subset will be used in chart.
|
|
* @type {Array.<number>}
|
|
* @readOnly
|
|
*/
|
|
this.indices = [];
|
|
|
|
/**
|
|
* Data storage
|
|
* @type {Object.<key, TypedArray|Array>}
|
|
* @private
|
|
*/
|
|
this._storage = {};
|
|
|
|
/**
|
|
* @type {Array.<string>}
|
|
*/
|
|
this._nameList = [];
|
|
/**
|
|
* @type {Array.<string>}
|
|
*/
|
|
this._idList = [];
|
|
/**
|
|
* Models of data option is stored sparse for optimizing memory cost
|
|
* @type {Array.<module:echarts/model/Model>}
|
|
* @private
|
|
*/
|
|
this._optionModels = [];
|
|
|
|
/**
|
|
* @param {module:echarts/data/List}
|
|
*/
|
|
this.stackedOn = null;
|
|
|
|
/**
|
|
* Global visual properties after visual coding
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
this._visual = {};
|
|
|
|
/**
|
|
* Globel layout properties.
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
this._layout = {};
|
|
|
|
/**
|
|
* Item visual properties after visual coding
|
|
* @type {Array.<Object>}
|
|
* @private
|
|
*/
|
|
this._itemVisuals = [];
|
|
|
|
/**
|
|
* Item layout properties after layout
|
|
* @type {Array.<Object>}
|
|
* @private
|
|
*/
|
|
this._itemLayouts = [];
|
|
|
|
/**
|
|
* Graphic elemnents
|
|
* @type {Array.<module:zrender/Element>}
|
|
* @private
|
|
*/
|
|
this._graphicEls = [];
|
|
|
|
/**
|
|
* @type {Array.<Array|Object>}
|
|
* @private
|
|
*/
|
|
this._rawData;
|
|
|
|
/**
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
this._extent;
|
|
};
|
|
|
|
var listProto = List.prototype;
|
|
|
|
listProto.type = 'list';
|
|
|
|
/**
|
|
* Get dimension name
|
|
* @param {string|number} dim
|
|
* Dimension can be concrete names like x, y, z, lng, lat, angle, radius
|
|
* Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
|
|
* @return {string} Concrete dim name.
|
|
*/
|
|
listProto.getDimension = function (dim) {
|
|
if (!isNaN(dim)) {
|
|
dim = this.dimensions[dim] || dim;
|
|
}
|
|
return dim;
|
|
};
|
|
/**
|
|
* Get type and stackable info of particular dimension
|
|
* @param {string|number} dim
|
|
* Dimension can be concrete names like x, y, z, lng, lat, angle, radius
|
|
* Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
|
|
*/
|
|
listProto.getDimensionInfo = function (dim) {
|
|
return zrUtil.clone(this._dimensionInfos[this.getDimension(dim)]);
|
|
};
|
|
|
|
/**
|
|
* Initialize from data
|
|
* @param {Array.<Object|number|Array>} data
|
|
* @param {Array.<string>} [nameList]
|
|
* @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
|
|
*/
|
|
listProto.initData = function (data, nameList, dimValueGetter) {
|
|
data = data || [];
|
|
|
|
if (!zrUtil.isArray(data)) {
|
|
throw new Error('Invalid data.');
|
|
}
|
|
|
|
this._rawData = data;
|
|
|
|
// Clear
|
|
var storage = this._storage = {};
|
|
var indices = this.indices = [];
|
|
|
|
var dimensions = this.dimensions;
|
|
var size = data.length;
|
|
var dimensionInfoMap = this._dimensionInfos;
|
|
|
|
var idList = [];
|
|
var nameRepeatCount = {};
|
|
|
|
nameList = nameList || [];
|
|
|
|
// Init storage
|
|
for (var i = 0; i < dimensions.length; i++) {
|
|
var dimInfo = dimensionInfoMap[dimensions[i]];
|
|
var DataCtor = dataCtors[dimInfo.type];
|
|
storage[dimensions[i]] = new DataCtor(size);
|
|
}
|
|
|
|
// Default dim value getter
|
|
dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) {
|
|
var value = modelUtil.getDataItemValue(dataItem);
|
|
return modelUtil.converDataValue(
|
|
zrUtil.isArray(value)
|
|
? value[dimIndex]
|
|
// If value is a single number or something else not array.
|
|
: value,
|
|
dimensionInfoMap[dimName]
|
|
);
|
|
};
|
|
|
|
for (var idx = 0; idx < data.length; idx++) {
|
|
var dataItem = data[idx];
|
|
// Each data item is value
|
|
// [1, 2]
|
|
// 2
|
|
// Bar chart, line chart which uses category axis
|
|
// only gives the 'y' value. 'x' value is the indices of cateogry
|
|
// Use a tempValue to normalize the value to be a (x, y) value
|
|
|
|
// Store the data by dimensions
|
|
for (var k = 0; k < dimensions.length; k++) {
|
|
var dim = dimensions[k];
|
|
var dimStorage = storage[dim];
|
|
// PENDING NULL is empty or zero
|
|
dimStorage[idx] = dimValueGetter(dataItem, dim, idx, k);
|
|
}
|
|
|
|
indices.push(idx);
|
|
}
|
|
|
|
// Use the name in option and create id
|
|
for (var i = 0; i < data.length; i++) {
|
|
var id = '';
|
|
if (!nameList[i]) {
|
|
nameList[i] = data[i].name;
|
|
// Try using the id in option
|
|
id = data[i].id;
|
|
}
|
|
var name = nameList[i] || '';
|
|
if (!id && name) {
|
|
// Use name as id and add counter to avoid same name
|
|
nameRepeatCount[name] = nameRepeatCount[name] || 0;
|
|
id = name;
|
|
if (nameRepeatCount[name] > 0) {
|
|
id += '__ec__' + nameRepeatCount[name];
|
|
}
|
|
nameRepeatCount[name]++;
|
|
}
|
|
id && (idList[i] = id);
|
|
}
|
|
|
|
this._nameList = nameList;
|
|
this._idList = idList;
|
|
};
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
listProto.count = function () {
|
|
return this.indices.length;
|
|
};
|
|
|
|
/**
|
|
* Get value. Return NaN if idx is out of range.
|
|
* @param {string} dim Dim must be concrete name.
|
|
* @param {number} idx
|
|
* @param {boolean} stack
|
|
* @return {number}
|
|
*/
|
|
listProto.get = function (dim, idx, stack) {
|
|
var storage = this._storage;
|
|
var dataIndex = this.indices[idx];
|
|
|
|
// If value not exists
|
|
if (dataIndex == null) {
|
|
return NaN;
|
|
}
|
|
|
|
var value = storage[dim] && storage[dim][dataIndex];
|
|
// FIXME ordinal data type is not stackable
|
|
if (stack) {
|
|
var dimensionInfo = this._dimensionInfos[dim];
|
|
if (dimensionInfo && dimensionInfo.stackable) {
|
|
var stackedOn = this.stackedOn;
|
|
while (stackedOn) {
|
|
// Get no stacked data of stacked on
|
|
var stackedValue = stackedOn.get(dim, idx);
|
|
// Considering positive stack, negative stack and empty data
|
|
if ((value >= 0 && stackedValue > 0) // Positive stack
|
|
|| (value <= 0 && stackedValue < 0) // Negative stack
|
|
) {
|
|
value += stackedValue;
|
|
}
|
|
stackedOn = stackedOn.stackedOn;
|
|
}
|
|
}
|
|
}
|
|
return value;
|
|
};
|
|
|
|
/**
|
|
* Get value for multi dimensions.
|
|
* @param {Array.<string>} [dimensions] If ignored, using all dimensions.
|
|
* @param {number} idx
|
|
* @param {boolean} stack
|
|
* @return {number}
|
|
*/
|
|
listProto.getValues = function (dimensions, idx, stack) {
|
|
var values = [];
|
|
|
|
if (!zrUtil.isArray(dimensions)) {
|
|
stack = idx;
|
|
idx = dimensions;
|
|
dimensions = this.dimensions;
|
|
}
|
|
|
|
for (var i = 0, len = dimensions.length; i < len; i++) {
|
|
values.push(this.get(dimensions[i], idx, stack));
|
|
}
|
|
|
|
return values;
|
|
};
|
|
|
|
/**
|
|
* If value is NaN. Inlcuding '-'
|
|
* @param {string} dim
|
|
* @param {number} idx
|
|
* @return {number}
|
|
*/
|
|
listProto.hasValue = function (idx) {
|
|
var dimensions = this.dimensions;
|
|
var dimensionInfos = this._dimensionInfos;
|
|
for (var i = 0, len = dimensions.length; i < len; i++) {
|
|
if (
|
|
// Ordinal type can be string or number
|
|
dimensionInfos[dimensions[i]].type !== 'ordinal'
|
|
&& isNaN(this.get(dimensions[i], idx))
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Get extent of data in one dimension
|
|
* @param {string} dim
|
|
* @param {boolean} stack
|
|
*/
|
|
listProto.getDataExtent = function (dim, stack) {
|
|
var dimData = this._storage[dim];
|
|
var dimInfo = this.getDimensionInfo(dim);
|
|
stack = (dimInfo && dimInfo.stackable) && stack;
|
|
var dimExtent = (this._extent || (this._extent = {}))[dim + (!!stack)];
|
|
var value;
|
|
if (dimExtent) {
|
|
return dimExtent;
|
|
}
|
|
// var dimInfo = this._dimensionInfos[dim];
|
|
if (dimData) {
|
|
var min = Infinity;
|
|
var max = -Infinity;
|
|
// var isOrdinal = dimInfo.type === 'ordinal';
|
|
for (var i = 0, len = this.count(); i < len; i++) {
|
|
value = this.get(dim, i, stack);
|
|
// FIXME
|
|
// if (isOrdinal && typeof value === 'string') {
|
|
// value = zrUtil.indexOf(dimData, value);
|
|
// }
|
|
value < min && (min = value);
|
|
value > max && (max = value);
|
|
}
|
|
return (this._extent[dim + stack] = [min, max]);
|
|
}
|
|
else {
|
|
return [Infinity, -Infinity];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get sum of data in one dimension
|
|
* @param {string} dim
|
|
* @param {boolean} stack
|
|
*/
|
|
listProto.getSum = function (dim, stack) {
|
|
var dimData = this._storage[dim];
|
|
var sum = 0;
|
|
if (dimData) {
|
|
for (var i = 0, len = this.count(); i < len; i++) {
|
|
var value = this.get(dim, i, stack);
|
|
if (!isNaN(value)) {
|
|
sum += value;
|
|
}
|
|
}
|
|
}
|
|
return sum;
|
|
};
|
|
|
|
/**
|
|
* Retreive the index with given value
|
|
* @param {number} idx
|
|
* @param {number} value
|
|
* @return {number}
|
|
*/
|
|
// FIXME Precision of float value
|
|
listProto.indexOf = function (dim, value) {
|
|
var storage = this._storage;
|
|
var dimData = storage[dim];
|
|
var indices = this.indices;
|
|
|
|
if (dimData) {
|
|
for (var i = 0, len = indices.length; i < len; i++) {
|
|
var rawIndex = indices[i];
|
|
if (dimData[rawIndex] === value) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
/**
|
|
* Retreive the index with given name
|
|
* @param {number} idx
|
|
* @param {number} name
|
|
* @return {number}
|
|
*/
|
|
listProto.indexOfName = function (name) {
|
|
var indices = this.indices;
|
|
var nameList = this._nameList;
|
|
|
|
for (var i = 0, len = indices.length; i < len; i++) {
|
|
var rawIndex = indices[i];
|
|
if (nameList[rawIndex] === name) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
};
|
|
|
|
/**
|
|
* Retreive the index of nearest value
|
|
* @param {string} dim
|
|
* @param {number} value
|
|
* @param {boolean} stack If given value is after stacked
|
|
* @return {number}
|
|
*/
|
|
listProto.indexOfNearest = function (dim, value, stack) {
|
|
var storage = this._storage;
|
|
var dimData = storage[dim];
|
|
|
|
if (dimData) {
|
|
var minDist = Number.MAX_VALUE;
|
|
var nearestIdx = -1;
|
|
for (var i = 0, len = this.count(); i < len; i++) {
|
|
var diff = value - this.get(dim, i, stack);
|
|
var dist = Math.abs(diff);
|
|
if (dist < minDist
|
|
// For the case of two data are same on xAxis, which has sequence data.
|
|
// Show the nearest index
|
|
// https://github.com/ecomfe/echarts/issues/2869
|
|
|| (dist === minDist && diff > 0)
|
|
) {
|
|
minDist = dist;
|
|
nearestIdx = i;
|
|
}
|
|
}
|
|
return nearestIdx;
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
/**
|
|
* Get raw data index
|
|
* @param {number} idx
|
|
* @return {number}
|
|
*/
|
|
listProto.getRawIndex = function (idx) {
|
|
var rawIdx = this.indices[idx];
|
|
return rawIdx == null ? -1 : rawIdx;
|
|
};
|
|
|
|
/**
|
|
* Get raw data item
|
|
* @param {number} idx
|
|
* @return {number}
|
|
*/
|
|
listProto.getRawDataItem = function (idx) {
|
|
return this._rawData[this.getRawIndex(idx)];
|
|
};
|
|
|
|
/**
|
|
* @param {number} idx
|
|
* @param {boolean} [notDefaultIdx=false]
|
|
* @return {string}
|
|
*/
|
|
listProto.getName = function (idx) {
|
|
return this._nameList[this.indices[idx]] || '';
|
|
};
|
|
|
|
/**
|
|
* @param {number} idx
|
|
* @param {boolean} [notDefaultIdx=false]
|
|
* @return {string}
|
|
*/
|
|
listProto.getId = function (idx) {
|
|
return this._idList[this.indices[idx]] || (this.getRawIndex(idx) + '');
|
|
};
|
|
|
|
|
|
function normalizeDimensions(dimensions) {
|
|
if (!zrUtil.isArray(dimensions)) {
|
|
dimensions = [dimensions];
|
|
}
|
|
return dimensions;
|
|
}
|
|
|
|
/**
|
|
* Data iteration
|
|
* @param {string|Array.<string>}
|
|
* @param {Function} cb
|
|
* @param {boolean} [stack=false]
|
|
* @param {*} [context=this]
|
|
*
|
|
* @example
|
|
* list.each('x', function (x, idx) {});
|
|
* list.each(['x', 'y'], function (x, y, idx) {});
|
|
* list.each(function (idx) {})
|
|
*/
|
|
listProto.each = function (dimensions, cb, stack, context) {
|
|
if (typeof dimensions === 'function') {
|
|
context = stack;
|
|
stack = cb;
|
|
cb = dimensions;
|
|
dimensions = [];
|
|
}
|
|
|
|
dimensions = zrUtil.map(
|
|
normalizeDimensions(dimensions), this.getDimension, this
|
|
);
|
|
|
|
var value = [];
|
|
var dimSize = dimensions.length;
|
|
var indices = this.indices;
|
|
|
|
context = context || this;
|
|
|
|
for (var i = 0; i < indices.length; i++) {
|
|
if (dimSize === 0) {
|
|
cb.call(context, i);
|
|
}
|
|
// Simple optimization
|
|
else if (dimSize === 1) {
|
|
cb.call(context, this.get(dimensions[0], i, stack), i);
|
|
}
|
|
else {
|
|
for (var k = 0; k < dimSize; k++) {
|
|
value[k] = this.get(dimensions[k], i, stack);
|
|
}
|
|
// Index
|
|
value[k] = i;
|
|
cb.apply(context, value);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Data filter
|
|
* @param {string|Array.<string>}
|
|
* @param {Function} cb
|
|
* @param {boolean} [stack=false]
|
|
* @param {*} [context=this]
|
|
*/
|
|
listProto.filterSelf = function (dimensions, cb, stack, context) {
|
|
if (typeof dimensions === 'function') {
|
|
context = stack;
|
|
stack = cb;
|
|
cb = dimensions;
|
|
dimensions = [];
|
|
}
|
|
|
|
dimensions = zrUtil.map(
|
|
normalizeDimensions(dimensions), this.getDimension, this
|
|
);
|
|
|
|
var newIndices = [];
|
|
var value = [];
|
|
var dimSize = dimensions.length;
|
|
var indices = this.indices;
|
|
|
|
context = context || this;
|
|
|
|
for (var i = 0; i < indices.length; i++) {
|
|
var keep;
|
|
// Simple optimization
|
|
if (dimSize === 1) {
|
|
keep = cb.call(
|
|
context, this.get(dimensions[0], i, stack), i
|
|
);
|
|
}
|
|
else {
|
|
for (var k = 0; k < dimSize; k++) {
|
|
value[k] = this.get(dimensions[k], i, stack);
|
|
}
|
|
value[k] = i;
|
|
keep = cb.apply(context, value);
|
|
}
|
|
if (keep) {
|
|
newIndices.push(indices[i]);
|
|
}
|
|
}
|
|
|
|
this.indices = newIndices;
|
|
|
|
// Reset data extent
|
|
this._extent = {};
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Data mapping to a plain array
|
|
* @param {string|Array.<string>} [dimensions]
|
|
* @param {Function} cb
|
|
* @param {boolean} [stack=false]
|
|
* @param {*} [context=this]
|
|
* @return {Array}
|
|
*/
|
|
listProto.mapArray = function (dimensions, cb, stack, context) {
|
|
if (typeof dimensions === 'function') {
|
|
context = stack;
|
|
stack = cb;
|
|
cb = dimensions;
|
|
dimensions = [];
|
|
}
|
|
|
|
var result = [];
|
|
this.each(dimensions, function () {
|
|
result.push(cb && cb.apply(this, arguments));
|
|
}, stack, context);
|
|
return result;
|
|
};
|
|
|
|
function cloneListForMapAndSample(original, excludeDimensions) {
|
|
var allDimensions = original.dimensions;
|
|
var list = new List(
|
|
zrUtil.map(allDimensions, original.getDimensionInfo, original),
|
|
original.hostModel
|
|
);
|
|
// FIXME If needs stackedOn, value may already been stacked
|
|
transferImmuProperties(list, original);
|
|
|
|
var storage = list._storage = {};
|
|
var originalStorage = original._storage;
|
|
// Init storage
|
|
for (var i = 0; i < allDimensions.length; i++) {
|
|
var dim = allDimensions[i];
|
|
var dimStore = originalStorage[dim];
|
|
if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
|
|
storage[dim] = new dimStore.constructor(
|
|
originalStorage[dim].length
|
|
);
|
|
}
|
|
else {
|
|
// Direct reference for other dimensions
|
|
storage[dim] = originalStorage[dim];
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
* Data mapping to a new List with given dimensions
|
|
* @param {string|Array.<string>} dimensions
|
|
* @param {Function} cb
|
|
* @param {boolean} [stack=false]
|
|
* @param {*} [context=this]
|
|
* @return {Array}
|
|
*/
|
|
listProto.map = function (dimensions, cb, stack, context) {
|
|
dimensions = zrUtil.map(
|
|
normalizeDimensions(dimensions), this.getDimension, this
|
|
);
|
|
|
|
var list = cloneListForMapAndSample(this, dimensions);
|
|
// Following properties are all immutable.
|
|
// So we can reference to the same value
|
|
var indices = list.indices = this.indices;
|
|
|
|
var storage = list._storage;
|
|
|
|
var tmpRetValue = [];
|
|
this.each(dimensions, function () {
|
|
var idx = arguments[arguments.length - 1];
|
|
var retValue = cb && cb.apply(this, arguments);
|
|
if (retValue != null) {
|
|
// a number
|
|
if (typeof retValue === 'number') {
|
|
tmpRetValue[0] = retValue;
|
|
retValue = tmpRetValue;
|
|
}
|
|
for (var i = 0; i < retValue.length; i++) {
|
|
var dim = dimensions[i];
|
|
var dimStore = storage[dim];
|
|
var rawIdx = indices[idx];
|
|
if (dimStore) {
|
|
dimStore[rawIdx] = retValue[i];
|
|
}
|
|
}
|
|
}
|
|
}, stack, context);
|
|
|
|
return list;
|
|
};
|
|
|
|
/**
|
|
* Large data down sampling on given dimension
|
|
* @param {string} dimension
|
|
* @param {number} rate
|
|
* @param {Function} sampleValue
|
|
* @param {Function} sampleIndex Sample index for name and id
|
|
*/
|
|
listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
|
|
var list = cloneListForMapAndSample(this, [dimension]);
|
|
var storage = this._storage;
|
|
var targetStorage = list._storage;
|
|
|
|
var originalIndices = this.indices;
|
|
var indices = list.indices = [];
|
|
|
|
var frameValues = [];
|
|
var frameIndices = [];
|
|
var frameSize = Math.floor(1 / rate);
|
|
|
|
var dimStore = targetStorage[dimension];
|
|
var len = this.count();
|
|
// Copy data from original data
|
|
for (var i = 0; i < storage[dimension].length; i++) {
|
|
targetStorage[dimension][i] = storage[dimension][i];
|
|
}
|
|
for (var i = 0; i < len; i += frameSize) {
|
|
// Last frame
|
|
if (frameSize > len - i) {
|
|
frameSize = len - i;
|
|
frameValues.length = frameSize;
|
|
}
|
|
for (var k = 0; k < frameSize; k++) {
|
|
var idx = originalIndices[i + k];
|
|
frameValues[k] = dimStore[idx];
|
|
frameIndices[k] = idx;
|
|
}
|
|
var value = sampleValue(frameValues);
|
|
var idx = frameIndices[sampleIndex(frameValues, value) || 0];
|
|
// Only write value on the filtered data
|
|
dimStore[idx] = value;
|
|
indices.push(idx);
|
|
}
|
|
|
|
return list;
|
|
};
|
|
|
|
/**
|
|
* Get model of one data item.
|
|
*
|
|
* @param {number} idx
|
|
*/
|
|
// FIXME Model proxy ?
|
|
listProto.getItemModel = function (idx) {
|
|
var hostModel = this.hostModel;
|
|
idx = this.indices[idx];
|
|
return new Model(this._rawData[idx], hostModel, hostModel && hostModel.ecModel);
|
|
};
|
|
|
|
/**
|
|
* Create a data differ
|
|
* @param {module:echarts/data/List} otherList
|
|
* @return {module:echarts/data/DataDiffer}
|
|
*/
|
|
listProto.diff = function (otherList) {
|
|
var idList = this._idList;
|
|
var otherIdList = otherList && otherList._idList;
|
|
return new DataDiffer(
|
|
otherList ? otherList.indices : [], this.indices, function (idx) {
|
|
return otherIdList[idx] || (idx + '');
|
|
}, function (idx) {
|
|
return idList[idx] || (idx + '');
|
|
}
|
|
);
|
|
};
|
|
/**
|
|
* Get visual property.
|
|
* @param {string} key
|
|
*/
|
|
listProto.getVisual = function (key) {
|
|
var visual = this._visual;
|
|
return visual && visual[key];
|
|
};
|
|
|
|
/**
|
|
* Set visual property
|
|
* @param {string|Object} key
|
|
* @param {*} [value]
|
|
*
|
|
* @example
|
|
* setVisual('color', color);
|
|
* setVisual({
|
|
* 'color': color
|
|
* });
|
|
*/
|
|
listProto.setVisual = function (key, val) {
|
|
if (isObject(key)) {
|
|
for (var name in key) {
|
|
if (key.hasOwnProperty(name)) {
|
|
this.setVisual(name, key[name]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
this._visual = this._visual || {};
|
|
this._visual[key] = val;
|
|
};
|
|
|
|
/**
|
|
* Set layout property.
|
|
* @param {string} key
|
|
* @param {*} [val]
|
|
*/
|
|
listProto.setLayout = function (key, val) {
|
|
if (isObject(key)) {
|
|
for (var name in key) {
|
|
if (key.hasOwnProperty(name)) {
|
|
this.setLayout(name, key[name]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
this._layout[key] = val;
|
|
};
|
|
|
|
/**
|
|
* Get layout property.
|
|
* @param {string} key.
|
|
* @return {*}
|
|
*/
|
|
listProto.getLayout = function (key) {
|
|
return this._layout[key];
|
|
};
|
|
|
|
/**
|
|
* Get layout of single data item
|
|
* @param {number} idx
|
|
*/
|
|
listProto.getItemLayout = function (idx) {
|
|
return this._itemLayouts[idx];
|
|
};
|
|
|
|
/**
|
|
* Set layout of single data item
|
|
* @param {number} idx
|
|
* @param {Object} layout
|
|
* @param {boolean=} [merge=false]
|
|
*/
|
|
listProto.setItemLayout = function (idx, layout, merge) {
|
|
this._itemLayouts[idx] = merge
|
|
? zrUtil.extend(this._itemLayouts[idx] || {}, layout)
|
|
: layout;
|
|
};
|
|
|
|
/**
|
|
* Clear all layout of single data item
|
|
*/
|
|
listProto.clearItemLayouts = function () {
|
|
this._itemLayouts.length = 0;
|
|
};
|
|
|
|
/**
|
|
* Get visual property of single data item
|
|
* @param {number} idx
|
|
* @param {string} key
|
|
* @param {boolean} ignoreParent
|
|
*/
|
|
listProto.getItemVisual = function (idx, key, ignoreParent) {
|
|
var itemVisual = this._itemVisuals[idx];
|
|
var val = itemVisual && itemVisual[key];
|
|
if (val == null && !ignoreParent) {
|
|
// Use global visual property
|
|
return this.getVisual(key);
|
|
}
|
|
return val;
|
|
};
|
|
|
|
/**
|
|
* Set visual property of single data item
|
|
*
|
|
* @param {number} idx
|
|
* @param {string|Object} key
|
|
* @param {*} [value]
|
|
*
|
|
* @example
|
|
* setItemVisual(0, 'color', color);
|
|
* setItemVisual(0, {
|
|
* 'color': color
|
|
* });
|
|
*/
|
|
listProto.setItemVisual = function (idx, key, value) {
|
|
var itemVisual = this._itemVisuals[idx] || {};
|
|
this._itemVisuals[idx] = itemVisual;
|
|
|
|
if (isObject(key)) {
|
|
for (var name in key) {
|
|
if (key.hasOwnProperty(name)) {
|
|
itemVisual[name] = key[name];
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
itemVisual[key] = value;
|
|
};
|
|
|
|
var setItemDataAndSeriesIndex = function (child) {
|
|
child.seriesIndex = this.seriesIndex;
|
|
child.dataIndex = this.dataIndex;
|
|
child.dataType = this.dataType;
|
|
};
|
|
/**
|
|
* Set graphic element relative to data. It can be set as null
|
|
* @param {number} idx
|
|
* @param {module:zrender/Element} [el]
|
|
*/
|
|
listProto.setItemGraphicEl = function (idx, el) {
|
|
var hostModel = this.hostModel;
|
|
|
|
if (el) {
|
|
// Add data index and series index for indexing the data by element
|
|
// Useful in tooltip
|
|
el.dataIndex = idx;
|
|
el.dataType = this.dataType;
|
|
el.seriesIndex = hostModel && hostModel.seriesIndex;
|
|
if (el.type === 'group') {
|
|
el.traverse(setItemDataAndSeriesIndex, el);
|
|
}
|
|
}
|
|
|
|
this._graphicEls[idx] = el;
|
|
};
|
|
|
|
/**
|
|
* @param {number} idx
|
|
* @return {module:zrender/Element}
|
|
*/
|
|
listProto.getItemGraphicEl = function (idx) {
|
|
return this._graphicEls[idx];
|
|
};
|
|
|
|
/**
|
|
* @param {Function} cb
|
|
* @param {*} context
|
|
*/
|
|
listProto.eachItemGraphicEl = function (cb, context) {
|
|
zrUtil.each(this._graphicEls, function (el, idx) {
|
|
if (el) {
|
|
cb && cb.call(context, el, idx);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Shallow clone a new list except visual and layout properties, and graph elements.
|
|
* New list only change the indices.
|
|
*/
|
|
listProto.cloneShallow = function () {
|
|
var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
|
|
var list = new List(dimensionInfoList, this.hostModel);
|
|
|
|
// FIXME
|
|
list._storage = this._storage;
|
|
|
|
transferImmuProperties(list, this);
|
|
|
|
list.indices = this.indices.slice();
|
|
|
|
return list;
|
|
};
|
|
|
|
/**
|
|
* Wrap some method to add more feature
|
|
* @param {string} methodName
|
|
* @param {Function} injectFunction
|
|
*/
|
|
listProto.wrapMethod = function (methodName, injectFunction) {
|
|
var originalMethod = this[methodName];
|
|
if (typeof originalMethod !== 'function') {
|
|
return;
|
|
}
|
|
this.__wrappedMethods = this.__wrappedMethods || [];
|
|
this.__wrappedMethods.push(methodName);
|
|
this[methodName] = function () {
|
|
var res = originalMethod.apply(this, arguments);
|
|
return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments)));
|
|
};
|
|
};
|
|
|
|
// Methods that create a new list based on this list should be listed here.
|
|
// Notice that those method should `RETURN` the new list.
|
|
listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map'];
|
|
// Methods that change indices of this list should be listed here.
|
|
listProto.CHANGABLE_METHODS = ['filterSelf'];
|
|
|
|
return List;
|
|
}); |