From 3067fe7569a033c0c6978d7427419e5d5f7961d8 Mon Sep 17 00:00:00 2001 From: sushuang Date: Sat, 6 Jan 2018 22:52:38 +0800 Subject: [PATCH] (1) fix parallel, support encode. (2) fix category axis collect. (3) some refactor about source and create list. --- src/chart/helper/createListFromArray.js | 18 +- src/chart/parallel/ParallelSeries.js | 185 +++++++----- src/component/dataset/DatasetModel.js | 45 ++- src/coord/axisDefault.js | 8 + src/coord/axisModelCreator.js | 6 + src/data/List.js | 5 +- src/data/OrdinalMeta.js | 44 +-- src/data/Source.js | 57 ++++ src/data/helper/completeDimensions.js | 15 +- src/data/helper/guessOrdinal.js | 25 -- src/data/helper/sourceHelper.js | 187 ++++++++++-- src/model/Component.js | 1 - src/model/Series.js | 90 +----- src/model/referHelper.js | 22 +- test/parallel-aqi.html | 378 ++++++++++++++++++++---- 15 files changed, 756 insertions(+), 330 deletions(-) create mode 100644 src/data/Source.js delete mode 100644 src/data/helper/guessOrdinal.js diff --git a/src/chart/helper/createListFromArray.js b/src/chart/helper/createListFromArray.js index ff801d2a0..4e06ef875 100644 --- a/src/chart/helper/createListFromArray.js +++ b/src/chart/helper/createListFromArray.js @@ -1,17 +1,13 @@ import * as zrUtil from 'zrender/src/core/util'; import List from '../../data/List'; import createDimensions from '../../data/helper/createDimensions'; +import {getDimTypeByAxis} from '../../data/helper/sourceHelper'; import {getDataItemValue} from '../../util/model'; import CoordinateSystem from '../../CoordinateSystem'; import {getCoordSysDefineBySeries} from '../../model/referHelper'; /** - * @param {Object} source - * { - * data: mandatory - * encodeDefine: optional - * dimensionsDefine: optional - * } + * @param {module:echarts/data/Source} source * @param {module:echarts/model/Series} seriesModel */ function createListFromArray(source, seriesModel) { @@ -57,7 +53,7 @@ function createListFromArray(source, seriesModel) { var categoryAxisModel = coordSysDefine.categoryAxisMap.get(coordDim); if (categoryAxisModel) { firstCategoryDimIndex == null && (firstCategoryDimIndex = dimIndex); - categoryAxisModel.ordinalMeta.prepareDimInfo(dimInfo, source); + dimInfo.ordinalMeta = categoryAxisModel.ordinalMeta; } }); @@ -82,14 +78,6 @@ function isStackable(axisType) { return axisType !== 'category' && axisType !== 'time'; } -function getDimTypeByAxis(axisType) { - return axisType === 'category' - ? 'ordinal' - : axisType === 'time' - ? 'time' - : 'float'; -} - function firstDataNotNull(data) { var i = 0; while (i < data.length && data[i] == null) { diff --git a/src/chart/parallel/ParallelSeries.js b/src/chart/parallel/ParallelSeries.js index b036851d9..40a43c40f 100644 --- a/src/chart/parallel/ParallelSeries.js +++ b/src/chart/parallel/ParallelSeries.js @@ -1,7 +1,6 @@ -import List from '../../data/List'; -import * as zrUtil from 'zrender/src/core/util'; +import {each, createHashMap} from 'zrender/src/core/util'; import SeriesModel from '../../model/Series'; -import guessOrdinal from '../../data/helper/guessOrdinal'; +import createListFromArray from '../helper/createListFromArray'; export default SeriesModel.extend({ @@ -12,48 +11,16 @@ export default SeriesModel.extend({ visualColorAccessPath: 'lineStyle.color', getInitialData: function (option, ecModel) { - var parallelModel = ecModel.getComponent( - 'parallel', this.get('parallelIndex') - ); - var parallelAxisIndices = parallelModel.parallelAxisIndex; - - var source = this.getSource(); - var rawData = source.data; - var modelDims = parallelModel.dimensions; - - var dataDims = generateDataDims(modelDims, rawData); - - // ??? need support encode? - var dataDimsInfo = zrUtil.map(dataDims, function (dim, dimIndex) { - - var modelDimsIndex = zrUtil.indexOf(modelDims, dim); - var axisModel = modelDimsIndex >= 0 && ecModel.getComponent( - 'parallelAxis', parallelAxisIndices[modelDimsIndex] - ); - - if (axisModel && axisModel.get('type') === 'category') { - translateCategoryValue(axisModel, dim, rawData); - return {name: dim, type: 'ordinal'}; - } - else if (modelDimsIndex < 0) { - return guessOrdinal(rawData, dimIndex) - ? {name: dim, type: 'ordinal'} - : dim; - } - else { - return dim; - } - }); - - var list = new List(dataDimsInfo, this); - list.initData(rawData); - // Anication is forbiden in progressive data mode. if (this.option.progressive) { this.option.animation = false; } - return list; + var source = this.getSource(); + + setEncodeAndDimensions(source, this); + + return createListFromArray(source, this); }, /** @@ -109,50 +76,110 @@ export default SeriesModel.extend({ } }); -function translateCategoryValue(axisModel, dim, rawData) { - var axisData = axisModel.getCategories(); - var numberDim = convertDimNameToNumber(dim); +function setEncodeAndDimensions(source, seriesModel) { + // The mapping of parallelAxis dimension to data dimension can + // be specified in parallelAxis.option.dim. For example, if + // parallelAxis.option.dim is 'dim3', it mapping to the third + // dimension of data. But `data.encode` has higher priority. + // Moreover, parallelModel.dimension should not be regarded as data + // dimensions. Consider dimensions = ['dim4', 'dim2', 'dim6']; - if (axisData && axisData.length) { - zrUtil.each(rawData, function (dataItem) { - if (!dataItem) { - return; - } - // FIXME - // time consuming, should use hash? - var index = zrUtil.indexOf(axisData, dataItem[numberDim]); - dataItem[numberDim] = index >= 0 ? index : NaN; - }); + if (source.encodeDefine) { + return; + } + + var parallelModel = seriesModel.ecModel.getComponent( + 'parallel', seriesModel.get('parallelIndex') + ); + if (!parallelModel) { + return; } - // FIXME - // 如果没有设置axis data, 应自动算出,或者提示。 + + var encodeDefine = source.encodeDefine = createHashMap(); + each(parallelModel.dimensions, function (axisDim) { + var dataDimIndex = convertDimNameToNumber(axisDim); + encodeDefine.set(axisDim, dataDimIndex); + }); } function convertDimNameToNumber(dimName) { return +dimName.replace('dim', ''); } -function generateDataDims(modelDims, rawData) { - // parallelModel.dimension should not be regarded as data - // dimensions. Consider dimensions = ['dim4', 'dim2', 'dim6']; - - // We detect max dim by parallelModel.dimensions and fist - // item in rawData arbitrarily. - var maxDimNum = 0; - zrUtil.each(modelDims, function (dimName) { - var numberDim = convertDimNameToNumber(dimName); - numberDim > maxDimNum && (maxDimNum = numberDim); - }); - - var firstItem = rawData[0]; - if (firstItem && firstItem.length - 1 > maxDimNum) { - maxDimNum = firstItem.length - 1; - } - - var dataDims = []; - for (var i = 0; i <= maxDimNum; i++) { - dataDims.push('dim' + i); - } - - return dataDims; -} \ No newline at end of file +// function translateCategoryValue(axisModel, dim, rawData) { +// // ???!! +// var axisData = axisModel.getCategories(); +// var numberDim = convertDimNameToNumber(dim); + +// if (axisData && axisData.length) { +// zrUtil.each(rawData, function (dataItem) { +// if (!dataItem) { +// return; +// } +// // FIXME +// // time consuming, should use hash? +// var index = zrUtil.indexOf(axisData, dataItem[numberDim]); +// dataItem[numberDim] = index >= 0 ? index : NaN; +// }); +// } +// // FIXME +// // 如果没有设置axis data, 应自动算出,或者提示。 +// } + + +// function generateDimParams(parallelModel, source) { +// var parallelAxisIndices = parallelModel.parallelAxisIndex; +// var modelDims = parallelModel.dimensions; + +// getDimTypeByAxis + + // ??? need support encode? + // var dataDimsInfo = zrUtil.map(dataDims, function (dim, dimIndex) { + + // var modelDimsIndex = zrUtil.indexOf(modelDims, dim); + // var axisModel = modelDimsIndex >= 0 && ecModel.getComponent( + // 'parallelAxis', parallelAxisIndices[modelDimsIndex] + // ); + + // if (axisModel && axisModel.get('type') === 'category') { + // translateCategoryValue(axisModel, dim, source); + // return {name: dim, type: 'ordinal'}; + // } + // else if (modelDimsIndex < 0) { + // return guessOrdinal(source, dimIndex) + // ? {name: dim, type: 'ordinal'} + // : dim; + // } + // else { + // return dim; + // } + // }); + + // TODO + // ??? Do not support encode and dimensions setting in parallels currently. + // encode and dimension are specified by parallelAxis. +// var encodeDefine = {}; +// var sysDimensions = {}; + +// // parallelModel.dimension should not be regarded as data +// // dimensions. Consider dimensions = ['dim4', 'dim2', 'dim6']; +// var maxDimNum = 0; +// each(modelDims, function (dimName) { +// var numberDim = +dimName.replace('dim', ''); +// numberDim > maxDimNum && (maxDimNum = numberDim); +// encodeDefine[dimName] = dimName; +// }); + +// var dataDims = []; +// for (var i = 0; i <= maxDimNum; i++) { +// dataDims.push('dim' + i); +// } + +// var params = zrUtil.defaults({ +// sysDimensions: modelDims, +// dimensionsDefine: dataDims, +// encodeDefine: encodeDefine +// }, source); + +// return params; +// } diff --git a/src/component/dataset/DatasetModel.js b/src/component/dataset/DatasetModel.js index 199119bdc..348365348 100644 --- a/src/component/dataset/DatasetModel.js +++ b/src/component/dataset/DatasetModel.js @@ -1,14 +1,30 @@ import * as echarts from '../../echarts'; +import { detectSourceFormat } from '../../data/helper/sourceHelper'; var DatasetModel = echarts.extendComponentModel({ type: 'dataset', + /** + * @readOnly + */ + parsedData: null, + /** * @protected */ defaultOption: { - sourceType: 'rows', // 'rows', 'columns', 'objects' + + // 'row', 'column' + seriesLayoutBy: 'column', + + // see "module:echarts/data/helper/sourceHelper#detectSourceFormat" + sourceFormat: 'unknown', + + header: true, + + dimensions: null, + source: null }, @@ -16,30 +32,13 @@ var DatasetModel = echarts.extendComponentModel({ * @override */ optionUpdated: function () { - parseData(this.option); + var option = this.option; + var sourceFormat = option.sourceFormat; + if (sourceFormat == null || sourceFormat === 'unknown') { + option.sourceFormat = detectSourceFormat(option.source); + } } }); -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; diff --git a/src/coord/axisDefault.js b/src/coord/axisDefault.js index cd6268e3b..580a8a38c 100644 --- a/src/coord/axisDefault.js +++ b/src/coord/axisDefault.js @@ -101,6 +101,14 @@ var axisDefault = {}; axisDefault.categoryAxis = zrUtil.merge({ // 类目起始和结束两端空白策略 boundaryGap: true, + // Set false to faster category collection. + // Only usefull in the case like: category is + // ['2012-01-01', '2012-01-02', ...], where the input + // data has been ensured not duplicate and is large data. + // null means "auto": + // if axis.data provided, do not deduplication, + // else do deduplication. + deduplication: null, // splitArea: { // show: false // }, diff --git a/src/coord/axisModelCreator.js b/src/coord/axisModelCreator.js index 71de9650a..8a4b37e93 100644 --- a/src/coord/axisModelCreator.js +++ b/src/coord/axisModelCreator.js @@ -60,7 +60,13 @@ export default function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraD } }, + /** + * Should not be called before all of 'getInitailData' finished. + * Because categories are collected during initializing data. + */ getCategories: function () { + // FIXME + // warning if called before all of 'getInitailData' finished. if (this.option.type === 'category') { return this.ordinalMeta.categories; } diff --git a/src/data/List.js b/src/data/List.js index 30087057c..1d5939e5e 100644 --- a/src/data/List.js +++ b/src/data/List.js @@ -153,8 +153,7 @@ var typedArrayProviderMethods = { * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...]. * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius * Spetial fields: { - * ordinalMeta: , - * sourceModelUID: + * ordinalMeta: * } * @param {module:echarts/model/Model} hostModel */ @@ -1733,7 +1732,7 @@ function converDataValue(value, dimInfo) { return !ordinalMeta ? value : typeof value === 'string' - ? ordinalMeta.parseAndCollect(value, dimInfo.sourceModelUID) + ? ordinalMeta.parseAndCollect(value) : NaN; } diff --git a/src/data/OrdinalMeta.js b/src/data/OrdinalMeta.js index b31df54d2..cf3ce6567 100644 --- a/src/data/OrdinalMeta.js +++ b/src/data/OrdinalMeta.js @@ -27,9 +27,9 @@ function OrdinalMeta(axisModel) { /** * @private - * @type {string} + * @type {boolean} */ - this._sourceModelUID = categories ? axisModel.uid : NO_SOURCE; + this._preventDeduplication = axisModel.get('dedplication', true) === false; /** * @private @@ -50,16 +50,21 @@ proto.getOrdinal = function (category) { /** * @param {string} category - * @param {string} [dimSourceModelUID] * @return {number} The ordinal. If not found, return NaN. */ -proto.parseAndCollect = function (category, dimSourceModelUID) { +proto.parseAndCollect = function (category) { 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) { + // Optimize for the scenario: + // category is ['2012-01-01', '2012-01-02', ...], where the input + // data has been ensured not duplicate and is large data. + // Notice, if a dataset dimension provide categroies, usually echarts + // should remove duplication except user tell echarts dont do that + // (set axis.deduplication = false), because echarts do not know whether + // the values in the category dimension has duplication (consider the + // parallel-aqi example) + if (needCollect && this._preventDeduplication) { index = this.categories.length; this.categories[index] = category; return index; @@ -82,31 +87,6 @@ proto.parseAndCollect = function (category, dimSourceModelUID) { 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 || ( diff --git a/src/data/Source.js b/src/data/Source.js new file mode 100644 index 000000000..3f73532e3 --- /dev/null +++ b/src/data/Source.js @@ -0,0 +1,57 @@ +import {extend, createHashMap} from 'zrender/src/core/util'; + +/** + * [sourceFormat] + * + * + "seriesOriginal": + * This format is only used in series.data, where + * itemStyle can be specified in data item. + * + * + "array2d": + * [ + * ['product', 'score', 'amount'], + * ['Matcha Latte', 89.3, 95.8], + * ['Milk Tea', 92.1, 89.4], + * ['Cheese Cocoa', 94.4, 91.2], + * ['Walnut Brownie', 85.4, 76.9] + * ] + * + * + "keyValues": + * [ + * {product: 'Matcha Latte', score: 89.3, amount: 95.8}, + * {product: 'Milk Tea', score: 92.1, amount: 89.4}, + * {product: 'Cheese Cocoa', score: 94.4, amount: 91.2}, + * {product: 'Walnut Brownie', score: 85.4, amount: 76.9} + * ] + * + * + "keyArrays": + * { + * 'product': ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie'], + * 'count': [823, 235, 1042, 988], + * 'score': [95.8, 81.4, 91.2, 76.9] + * } + * + * + "typedArray" + * + * + null/undefined + * + */ + +/** + * @constructor + * @param {Object} fields + * @param {string} fields.modelUID Not null/undefined. + * @param {Array|Object} fields.data Not null/undefined. + * @param {Array.} dimensionsDefine Original define, can be null/undefined. + * @param {string} seriesLayoutBy 'row' or 'column' + * @param {HashMap} encodeDefine Original define, can be null/undefined. + * @param {string} sourceFormat See also"detectSourceFormat". + */ +function Source(fields) { + extend(this, fields); + if (this.encodeDefine) { + this.encodeDefine = createHashMap(this.encodeDefine); + } +} + +export default Source; \ No newline at end of file diff --git a/src/data/helper/completeDimensions.js b/src/data/helper/completeDimensions.js index 54c8bd6dd..dc77263e5 100644 --- a/src/data/helper/completeDimensions.js +++ b/src/data/helper/completeDimensions.js @@ -6,7 +6,7 @@ import * as zrUtil from 'zrender/src/core/util'; import {normalizeToArray, getDataItemValue} from '../../util/model'; -import guessOrdinal from './guessOrdinal'; +import {guessOrdinal} from './sourceHelper'; var each = zrUtil.each; var isString = zrUtil.isString; @@ -69,8 +69,11 @@ function completeDimensions(sysDims, data, opt) { // 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 + // But + // (1) custom series should be considered. where other dims // may be visited. + // (2) sometimes user need to calcualte bubble size or use visualMap + // on other dimensions besides coordSys needed. var value0 = getDataItemValue(data[0]); dimCount = Math.max( zrUtil.isArray(value0) && value0.length || 1, @@ -173,9 +176,11 @@ function completeDimensions(sysDims, data, opt) { dataDimNameMap )); - if (!isDataTypedArray) { - resultItem.type == null && guessOrdinal(data, resultDimIdx) - && (resultItem.type = 'ordinal'); + if (!isDataTypedArray + && resultItem.type == null + && guessOrdinal(data, resultDimIdx) + ) { + resultItem.type = 'ordinal'; } } diff --git a/src/data/helper/guessOrdinal.js b/src/data/helper/guessOrdinal.js deleted file mode 100644 index 95be7820b..000000000 --- a/src/data/helper/guessOrdinal.js +++ /dev/null @@ -1,25 +0,0 @@ -import {isArray, isString} from 'zrender/src/core/util'; -import {getDataItemValue} from '../../util/model'; - -// The rule should not be complex, otherwise user might not -// be able to known where the data is wrong. -export default function (data, dimIndex) { - for (var i = 0, len = data.length; i < len; i++) { - var value = getDataItemValue(data[i]); - - if (!isArray(value)) { - return false; - } - - var value = value[dimIndex]; - // Consider usage convenience, '1', '2' will be treated as "number". - // `isFinit('')` get `true`. - if (value != null && isFinite(value) && value !== '') { - return false; - } - else if (isString(value) && value !== '-') { - return true; - } - } - return false; -} diff --git a/src/data/helper/sourceHelper.js b/src/data/helper/sourceHelper.js index d1d9a6a07..cf15230ed 100644 --- a/src/data/helper/sourceHelper.js +++ b/src/data/helper/sourceHelper.js @@ -1,10 +1,69 @@ import {makeInner} from '../../util/model'; import {getCoordSysDefineBySeries} from '../../model/referHelper'; -import {createHashMap, each} from 'zrender/src/core/util'; +import {createHashMap, each, isArray, isString, isObject} from 'zrender/src/core/util'; +import {getDataItemValue} from '../../util/model'; +import Source from '../Source'; var inner = makeInner(); +/** + * [Scenarios]: + * (1) Provide source data directly: + * series: { + * encode: {...}, + * dimensions: [...] + * seriesLayoutBy: 'row', + * data: [[...]] + * } + * (2) Refer to datasetModel. + * series: [{ + * encode: {...} + * // Ignore datasetIndex means `datasetIndex: 0` + * // and the dimensions defination in dataset is used + * }, { + * encode: {...}, + * seriesLayoutBy: 'column', + * datasetIndex: 1 + * }] + * + * Get data from series itself or datset. + * @return {module:echarts/data/Source} source + */ +export function getSource(seriesModel) { + var seriesOption = seriesModel.option; + var modelUID = seriesModel.uid; + var data = seriesOption.data; + var dimensionsDefine = seriesOption.dimensions; + var seriesLayoutBy = seriesOption.seriesLayoutBy; + var sourceFormat = 'seriesOriginal'; + + var datasetModel = getDatasetModel(seriesModel); + if (datasetModel) { + var datasetOption = datasetModel.option; + modelUID = datasetModel.uid; + data = datasetModel.parsedData; + // Dimensions defined on series has higher priority. + dimensionsDefine = dimensionsDefine || detectDatasetDimensions(datasetModel); + seriesLayoutBy = seriesLayoutBy || datasetOption.seriesLayoutBy; + sourceFormat = datasetOption.sourceFormat; + } + + return new Source({ + modelUID: modelUID, + // Do not clone data for performance. + data: data, + dimensionsDefine: dimensionsDefine ? dimensionsDefine.slice() : null, + seriesLayoutBy: seriesLayoutBy, + encodeDefine: inner(seriesModel).encode, + sourceFormat: sourceFormat + }); +} + +/** + * MUST be called before mergeOption of all series. + * @param {module:echarts/model/Global} ecModel + */ export function resetDefaultEncode(ecModel) { inner(ecModel).datasetMap = createHashMap(); } @@ -23,11 +82,21 @@ export function resetDefaultEncode(ecModel) { * 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. + * @param {module:echarts/model/Series} seriesModel */ -export function makeDefaultEncode(seriesModel) { - var ecModel = seriesModel.ecModel; - var datasetMap = inner(ecModel).datasetMap; +export function setEncode(seriesModel) { + var datasetModel = getDatasetModel(seriesModel); + // Note: dataset option does not have `encode`. + var optionEncode = seriesModel.option.encode; + inner(seriesModel).encode = (!optionEncode && datasetModel) + ? makeDefaultEncode(seriesModel, datasetModel) + : optionEncode; +} + +function makeDefaultEncode(seriesModel, datasetModel) { + if (!datasetModel) { + return; + } var coordSysDefine = getCoordSysDefineBySeries(seriesModel); @@ -35,16 +104,11 @@ export function makeDefaultEncode(seriesModel) { // 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 ecModel = seriesModel.ecModel; + var datasetMap = inner(ecModel).datasetMap; var datasetUID = datasetModel.uid; var datasetRecord = datasetMap.get(datasetUID) || datasetMap.set(datasetUID, {categoryWayDim: 1, valueWayDim: 0}); @@ -64,10 +128,99 @@ export function makeDefaultEncode(seriesModel) { return encode; } -export function getDatasetModel(seriesModel) { - var thisData = seriesModel.option.data; - return seriesModel.ecModel.getComponent( - 'dataset', thisData && thisData.datasetIndex || 0 - ); +/** + * If return null/undefined, indicate that should not use datasetModel. + */ +function getDatasetModel(seriesModel) { + var option = seriesModel.option; + // Caution: consider the scenario: + // A dataset is declared and a series is not expected to use the dataset, + // and at the beginning `setOption({series: { noData })` (just prepare other + // option but no data), then `setOption({series: {data: [...]}); In this case, + // the user should set an empty array to avoid that dataset is used by default. + var thisData = option.data; + if (!thisData) { + return seriesModel.ecModel.getComponent('dataset', option.datasetIndex || 0); + } +} + +// The rule should not be complex, otherwise user might not +// be able to known where the data is wrong. +/** + * @param {Array|List} sourceData + * @param {number} dimIndex + */ +export function guessOrdinal(sourceData, dimIndex) { + for (var i = 0, len = sourceData.length; i < len; i++) { + var value = getDataItemValue(sourceData[i]); + + if (!isArray(value)) { + return false; + } + + var value = value[dimIndex]; + // Consider usage convenience, '1', '2' will be treated as "number". + // `isFinit('')` get `true`. + if (value != null && isFinite(value) && value !== '') { + return false; + } + else if (isString(value) && value !== '-') { + return true; + } + } + return false; } +/** + * @see {module:echarts/data/Source} + * @param {Array|Object} sourceData + * @return {string} sourceFormat + */ +export function detectSourceFormat(sourceData) { + if (!sourceData) { + return 'unknown'; + } + + var isTypedArray = isTypedArray(sourceData); + + if (isTypedArray(sourceData)) { + return 'typedArray'; + } + else if (isArray(sourceData)) { + // FIXME Whether tolerate null in top level array? + for (var i = 0, len = sourceData.length; i < len; i++) { + var item = sourceData[i]; + + if (item == null) { + continue; + } + else if (isArray(item)) { + return 'array2d'; + } + else if (isObject(item)) { + return 'keyValues'; + } + } + } + else if (isObject(sourceData)) { + for (var key in sourceData) { + if (sourceData.hasOwnProtytpe(key) && isArray(sourceData[key])) { + return 'keyArrays'; + } + } + } + + return 'unknown'; +} + +function detectDatasetDimensions(datasetModel) { + +} + +export function getDimTypeByAxis(axisType) { + return axisType === 'category' + ? 'ordinal' + : axisType === 'time' + ? 'time' + : 'float'; +} diff --git a/src/model/Component.js b/src/model/Component.js index 1aa719a22..a0205b5f0 100644 --- a/src/model/Component.js +++ b/src/model/Component.js @@ -94,7 +94,6 @@ var ComponentModel = Model.extend({ this.uid = componentUtil.getUID('componentModel'); }, - init: function (option, parentModel, ecModel, extraOpt) { this.mergeDefaultAndTheme(option, ecModel); }, diff --git a/src/model/Series.js b/src/model/Series.js index 9f4e0eeae..855b23739 100644 --- a/src/model/Series.js +++ b/src/model/Series.js @@ -16,8 +16,8 @@ import { } from '../util/layout'; import {createTask} from '../stream/task'; import { - getDatasetModel, - makeDefaultEncode + setEncode, + getSource } from '../data/helper/sourceHelper'; var inner = modelUtil.makeInner(); @@ -77,7 +77,7 @@ var SeriesModel = ComponentModel.extend({ this.mergeDefaultAndTheme(option, ecModel); - setDefaultEncode(this); + setEncode(this); var data = this.getInitialData(option, ecModel); @@ -146,7 +146,7 @@ var SeriesModel = ComponentModel.extend({ mergeLayoutParam(this.option, newSeriesOption, layoutMode); } - setDefaultEncode(this); + setEncode(this); var data = this.getInitialData(newSeriesOption, ecModel); // ??? set dirty on ecModel, becusue it will call mergeOption({})? @@ -177,8 +177,13 @@ var SeriesModel = ComponentModel.extend({ /** * Append data to list + * @param {Object} params + * @param {Array|TypedArray} params.data */ appendData: function (params) { + // FIXME ??? + // (1) If data from dataset, forbidden append. + // (2) support append data of dataset. var data = this.getRawData(); data.appendData(params.data); }, @@ -200,67 +205,11 @@ var SeriesModel = ComponentModel.extend({ }, /** - * [Scenarios]: - * (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} [dataAttr='data'] Or can be like 'nodes', 'links' - * @return {Object} - * { - * modelUID: Not null/undefined. - * data: Not null/undefined. - * dimensionsDefine: > Original define, can be null/undefined. - * encodeDefine: Original define, can be null/undefined. - * } + * @see {module:echarts/data/helper/sourceHelper#getSource} + * @return {module:echarts/data/Source} source */ - getSource: function (dataAttr) { - dataAttr = dataAttr || 'data'; - - var thisOption = this.option; - var thisData = thisOption[dataAttr]; - var dimensionsDefine = 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[dataAttr]; - modelUID = datasetModel.uid; - dimensionsDefine = datasetOption.dimensions; - dimensionsDefine && (dimensionsDefine = dimensionsDefine.slice()); - } - } - } - - return { - modelUID: modelUID, - data: data, - dimensionsDefine: dimensionsDefine, - encodeDefine: inner(this).encode - }; + getSource: function () { + return getSource(this); }, /** @@ -475,15 +424,4 @@ 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 +export default SeriesModel; diff --git a/src/model/referHelper.js b/src/model/referHelper.js index 41a19f0dc..1e8a5a615 100644 --- a/src/model/referHelper.js +++ b/src/model/referHelper.js @@ -8,7 +8,7 @@ // check: "modelHelper" of tooltip and "BrushTargetManager". import {__DEV__} from '../config'; -import {createHashMap, retrieve} from 'zrender/src/core/util'; +import {createHashMap, retrieve, each} from 'zrender/src/core/util'; /** * @return {Object} For example: @@ -25,6 +25,7 @@ import {createHashMap, retrieve} from 'zrender/src/core/util'; * }), * // It also indicate that whether there is category axis. * firstCategoryDimIndex: 1, + * // To replace user specified encode. * } */ export function getCoordSysDefineBySeries(seriesModel) { @@ -127,6 +128,25 @@ var fetchers = { geo: function (seriesModel, result, axisMap, categoryAxisMap) { result.coordSysDims = ['lng', 'lat']; + }, + + parallel: function (seriesModel, result, axisMap, categoryAxisMap) { + var ecModel = seriesModel.ecModel; + var parallelModel = ecModel.getComponent( + 'parallel', seriesModel.get('parallelIndex') + ); + var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice(); + + each(parallelModel.parallelAxisIndex, function (axisIndex, index) { + var axisModel = ecModel.getComponent('parallelAxis', axisIndex); + var axisDim = coordSysDims[index]; + axisMap.set(axisDim, axisModel); + + if (isCategory(axisModel) && result.firstCategoryDimIndex == null) { + categoryAxisMap.set(axisDim, axisModel); + result.firstCategoryDimIndex = index; + } + }); } }; diff --git a/test/parallel-aqi.html b/test/parallel-aqi.html index 7417bc572..ac25550fe 100644 --- a/test/parallel-aqi.html +++ b/test/parallel-aqi.html @@ -1,24 +1,43 @@ - - - + + + + + -
- + + + + + + + + + + + + + + + + + + + + + + + + + + + - chart.on('axisAreaSelected', function (event) { + + + + + + + + + + + + + + + + + \ No newline at end of file -- GitLab