提交 36270ab1 编写于 作者: S sushuang

(1) Support dataset.

(2) Support category collection for category axis.
上级 1d7f90ba
export * from './src/echarts';
export * from './src/export';
import './src/component/dataset';
// Import all charts and components
import './src/chart/line';
import './src/chart/bar';
......
export * from './src/echarts';
export * from './src/export';
import './src/component/dataset';
export * from './src/echarts';
export * from './src/export';
import './src/component/dataset';
import './src/chart/line';
import './src/chart/bar';
import './src/chart/pie';
......
export * from './src/echarts';
import './src/component/dataset';
import './src/chart/line';
import './src/chart/bar';
import './src/chart/pie';
......
......@@ -2,8 +2,9 @@ import {__DEV__} from '../../config';
import * as zrUtil from 'zrender/src/core/util';
import List from '../../data/List';
import completeDimensions from '../../data/helper/completeDimensions';
import {getDataItemValue, converDataValue, isDataItemOption} from '../../util/model';
import {getDataItemValue} from '../../util/model';
import CoordinateSystem from '../../CoordinateSystem';
import {getCoordSysDefineBySeries} from '../../model/referHelper';
function firstDataNotNull(data) {
var i = 0;
......@@ -12,7 +13,7 @@ function firstDataNotNull(data) {
}
return data[i];
}
function ifNeedCompleteOrdinalData(data) {
function isNeedCompleteOrdinalData(data) {
var sampleItem = firstDataNotNull(data);
return sampleItem != null
&& !zrUtil.isArray(getDataItemValue(sampleItem));
......@@ -21,9 +22,10 @@ function ifNeedCompleteOrdinalData(data) {
/**
* Helper function to create a list from option data
*/
function createListFromArray(data, seriesModel, ecModel) {
// If data is undefined
data = data || [];
function doCreateListFromArray(seriesDataAttr, seriesModel, ecModel) {
var source = seriesModel.getSource(seriesDataAttr);
// Consider empty data.
var data = source.data || [];
if (__DEV__) {
if (!zrUtil.isArrayLike(data)) {
......@@ -34,84 +36,75 @@ function createListFromArray(data, seriesModel, ecModel) {
var isDataTypedArray = zrUtil.isTypedArray(data);
if (isDataTypedArray) {
if (__DEV__) {
if (!seriesModel.get('dimensions')) {
if (!source.dimensions) {
throw new Error('dimensions must be given if data is a ' + Object.prototype.toString.call(data));
}
}
}
var coordSysName = seriesModel.get('coordinateSystem');
var creator = creators[coordSysName];
var registeredCoordSys = CoordinateSystem.get(coordSysName);
var completeDimOpt = {
encodeDef: seriesModel.get('encode'),
dimsDef: seriesModel.get('dimensions')
encodeDef: source.encode,
dimsDef: source.dimensions
};
if (isDataTypedArray) {
completeDimOpt.dimCount = completeDimOpt.dimsDef.length;
}
// FIXME
var axesInfo = creator && creator(data, seriesModel, ecModel, completeDimOpt);
var dimensions = axesInfo && axesInfo.dimensions;
var coordSysName = seriesModel.get('coordinateSystem');
var registeredCoordSys = CoordinateSystem.get(coordSysName);
var coordSysDefine = getCoordSysDefineBySeries(seriesModel);
var coordSysDimDefs;
if (!dimensions) {
if (coordSysDefine) {
coordSysDimDefs = zrUtil.map(coordSysDefine.coordSysDims, function (dim) {
var dimInfo = {name: dim};
var axisModel = coordSysDefine.axisMap.get(dim);
if (axisModel) {
var axisType = axisModel.get('type');
dimInfo.type = getDimTypeByAxis(axisType);
dimInfo.stackable = isStackable(axisType);
}
return dimInfo;
});
}
if (!coordSysDimDefs) {
// Get dimensions from registered coordinate system
dimensions = (registeredCoordSys && (
coordSysDimDefs = (registeredCoordSys && (
registeredCoordSys.getDimensionsInfo
? registeredCoordSys.getDimensionsInfo()
: registeredCoordSys.dimensions.slice()
)) || ['x', 'y'];
dimensions = completeDimensions(dimensions, data, completeDimOpt);
}
var categoryIndex = axesInfo ? axesInfo.categoryIndex : -1;
var dimInfoList = completeDimensions(coordSysDimDefs, data, completeDimOpt);
var list = new List(dimensions, seriesModel);
var firstCategoryDimIndex;
coordSysDefine && zrUtil.each(dimInfoList, function (dimInfo, dimIndex) {
var coordDim = dimInfo.coordDim;
var categoryAxisModel = coordSysDefine.categoryAxisMap.get(coordDim);
if (categoryAxisModel) {
firstCategoryDimIndex == null && (firstCategoryDimIndex = dimIndex);
categoryAxisModel.ordinalMeta.prepareDimInfo(dimInfo, source);
}
});
var nameList = createNameList(axesInfo, data);
var list = new List(dimInfoList, seriesModel);
var categories = {};
var dimValueGetter = (categoryIndex >= 0 && ifNeedCompleteOrdinalData(data))
var dimValueGetter = (firstCategoryDimIndex != null && isNeedCompleteOrdinalData(data))
? function (itemOpt, dimName, dataIndex, dimIndex) {
// If any dataItem is like { value: 10 }
if (isDataItemOption(itemOpt)) {
list.hasItemOption = true;
}
// Use dataIndex as ordinal value in categoryAxis
return dimIndex === categoryIndex
return dimIndex === firstCategoryDimIndex
? dataIndex
: converDataValue(getDataItemValue(itemOpt), dimensions[dimIndex]);
: List.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex);
}
: function (itemOpt, dimName, dataIndex, dimIndex) {
var value = getDataItemValue(itemOpt);
var val = converDataValue(value && value[dimIndex], dimensions[dimIndex]);
// If any dataItem is like { value: 10 }
if (isDataItemOption(itemOpt)) {
list.hasItemOption = true;
}
var categoryAxesModels = axesInfo && axesInfo.categoryAxesModels;
if (categoryAxesModels && categoryAxesModels[dimName]) {
// If given value is a category string
if (typeof val === 'string') {
// Lazy get categories
categories[dimName] = categories[dimName]
|| categoryAxesModels[dimName].getCategories();
val = zrUtil.indexOf(categories[dimName], val);
if (val < 0 && !isNaN(val)) {
// In case some one write '1', '2' istead of 1, 2
val = +val;
}
}
}
return val;
};
: null;
list.hasItemOption = false;
list.initData(data, nameList, dimValueGetter);
list.initData(data, firstCategoryDimIndex, dimValueGetter);
return list;
}
......@@ -128,209 +121,12 @@ function getDimTypeByAxis(axisType) {
: 'float';
}
/**
* Creaters for each coord system.
*/
var creators = {
cartesian2d: function (data, seriesModel, ecModel, completeDimOpt) {
var axesModels = zrUtil.map(['xAxis', 'yAxis'], function (name) {
return ecModel.queryComponents({
mainType: name,
index: seriesModel.get(name + 'Index'),
id: seriesModel.get(name + 'Id')
})[0];
});
var xAxisModel = axesModels[0];
var yAxisModel = axesModels[1];
if (__DEV__) {
if (!xAxisModel) {
throw new Error('xAxis "' + zrUtil.retrieve(
seriesModel.get('xAxisIndex'),
seriesModel.get('xAxisId'),
0
) + '" not found');
}
if (!yAxisModel) {
throw new Error('yAxis "' + zrUtil.retrieve(
seriesModel.get('xAxisIndex'),
seriesModel.get('yAxisId'),
0
) + '" not found');
}
}
var xAxisType = xAxisModel.get('type');
var yAxisType = yAxisModel.get('type');
var dimensions = [
{
name: 'x',
type: getDimTypeByAxis(xAxisType),
stackable: isStackable(xAxisType)
},
{
name: 'y',
// If two category axes
type: getDimTypeByAxis(yAxisType),
stackable: isStackable(yAxisType)
}
];
var isXAxisCateogry = xAxisType === 'category';
var isYAxisCategory = yAxisType === 'category';
dimensions = completeDimensions(dimensions, data, completeDimOpt);
var categoryAxesModels = {};
if (isXAxisCateogry) {
categoryAxesModels.x = xAxisModel;
}
if (isYAxisCategory) {
categoryAxesModels.y = yAxisModel;
}
return {
dimensions: dimensions,
categoryIndex: isXAxisCateogry ? 0 : (isYAxisCategory ? 1 : -1),
categoryAxesModels: categoryAxesModels
};
},
singleAxis: function (data, seriesModel, ecModel, completeDimOpt) {
var singleAxisModel = ecModel.queryComponents({
mainType: 'singleAxis',
index: seriesModel.get('singleAxisIndex'),
id: seriesModel.get('singleAxisId')
})[0];
if (__DEV__) {
if (!singleAxisModel) {
throw new Error('singleAxis should be specified.');
}
}
var singleAxisType = singleAxisModel.get('type');
var isCategory = singleAxisType === 'category';
var dimensions = [{
name: 'single',
type: getDimTypeByAxis(singleAxisType),
stackable: isStackable(singleAxisType)
}];
dimensions = completeDimensions(dimensions, data, completeDimOpt);
var categoryAxesModels = {};
if (isCategory) {
categoryAxesModels.single = singleAxisModel;
}
return {
dimensions: dimensions,
categoryIndex: isCategory ? 0 : -1,
categoryAxesModels: categoryAxesModels
};
},
polar: function (data, seriesModel, ecModel, completeDimOpt) {
var polarModel = ecModel.queryComponents({
mainType: 'polar',
index: seriesModel.get('polarIndex'),
id: seriesModel.get('polarId')
})[0];
var angleAxisModel = polarModel.findAxisModel('angleAxis');
var radiusAxisModel = polarModel.findAxisModel('radiusAxis');
if (__DEV__) {
if (!angleAxisModel) {
throw new Error('angleAxis option not found');
}
if (!radiusAxisModel) {
throw new Error('radiusAxis option not found');
}
}
var radiusAxisType = radiusAxisModel.get('type');
var angleAxisType = angleAxisModel.get('type');
var dimensions = [
{
name: 'radius',
type: getDimTypeByAxis(radiusAxisType),
stackable: isStackable(radiusAxisType)
},
{
name: 'angle',
type: getDimTypeByAxis(angleAxisType),
stackable: isStackable(angleAxisType)
}
];
var isAngleAxisCateogry = angleAxisType === 'category';
var isRadiusAxisCateogry = radiusAxisType === 'category';
dimensions = completeDimensions(dimensions, data, completeDimOpt);
var categoryAxesModels = {};
if (isRadiusAxisCateogry) {
categoryAxesModels.radius = radiusAxisModel;
}
if (isAngleAxisCateogry) {
categoryAxesModels.angle = angleAxisModel;
}
return {
dimensions: dimensions,
categoryIndex: isAngleAxisCateogry ? 1 : (isRadiusAxisCateogry ? 0 : -1),
categoryAxesModels: categoryAxesModels
};
},
geo: function (data, seriesModel, ecModel, completeDimOpt) {
// TODO Region
// 多个散点图系列在同一个地区的时候
return {
dimensions: completeDimensions([
{name: 'lng'},
{name: 'lat'}
], data, completeDimOpt)
};
}
};
function createNameList(result, data) {
var nameList = [];
var categoryDim = result && result.dimensions[result.categoryIndex];
var categoryAxisModel;
if (categoryDim) {
categoryAxisModel = result.categoryAxesModels[categoryDim.name];
}
if (categoryAxisModel) {
// FIXME Two category axis
var categories = categoryAxisModel.getCategories();
if (categories) {
var dataLen = data.length;
// Ordered data is given explicitly like
// [[3, 0.2], [1, 0.3], [2, 0.15]]
// or given scatter data,
// pick the category
if (zrUtil.isArray(data[0]) && data[0].length > 1) {
nameList = [];
for (var i = 0; i < dataLen; i++) {
nameList[i] = categories[data[i][result.categoryIndex || 0]];
}
}
else {
nameList = categories.slice(0);
}
}
}
return nameList;
function createListFromArray(data, seriesModel, ecModel) {
// Compatibal with previous interface, the first parameter is `data`,
// but the meaning of "data" it is not used currently, only the meaning
// of "seriesDataAttr" used.
!zrUtil.isString(data) && (data = null);
return doCreateListFromArray(data, seriesModel, ecModel);
}
export default createListFromArray;
......@@ -108,7 +108,7 @@ export default SeriesModel.extend({
});
function translateCategoryValue(axisModel, dim, rawData) {
var axisData = axisModel.get('data');
var axisData = axisModel.getCategories();
var numberDim = convertDimNameToNumber(dim);
if (axisData && axisData.length) {
......
......@@ -107,7 +107,7 @@ export default AxisView.extend({
_axisLabel: function (angleAxisModel, polar, ticksAngles, radiusExtent) {
var axis = angleAxisModel.axis;
var categoryData = angleAxisModel.get('data');
var categoryData = angleAxisModel.getCategories();
var labelModel = angleAxisModel.getModel('axisLabel');
var labels = angleAxisModel.getFormattedLabels();
......
......@@ -659,7 +659,7 @@ function buildAxisLabel(axisBuilder, axisModel, opt) {
) * PI / 180;
var labelLayout = innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);
var categoryData = axisModel.get('data');
var categoryData = axisModel.getCategories();
var labelEls = [];
var silent = isSilent(axisModel);
......
......@@ -394,7 +394,7 @@ function fixExtentByAxis(axisProxy, dataExtent) {
// For category axis, if min/max/scale are not set, extent is determined
// by axis.data by default.
var isCategoryAxis = axisModel.get('type') === 'category';
var axisDataLen = isCategoryAxis && (axisModel.get('data') || []).length;
var axisDataLen = isCategoryAxis && axisModel.getCategories().length;
if (min != null && min !== 'dataMin' && typeof min !== 'function') {
dataExtent[0] = min;
......
import './dataset/DatasetModel';
import * as echarts from '../echarts';
echarts.extendComponentView({type: 'dataset'});
import * as echarts from '../../echarts';
var DatasetModel = echarts.extendComponentModel({
type: 'dataset',
/**
* @protected
*/
defaultOption: {
sourceType: 'rows', // 'rows', 'columns', 'objects'
source: null
},
/**
* @override
*/
optionUpdated: function () {
parseData(this.option);
}
});
function parseData(option) {
var sourceType = option.sourceType;
var parser = parsers[sourceType];
if (parser && option.source) {
option.data = parser(option.source, option);
}
option.source = null;
}
var parsers = {
rows: function (source, option) {
return source;
},
columns: function (source, option) {
// TODO
},
objects: function (source, option) {
// TODO
}
};
export default DatasetModel;
......@@ -445,6 +445,7 @@ featureManager.register('dataView', DataView);
echarts.registerAction({
type: 'changeDataView',
event: 'dataViewChanged',
// ???
update: 'prepareAndUpdate'
}, function (payload, ecModel) {
var newSeriesOptList = [];
......
......@@ -26,7 +26,7 @@ export function getScaleExtent(scale, model) {
var boundaryGap;
var span;
if (scaleType === 'ordinal') {
axisDataLen = (model.get('data') || []).length;
axisDataLen = model.getCategories().length;
}
else {
boundaryGap = model.get('boundaryGap');
......@@ -159,7 +159,7 @@ export function createScaleByModel(model, axisType) {
// Buildin scale
case 'category':
return new OrdinalScale(
model.getCategories(), [Infinity, -Infinity]
model.ordinalMeta, [Infinity, -Infinity]
);
case 'value':
return new IntervalScale();
......
import * as zrUtil from 'zrender/src/core/util';
import * as axisHelper from './axisHelper';
function getName(obj) {
if (zrUtil.isObject(obj) && obj.value != null) {
return obj.value;
}
else {
return obj + '';
}
}
export default {
/**
......@@ -23,14 +14,6 @@ export default {
);
},
/**
* Get categories
*/
getCategories: function () {
return this.get('type') === 'category'
&& zrUtil.map(this.get('data'), getName);
},
/**
* @param {boolean} origin
* @return {number|string} min value or 'dataMin' or null/undefined (means auto) or NaN
......
......@@ -5,6 +5,8 @@ import {
getLayoutParams,
mergeLayoutParam
} from '../util/layout';
import OrdinalMeta from '../data/OrdinalMeta';
// FIXME axisType is fixed ?
var AXIS_TYPES = ['value', 'category', 'time', 'log'];
......@@ -22,8 +24,16 @@ export default function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraD
BaseAxisModelClass.extend({
/**
* @readOnly
*/
type: axisName + 'Axis.' + axisType,
/**
* @readOnly
*/
ordinalMeta: null,
mergeDefaultAndTheme: function (option, ecModel) {
var layoutMode = this.layoutMode;
var inputPositionParams = layoutMode
......@@ -40,6 +50,22 @@ export default function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraD
}
},
/**
* @override
*/
optionUpdated: function () {
var thisOption = this.option;
if (thisOption.type === 'category') {
this.ordinalMeta = new OrdinalMeta(this);
}
},
getCategories: function () {
if (this.option.type === 'category') {
return this.ordinalMeta.categories;
}
},
defaultOption: zrUtil.mergeAll(
[
{},
......@@ -55,4 +81,4 @@ export default function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraD
axisName + 'Axis',
zrUtil.curry(axisTypeDefaulter, axisName)
);
}
\ No newline at end of file
}
......@@ -8,6 +8,7 @@ import * as zrUtil from 'zrender/src/core/util';
import Model from '../model/Model';
import DataDiffer from './DataDiffer';
import * as modelUtil from '../util/model';
import {parseDate} from '../util/number';
var isObject = zrUtil.isObject;
......@@ -146,6 +147,10 @@ var typedArrayProviderMethods = {
* @param {Array.<string|Object>} dimensions
* For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
* Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
* Spetial fields: {
* ordinalMeta: <module:echarts/data/OrdinalMeta>,
* sourceModelUID: <string>
* }
* @param {module:echarts/model/Model} hostModel
*/
var List = function (dimensions, hostModel) {
......@@ -368,16 +373,21 @@ listProto.parseDimensions = function (dims) {
* 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)]);
// Do not clone, because there may be categories in dimInfo.
return this._dimensionInfos[this.getDimension(dim)];
};
/**
* Initialize from data
* @param {Array.<Object|number|Array>} data
* @param {Array.<string>} [nameList]
* @param {number|Array.<string>} [defaultNameDimIndex] The name of a datum is used on data diff and
* defualt label/tooltip. It can be specified in raw data, or by nameList provided outside
* (usually provided categroy axis).
* If it is a number, the default name is fetched from the dim.
* If it is an array, it is nameList (this approach has been deprecated).
* @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
*/
listProto.initData = function (data, nameList, dimValueGetter) {
listProto.initData = function (data, defaultNameDimIndex, dimValueGetter) {
data = data || [];
var isDataArray = zrUtil.isArrayLike(data);
......@@ -396,32 +406,24 @@ listProto.initData = function (data, nameList, dimValueGetter) {
this._storage = {};
this._indices = null;
var dimensionInfoMap = this._dimensionInfos;
this._nameList = [];
// @deprecated
if (zrUtil.isArray(defaultNameDimIndex)) {
this._nameList = defaultNameDimIndex;
defaultNameDimIndex = null;
}
this._defaultNameDimIndex = defaultNameDimIndex;
this._nameList = nameList = nameList || [];
this._idList = [];
this._nameRepeatCount = {};
var self = this;
if (!dimValueGetter) {
this.hasItemOption = false;
}
// Default dim value getter
this._dimValueGetter = dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) {
var value = modelUtil.getDataItemValue(dataItem);
// If any dataItem is like { value: 10 }
if (!data.pure && modelUtil.isDataItemOption(dataItem)) {
self.hasItemOption = true;
}
return modelUtil.converDataValue(
(value instanceof Array)
? value[dimIndex]
// If value is a single number or something else not array.
: value,
dimensionInfoMap[dimName]
);
};
this._dimValueGetter = dimValueGetter = dimValueGetter
|| defaultDimValueGetter;
// Reset raw extent.
this._rawExtent = {};
......@@ -461,6 +463,7 @@ listProto._initDataFromProvider = function (start, end) {
var dimensions = this.dimensions;
var dimensionInfoMap = this._dimensionInfos;
var nameList = this._nameList;
var defaultNameDimIndex = this._defaultNameDimIndex;
var idList = this._idList;
var rawExtent = this._rawExtent;
var nameRepeatCount = this._nameRepeatCount = {};
......@@ -531,19 +534,37 @@ listProto._initDataFromProvider = function (start, end) {
// Use the name in option and create id
if (!rawData.pure) {
if (!nameList[idx] && dataItem) {
var name = null;
if (dataItem) {
if (dataItem.name != null) {
nameList[idx] = dataItem.name;
name = dataItem.name;
}
else if (nameDimIdx != null) {
nameList[idx] = storage[dimensions[nameDimIdx]][chunkIndex][chunkOffset];
name = storage[dimensions[nameDimIdx]][chunkIndex][chunkOffset];
}
}
if (name == null) {
if (defaultNameDimIndex != null) {
var dimInfo = dimensionInfoMap[dimensions[defaultNameDimIndex]];
var ordinalMeta = dimInfo.ordinalMeta;
if (ordinalMeta) {
name = ordinalMeta.categories[idx];
}
}
else {
// If nameList is given.
name = nameList[idx];
}
}
var name = nameList[idx];
nameList[idx] = name;
// Try using the id in option
var id = dataItem && dataItem.id;
if (id == null && name) {
if (id == null && name != null) {
// Use name as id and add counter to avoid same name
nameRepeatCount[name] = nameRepeatCount[name] || 0;
id = name;
......@@ -560,6 +581,7 @@ listProto._initDataFromProvider = function (start, end) {
// Clean unused data if data source is typed array.
rawData.clean();
}
this._count = end;
};
......@@ -1620,4 +1642,54 @@ listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map'];
// Methods that change indices of this list should be listed here.
// listProto.CHANGABLE_METHODS = ['filterSelf'];
var defaultDimValueGetter = List.defaultDimValueGetter = function (dataItem, dimName, dataIndex, dimIndex) {
var value = modelUtil.getDataItemValue(dataItem);
// If any dataItem is like { value: 10 }
if (!this._rawData.pure && modelUtil.isDataItemOption(dataItem)) {
this.hasItemOption = true;
}
return converDataValue(
(value instanceof Array)
? value[dimIndex]
// If value is a single number or something else not array.
: value,
this._dimensionInfos[dimName]
);
};
/**
* This helper method convert value in data.
* @param {string|number|Date} value
* @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'.
* If "dimInfo.ordinalParseAndSave", ordinal value can be parsed.
*/
function converDataValue(value, dimInfo) {
// Performance sensitive.
var dimType = dimInfo && dimInfo.type;
if (dimType === 'ordinal') {
// If given value is a category string
var ordinalMeta = dimInfo && dimInfo.ordinalMeta;
return !ordinalMeta
? value
: typeof value === 'string'
? ordinalMeta.parseAndCollect(value, dimInfo.sourceModelUID)
: NaN;
}
if (dimType === 'time'
// spead up when using timestamp
&& typeof value !== 'number'
&& value != null
&& value !== '-'
) {
value = +parseDate(value);
}
// dimType defaults 'number'.
// If dimType is not ordinal and value is null or undefined or NaN or '-',
// parse to NaN.
return (value == null || value === '')
? NaN : +value; // If string (like '-'), using '+' parse to NaN
};
export default List;
import {createHashMap, isObject, map} from 'zrender/src/core/util';
var MULTIPLE_SOURCE = 'multiple_source';
// Used to avoid null/undefined comparison.
var NO_SOURCE = 'no_source';
/**
* @constructor
* @param {module:echart/model/Model} axisModel
*/
function OrdinalMeta(axisModel) {
var data = axisModel.option.data;
var categories = data && map(data, getName);
/**
* @readOnly
* @type {Array.<string>}
*/
this.categories = categories || [];
/**
* @private
* @type {boolean}
*/
this._needCollect = !categories;
/**
* @private
* @type {string}
*/
this._sourceModelUID = categories ? axisModel.uid : NO_SOURCE;
/**
* @private
* @type {boolean}
*/
this._map;
}
var proto = OrdinalMeta.prototype;
/**
* @param {string} category
* @return {number} ordinal
*/
proto.getOrdinal = function (category) {
return getOrCreateMap(this).get(category);
};
/**
* @param {string} category
* @param {string} [dimSourceModelUID]
* @return {number} The ordinal. If not found, return NaN.
*/
proto.parseAndCollect = function (category, dimSourceModelUID) {
var index;
var needCollect = this._needCollect;
// Optimize for the scenario: Only a dataset dimension provide categroies
// and many series use the dimension. We avoid to create map.
if (needCollect && dimSourceModelUID === this._sourceModelUID) {
index = this.categories.length;
this.categories[index] = category;
return index;
}
var map = getOrCreateMap(this);
index = map.get(category);
if (index == null) {
if (needCollect) {
index = this.categories.length;
this.categories[index] = category;
map.set(category, index);
}
else {
index = NaN;
}
}
return index;
};
proto.prepareDimInfo = function (dimInfo, source) {
dimInfo.ordinalMeta = this;
addSourceModelUID(this, source.modelUID);
dimInfo.sourceModelUID = source.modelUID;
};
/**
* Consider these cases:
* (1) A category axis provides data (categories) and
* many series refer the axis.
* (2) Only a dataset dimension provide categroies and many
* series use the dimension.
*
* In those cases above, categoryMap is not needed to be
* created. So we use sourceModelUID to make this optimization.
* @private
*/
function addSourceModelUID(ordinalMeta, sourceModelUID) {
if (ordinalMeta._needCollect) {
ordinalMeta._sourceModelUID = ordinalMeta._sourceModelUID === NO_SOURCE
? sourceModelUID
: MULTIPLE_SOURCE;
}
};
// Do not create map until needed.
function getOrCreateMap(ordinalMeta) {
return ordinalMeta._map || (
ordinalMeta._map = createHashMap(ordinalMeta.categories)
);
};
function getName(obj) {
if (isObject(obj) && obj.value != null) {
return obj.value;
}
else {
return obj + '';
}
}
export default OrdinalMeta;
......@@ -12,6 +12,8 @@ var defaults = zrUtil.defaults;
var OTHER_DIMS = {tooltip: 1, label: 1, itemName: 1};
/**
* @see {module:echarts/test/ut/spec/data/completeDimensions}
*
* Complete the dimensions array, by user defined `dimension` and `encode`,
* and guessing from the data structure.
* If no 'value' dimension specified, the first no-named dimension will be
......@@ -60,6 +62,12 @@ function completeDimensions(sysDims, data, opt) {
var dimCount = opt.dimCount;
if (dimCount == null) {
// ??? TODO
// Originally detect dimCount by data[0]. Should we
// optimize it to only by sysDims and dimensions and encode.
// So only necessary dims will be initialized.
// But custom series should be considered. where other dims
// may be visited.
var value0 = retrieveValue(data[0]);
dimCount = Math.max(
zrUtil.isArray(value0) && value0.length || 1,
......
import {makeInner} from '../../util/model';
import {getCoordSysDefineBySeries} from '../../model/referHelper';
import {createHashMap, each} from 'zrender/src/core/util';
var inner = makeInner();
export function resetDefaultEncode(ecModel) {
inner(ecModel).datasetMap = createHashMap();
}
/**
* [Caution]:
* MUST be called after series option merged and
* before "series.getInitailData()" called.
*
* [Rule]:
* Category axis (if exists) alway map to the first dimension.
* Each other axis occupies a subsequent dimension.
*
* [Why make default encode]:
* Simplify the typing of encode in option, avoiding the case like that:
* series: [{encode: {x: 0, y: 1}}, {encode: {x: 0, y: 2}}, {encode: {x: 0, y: 3}}],
* where the "y" have to be manually typed as "1, 2, 3, ...".
*
* @return {Object} The object of default encode.
*/
export function makeDefaultEncode(seriesModel) {
var ecModel = seriesModel.ecModel;
var datasetMap = inner(ecModel).datasetMap;
var coordSysDefine = getCoordSysDefineBySeries(seriesModel);
if (!coordSysDefine) {
// Usually in this case series will use the first data
// dimension as the "value" dimension, or other default
// processes respectively.
// ??? clear default incode
return;
}
var datasetModel = getDatasetModel(seriesModel);
if (!datasetModel) {
return;
}
var datasetUID = datasetModel.uid;
var datasetRecord = datasetMap.get(datasetUID)
|| datasetMap.set(datasetUID, {categoryWayDim: 1, valueWayDim: 0});
var encode = {};
each(coordSysDefine.coordSysDims, function (dim) {
encode[dim] = coordSysDefine.firstCategoryDimIndex == null
// In value way.
? datasetRecord.valueWayDim++
// In category way.
: coordSysDefine.categoryAxisMap.get(dim)
? 0
: datasetRecord.categoryWayDim++;
});
return encode;
}
export function getDatasetModel(seriesModel) {
var thisData = seriesModel.option.data;
return seriesModel.ecModel.getComponent(
'dataset', thisData && thisData.datasetIndex || 0
);
}
......@@ -787,7 +787,7 @@ var updateMethods = {
// deteming whether use progressive rendering.
updateStreamModes(this, ecModel);
stackSeriesData.call(this, ecModel);
stackSeriesData(ecModel);
coordSysMgr.update(ecModel, api);
......
......@@ -12,7 +12,6 @@ import {makeInner} from '../util/model';
import * as layout from '../util/layout';
import boxLayoutMixin from './mixin/boxLayout';
var arrayPush = Array.prototype.push;
var inner = makeInner();
/**
......@@ -184,12 +183,20 @@ componentUtil.enableTopologicalTravel(ComponentModel, getDependencies);
function getDependencies(componentType) {
var deps = [];
zrUtil.each(ComponentModel.getClassesByMainType(componentType), function (Clazz) {
arrayPush.apply(deps, Clazz.prototype.dependencies || []);
deps = deps.concat(Clazz.prototype.dependencies || []);
});
// Ensure main type
return zrUtil.map(deps, function (type) {
// Ensure main type.
deps = zrUtil.map(deps, function (type) {
return parseClassType(type).main;
});
// Hack dataset for convenience.
if (componentType !== 'dataset' && zrUtil.indexOf(deps, 'dataset') <= 0) {
deps.unshift('dataset');
}
return deps;
}
zrUtil.mixin(ComponentModel, boxLayoutMixin);
......
......@@ -28,6 +28,7 @@ import Model from './Model';
import ComponentModel from './Component';
import globalDefault from './globalDefault';
import colorPaletteMinin from './mixin/colorPalette';
import {resetDefaultEncode} from '../data/helper/sourceHelper';
// import {createTask} from '../stream/task';
var OPTION_INNER_KEY = '\0_ec_inner';
......@@ -125,9 +126,11 @@ var GlobalModel = Model.extend({
var option = this.option;
var componentsMap = this._componentsMap;
var newCptTypes = [];
// var globalSettingTask = this.settingTask;
// 如果不存在对应的 component model 则直接 merge
resetDefaultEncode(this);
// If no component class, merge directly.
// For example: color, animaiton options, etc.
each(newOption, function (componentOption, mainType) {
if (componentOption == null) {
return;
......@@ -139,20 +142,15 @@ var GlobalModel = Model.extend({
? clone(componentOption)
: merge(option[mainType], componentOption, true);
}
else {
else if (mainType) {
newCptTypes.push(mainType);
}
});
// FIXME OPTION 同步是否要改回原来的
ComponentModel.topologicalTravel(
newCptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this
);
this._seriesIndicesMap = createHashMap(
this._seriesIndices = this._seriesIndices || []
);
function visitComponent(mainType, dependencies) {
var newCptOptionList = modelUtil.normalizeToArray(newOption[mainType]);
......@@ -238,6 +236,10 @@ var GlobalModel = Model.extend({
createSeriesIndices(this, componentsMap.get('series'));
}
}
this._seriesIndicesMap = createHashMap(
this._seriesIndices = this._seriesIndices || []
);
},
/**
......@@ -550,7 +552,7 @@ var GlobalModel = Model.extend({
*/
isSeriesFiltered: function (seriesModel) {
assertSeriesInitialized(this);
return !this._seriesIndicesMap.get(seriesModel.componentIndex);
return this._seriesIndicesMap.get(seriesModel.componentIndex) == null;
},
/**
......
......@@ -15,6 +15,10 @@ import {
mergeLayoutParam
} from '../util/layout';
import {createTask} from '../stream/task';
import {
getDatasetModel,
makeDefaultEncode
} from '../data/helper/sourceHelper';
var inner = modelUtil.makeInner();
......@@ -73,6 +77,8 @@ var SeriesModel = ComponentModel.extend({
this.mergeDefaultAndTheme(option, ecModel);
setDefaultEncode(this);
var data = this.getInitialData(option, ecModel);
if (__DEV__) {
......@@ -140,6 +146,8 @@ var SeriesModel = ComponentModel.extend({
mergeLayoutParam(this.option, newSeriesOption, layoutMode);
}
setDefaultEncode(this);
var data = this.getInitialData(newSeriesOption, ecModel);
// ??? set dirty on ecModel, becusue it will call mergeOption({})?
this.dataTask.dirty();
......@@ -191,6 +199,72 @@ var SeriesModel = ComponentModel.extend({
inner(this).data = data;
},
/**
* Usage:
* (1) Provide source data directly:
* series: {
* encode: {...},
* dimensions: [...]
* data: [[...]]
* }
*
* (2) Ignore datasetIndex means `datasetIndex: 0`,
* and the dimensions defination in dataset is used:
* series: {
* encode: {...}
* }
*
* (3) Use different datasets, and the dimensions defination
* in dataset is used:
* series: {
* nodes: {datasetIndex: 1, encode: {...}},
* links: {datasetIndex: 2, encode: {...}}
* }
*
* Get data from series itself or datset.
* @param {string} [seriesDataAttr='data'] Or can be like 'nodes', 'links'
* @return {Object}
* {
* modelUID,
* data,
* dimensions,
* encode,
* }
*/
getSource: function (seriesDataAttr) {
seriesDataAttr = seriesDataAttr || 'data';
var thisOption = this.option;
var thisData = thisOption.data;
var dimensions = thisOption.dimensions;
var data;
var modelUID;
if (thisData && thisData.datasetIndex == null) {
data = thisData;
modelUID = this.uid;
}
else {
var datasetModel = getDatasetModel(this);
if (datasetModel) {
var datasetOption = datasetModel.option;
if (datasetOption) {
data = datasetOption.data;
modelUID = datasetModel.uid;
dimensions = datasetOption.dimensions;
dimensions && (dimensions = dimensions.slice());
}
}
}
return {
modelUID: modelUID,
data: data,
dimensions: dimensions,
encode: inner(this).encode
};
},
/**
* Get data before processed
* @return {module:echarts/data/List}
......@@ -246,6 +320,8 @@ var SeriesModel = ComponentModel.extend({
*/
formatTooltip: function (dataIndex, multipleSeries, dataType) {
function formatArrayValue(value) {
// ???
// check: category-no-encode-has-axis-data in dataset.html
var vertially = zrUtil.reduce(value, function (vertially, val, idx) {
var dimItem = data.getDimensionInfo(idx);
return vertially |= dimItem && dimItem.tooltip !== false && dimItem.tooltipName != null;
......@@ -401,4 +477,15 @@ function dataTaskProgress(param, context) {
context.model.getRawData().cloneShallow(context.outputData);
}
function setDefaultEncode(seriesModel) {
inner(seriesModel).encode = getOptionEncode(seriesModel)
|| makeDefaultEncode(seriesModel);
}
function getOptionEncode(seriesModel) {
var thisOption = seriesModel.option;
var thisData = thisOption.data;
return thisData && thisData.encode || thisOption.encode;
}
export default SeriesModel;
\ No newline at end of file
/**
* Helper for model references.
* There are many manners to refer axis/coordSys.
*/
// TODO
// merge relevant logic to this file?
// check: "modelHelper" of tooltip and "BrushTargetManager".
import {__DEV__} from '../config';
import {createHashMap, retrieve} from 'zrender/src/core/util';
/**
* @return {Object} For example:
* {
* coordSysName: 'cartesian2d',
* coordSysDims: ['x', 'y', ...],
* axisMap: HashMap({
* x: xAxisModel,
* y: yAxisModel
* }),
* categoryAxisMap: HashMap({
* x: xAxisModel,
* y: undefined
* }),
* // It also indicate that whether there is category axis.
* firstCategoryDimIndex: 1,
* }
*/
export function getCoordSysDefineBySeries(seriesModel) {
var coordSysName = seriesModel.get('coordinateSystem');
var result = {
coordSysName: coordSysName,
coordSysDims: [],
axisMap: createHashMap(),
categoryAxisMap: createHashMap()
};
var fetch = fetchers[coordSysName];
if (fetch) {
fetch(seriesModel, result, result.axisMap, result.categoryAxisMap);
return result;
}
}
var fetchers = {
cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) {
var xAxisModel = seriesModel.getReferringComponents('xAxis')[0];
var yAxisModel = seriesModel.getReferringComponents('yAxis')[0];
if (__DEV__) {
if (!xAxisModel) {
throw new Error('xAxis "' + retrieve(
seriesModel.get('xAxisIndex'),
seriesModel.get('xAxisId'),
0
) + '" not found');
}
if (!yAxisModel) {
throw new Error('yAxis "' + retrieve(
seriesModel.get('xAxisIndex'),
seriesModel.get('yAxisId'),
0
) + '" not found');
}
}
result.coordSysDims = ['x', 'y'];
axisMap.set('x', xAxisModel);
axisMap.set('y', yAxisModel);
if (isCategory(xAxisModel)) {
categoryAxisMap.set('x', xAxisModel);
result.firstCategoryDimIndex = 0;
}
if (isCategory(yAxisModel)) {
categoryAxisMap.set('y', yAxisModel);
result.firstCategoryDimIndex = 1;
}
},
singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) {
var singleAxisModel = seriesModel.getReferringComponents('singleAxis')[0];
if (__DEV__) {
if (!singleAxisModel) {
throw new Error('singleAxis should be specified.');
}
}
result.coordSysDims = ['singleAxis'];
axisMap.set('single', singleAxisModel);
if (isCategory(singleAxisModel)) {
categoryAxisMap.set('single', singleAxisModel);
result.firstCategoryDimIndex = 0;
}
},
polar: function (seriesModel, result, axisMap, categoryAxisMap) {
var polarModel = seriesModel.getReferringComponents('polar')[0];
var radiusAxisModel = polarModel.findAxisModel('radiusAxis');
var angleAxisModel = polarModel.findAxisModel('angleAxis');
if (__DEV__) {
if (!angleAxisModel) {
throw new Error('angleAxis option not found');
}
if (!radiusAxisModel) {
throw new Error('radiusAxis option not found');
}
}
result.coordSysDims = ['radius', 'angle'];
axisMap.set('radius', radiusAxisModel);
axisMap.set('angle', angleAxisModel);
if (isCategory(radiusAxisModel)) {
categoryAxisMap.set('radius', radiusAxisModel);
result.firstCategoryDimIndex = 0;
}
if (isCategory(angleAxisModel)) {
categoryAxisMap.set('angle', angleAxisModel);
result.firstCategoryDimIndex = 1;
}
},
geo: function (seriesModel, result, axisMap, categoryAxisMap) {
result.coordSysDims = ['lng', 'lat'];
}
};
function isCategory(axisModel) {
return axisModel.get('type') === 'category';
}
......@@ -16,14 +16,14 @@ var OrdinalScale = Scale.extend({
type: 'ordinal',
init: function (data, extent) {
this._data = data;
this._extent = extent || [0, data.length - 1];
init: function (ordinalMeta, extent) {
this._ordinalMeta = ordinalMeta;
this._extent = extent || [0, ordinalMeta.categories.length - 1];
},
parse: function (val) {
return typeof val === 'string'
? zrUtil.indexOf(this._data, val)
? this._ordinalMeta.getOrdinal(val)
// val might be float.
: Math.round(val);
},
......@@ -31,7 +31,7 @@ var OrdinalScale = Scale.extend({
contain: function (rank) {
rank = this.parse(rank);
return scaleProto.contain.call(this, rank)
&& this._data[rank] != null;
&& this._ordinalMeta.categories[rank] != null;
},
/**
......@@ -69,7 +69,7 @@ var OrdinalScale = Scale.extend({
* @return {string}
*/
getLabel: function (n) {
return this._data[n];
return this._ordinalMeta.categories[n];
},
/**
......
......@@ -90,34 +90,6 @@ export function isDataItemOption(dataItem) {
// && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));
}
/**
* This helper method convert value in data.
* @param {string|number|Date} value
* @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'.
*/
export function converDataValue(value, dimInfo) {
// Performance sensitive.
var dimType = dimInfo && dimInfo.type;
if (dimType === 'ordinal') {
return value;
}
if (dimType === 'time'
// spead up when using timestamp
&& typeof value !== 'number'
&& value != null
&& value !== '-'
) {
value = +nubmerUtil.parseDate(value);
}
// dimType defaults 'number'.
// If dimType is not ordinal and value is null or undefined or NaN or '-',
// parse to NaN.
return (value == null || value === '')
? NaN : +value; // If string (like '-'), using '+' parse to NaN
}
// /**
// * Create a model proxy to be used in tooltip for edge data, markLine data, markPoint data.
// * @param {module:echarts/data/List} data
......@@ -691,4 +663,3 @@ export function getAttribute(dom, key) {
? dom.getAttribute(key)
: dom[key];
}
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册