This is an automated email from the ASF dual-hosted git repository. sushuang pushed a commit to branch custom-morph2 in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit 58397cd61297beafe27e178e41d12203f08d1ee2 Author: 100pah <sushuang0...@gmail.com> AuthorDate: Fri Sep 25 03:40:15 2020 +0800 fix: enhance transition morphing API. --- src/chart/custom.ts | 125 +++++---- src/data/DataDiffer.ts | 6 +- src/data/List.ts | 2 +- src/data/helper/transform.ts | 51 +++- src/echarts.ts | 44 ++-- src/model/Series.ts | 2 + test/custom-shape-morphing2.html | 532 ++++++++++++++++++++++++++++----------- 7 files changed, 517 insertions(+), 245 deletions(-) diff --git a/src/chart/custom.ts b/src/chart/custom.ts index ca87fb4..fed727d 100644 --- a/src/chart/custom.ts +++ b/src/chart/custom.ts @@ -19,7 +19,7 @@ import { hasOwn, assert, isString, retrieve2, retrieve3, defaults, each, - keys, isArrayLike, bind, isFunction, eqNaN, isArray + keys, isArrayLike, bind, isFunction, eqNaN, noop } from 'zrender/src/core/util'; import * as graphicUtil from '../util/graphic'; import { setDefaultStateProxy, enableHoverEmphasis } from '../util/states'; @@ -27,7 +27,7 @@ import * as labelStyleHelper from '../label/labelStyle'; import {getDefaultLabel} from './helper/labelHelper'; import createListFromArray from './helper/createListFromArray'; import {getLayoutOnAxis} from '../layout/barGrid'; -import DataDiffer from '../data/DataDiffer'; +import DataDiffer, { DataDiffCallbackMode } from '../data/DataDiffer'; import SeriesModel from '../model/Series'; import Model from '../model/Model'; import ChartView from '../view/Chart'; @@ -462,56 +462,69 @@ class CustomSeriesView extends ChartView { // roam or data zoom according to `actionType`. const transOpt = customSeries.__transientTransitionOpt; + const cbMode: DataDiffCallbackMode = transOpt ? 'multiple' : 'single'; - (new DataDiffer( - oldData ? oldData.getIndices() : [], - data.getIndices(), - createGetKey(oldData, transOpt && transOpt.from), - createGetKey(data, transOpt && transOpt.to), - null, - transOpt ? 'multiple' : 'single' - )) - .add(function (newIdx) { - createOrUpdateItem( - null, newIdx, renderItem(newIdx, payload), customSeries, group, data, null - ); - }) - .update(function (newIdx, oldIdx) { - createOrUpdateItem( - oldData.getItemGraphicEl(oldIdx), - newIdx, renderItem(newIdx, payload), customSeries, group, data, null - ); - }) - .remove(function (oldIdx) { - doRemoveEl(oldData.getItemGraphicEl(oldIdx), customSeries, group); - }) - .updateManyToOne(function (newIdx, oldIndices) { - const oldElsToMerge: graphicUtil.Path[] = []; - for (let i = 0; i < oldIndices.length; i++) { - const oldEl = oldData.getItemGraphicEl(oldIndices[i]); - if (elCanMorph(oldEl)) { - oldElsToMerge.push(oldEl); + if (transOpt && (transOpt.from == null || transOpt.to == null)) { + oldData && oldData.each(function (oldIdx) { + doRemoveEl(oldData.getItemGraphicEl(oldIdx), customSeries, group); + }); + data.each(function (newIdx) { + createOrUpdateItem( + null, newIdx, renderItem(newIdx, payload), customSeries, group, data, null + ); + }); + } + else { + (new DataDiffer( + oldData ? oldData.getIndices() : [], + data.getIndices(), + createGetKey(oldData, cbMode, transOpt && transOpt.from), + createGetKey(data, cbMode, transOpt && transOpt.to), + null, + cbMode + )) + .add(function (newIdx) { + createOrUpdateItem( + null, newIdx, renderItem(newIdx, payload), customSeries, group, data, null + ); + }) + .update(function (newIdx, oldIdx) { + createOrUpdateItem( + oldData.getItemGraphicEl(oldIdx), + newIdx, renderItem(newIdx, payload), customSeries, group, data, null + ); + }) + .remove(function (oldIdx) { + doRemoveEl(oldData.getItemGraphicEl(oldIdx), customSeries, group); + }) + .updateManyToOne(function (newIdx, oldIndices) { + const oldElsToMerge: graphicUtil.Path[] = []; + for (let i = 0; i < oldIndices.length; i++) { + const oldEl = oldData.getItemGraphicEl(oldIndices[i]); + if (elCanMorph(oldEl)) { + oldElsToMerge.push(oldEl); + } + removeElementDirectly(oldEl, group); } - removeElementDirectly(oldEl, group); - } - createOrUpdateItem( - null, newIdx, renderItem(newIdx, payload), customSeries, - group, data, oldElsToMerge - ); - }) - .updateOneToMany(function (newIndices, oldIdx) { - const newLen = newIndices.length; - const oldEl = oldData.getItemGraphicEl(oldIdx); - const oldElSplitted = elCanMorph(oldEl) ? splitShapeForMorphingFrom(oldEl, newLen) : []; - removeElementDirectly(oldEl, group); - for (let i = 0; i < newLen; i++) { createOrUpdateItem( - null, newIndices[i], renderItem(newIndices[i], payload), customSeries, - group, data, oldElSplitted[i] + null, newIdx, renderItem(newIdx, payload), customSeries, + group, data, oldElsToMerge ); - } - }) - .execute(); + }) + .updateOneToMany(function (newIndices, oldIdx) { + const newLen = newIndices.length; + const oldEl = oldData.getItemGraphicEl(oldIdx); + const oldElSplitted = elCanMorph(oldEl) ? splitShapeForMorphingFrom(oldEl, newLen) : []; + removeElementDirectly(oldEl, group); + for (let i = 0; i < newLen; i++) { + createOrUpdateItem( + null, newIndices[i], renderItem(newIndices[i], payload), customSeries, + group, data, oldElSplitted[i] + ); + } + }) + .execute(); + } // Do clipping const clipPath = customSeries.get('clip', true) @@ -582,20 +595,24 @@ class CustomSeriesView extends ChartView { ChartView.registerClass(CustomSeriesView); -function createGetKey(data: List, dimension: DimensionLoose) { +function createGetKey( + data: List, + cbMode: DataDiffCallbackMode, + dimension: DimensionLoose +) { if (!data) { return; } - const diffBy = data.getDimension(dimension); - - if (diffBy == null) { + if (cbMode === 'single') { return function (rawIdx: number, dataIndex: number) { return data.getId(dataIndex); }; } - const dimInfo = data.getDimensionInfo(diffBy); + const diffByDimName = data.getDimension(dimension); + const dimInfo = data.getDimensionInfo(diffByDimName); + if (!dimInfo) { let errMsg = ''; if (__DEV__) { @@ -605,7 +622,7 @@ function createGetKey(data: List, dimension: DimensionLoose) { } const ordinalMeta = dimInfo.ordinalMeta; return function (rawIdx: number, dataIndex: number) { - let key = data.get(diffBy, dataIndex); + let key = data.get(diffByDimName, dataIndex); if (ordinalMeta) { key = ordinalMeta.categories[key as number]; } diff --git a/src/data/DataDiffer.ts b/src/data/DataDiffer.ts index 43baad1..4b56017 100644 --- a/src/data/DataDiffer.ts +++ b/src/data/DataDiffer.ts @@ -266,12 +266,12 @@ class DataDiffer<CTX = unknown> { keyArr: string[], keyGetterName: '_oldKeyGetter' | '_newKeyGetter' ): void { - const cbModeByKey = this._cbModeMultiple; + const cbModeMultiple = this._cbModeMultiple; for (let i = 0; i < arr.length; i++) { // Add prefix to avoid conflict with Object.prototype. const key = '_ec_' + this[keyGetterName](arr[i], i); - if (!cbModeByKey) { + if (!cbModeMultiple) { keyArr[i] = key; } if (!map) { @@ -285,7 +285,7 @@ class DataDiffer<CTX = unknown> { // Simple optimize: in most cases, one index has one key, // do not need array. map[key] = i; - if (cbModeByKey) { + if (cbModeMultiple) { keyArr.push(key); } } diff --git a/src/data/List.ts b/src/data/List.ts index 1f0d0a8..34b2736 100644 --- a/src/data/List.ts +++ b/src/data/List.ts @@ -2100,7 +2100,7 @@ class List< dimensions: ItrParamDims ): Array<DimensionLoose> { if (!zrUtil.isArray(dimensions)) { - dimensions = [dimensions]; + dimensions = dimensions != null ? [dimensions] : []; } return dimensions; }; diff --git a/src/data/helper/transform.ts b/src/data/helper/transform.ts index 68715a8..bad0de4 100644 --- a/src/data/helper/transform.ts +++ b/src/data/helper/transform.ts @@ -47,15 +47,6 @@ export interface DataTransformOption { print?: boolean; } -export interface DataTransformResult { - source: Source; -} - -export interface DataTransform { - (sourceList: Source[], config: DataTransformConfig): { - } -} - export interface ExternalDataTransform<TO extends DataTransformOption = DataTransformOption> { // Must include namespace like: 'ecStat:regression' type: string, @@ -73,7 +64,21 @@ interface ExternalDataTransformParam<TO extends DataTransformOption = DataTransf } export interface ExternalDataTransformResultItem { data: OptionSourceData; + /** + * A `transform` can optionally return a dimensions definition. + * If the `transform` make sure the dimensions of the result data, it can make that return. + * Otherwise, it's recommanded not to make such a `dimensions`. In this case, echarts will + * inherit dimensions definition from the upstream. If there is no dimensions definition + * of the upstream, the echarts will left it undefined. + * Notice: return a incorrect dimensions definition will cause the downstream can not use + * the values under that dimensions correctly. + * + * @see also `source.isDimensionsDefined`. + */ dimensions?: DimensionDefinitionLoose[]; + /** + * Similar to `dimensions`, a `transform` can return that optionally. + */ sourceHeader?: OptionSourceHeader; } interface ExternalDimensionDefinition extends Partial<DimensionDefinition> { @@ -97,14 +102,33 @@ class ExternalSource { sourceFormat: SourceFormat; sourceHeaderCount: number; + /** + * @return If dimension not found, return null/undefined. + */ getDimensionInfo(dim: DimensionLoose): ExternalDimensionDefinition { return; } + /** + * If dimensions are defined (see `isDimensionsDefined`), `dimensionInfoAll` is corresponding to + * the defined dimensions. + * Otherwise, `dimensionInfoAll` is determined by data columns. + * @return Always return an array (even empty array). + */ getDimensionInfoAll(): ExternalDimensionDefinition[] { return; } + /** + * dimensions defined if and only if: + * (a) dataset.dimensions are declared. + * or + * (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`) + */ + isDimensionsDefined(): boolean { + return; + } + getRawDataItem(dataIndex: number): OptionDataItem { return; } @@ -210,6 +234,7 @@ function createExternalSource(internalSource: Source): ExternalSource { extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName); extSource.getDimensionInfoAll = bind(getDimensionInfoAll, null, dimensions); + extSource.isDimensionsDefined = bind(isDimensionsDefined, null, !!dimsDef); return extSource; } @@ -235,12 +260,14 @@ function getDimensionInfo( } } -function getDimensionInfoAll( - dimensions: ExternalDimensionDefinition[] -): ExternalDimensionDefinition[] { +function getDimensionInfoAll(dimensions: ExternalDimensionDefinition[]): ExternalDimensionDefinition[] { return dimensions; } +function isDimensionsDefined(defined: boolean): boolean { + return defined; +} + const externalTransformMap = createHashMap<ExternalDataTransform, string>(); diff --git a/src/echarts.ts b/src/echarts.ts index c27a65b..9930fe1 100644 --- a/src/echarts.ts +++ b/src/echarts.ts @@ -187,16 +187,20 @@ interface SetOptionOpts { // Rule: only `id` mapped will be merged, // other components of the certain `mainType` will be removed. replaceMerge?: GlobalModelSetOptionOpts['replaceMerge']; - transition?: SetOptionTransitionOptItem | SetOptionTransitionOptItem[]; + transition?: SetOptionTransitionOpt }; interface SetOptionTransitionOptItem { - from: SetOptionTransitionOptFinder | DimensionLoose; - to: SetOptionTransitionOptFinder | DimensionLoose; + // If `from` not given, it means that do not make series transition mandatorily. + // There might be transition mapping dy default. Sometimes we do not need them, + // which might bring about misleading. + from?: SetOptionTransitionOptFinder; + to: SetOptionTransitionOptFinder; } interface SetOptionTransitionOptFinder extends modelUtil.ModelFinderObject { dimension: DimensionLoose; } +type SetOptionTransitionOpt = SetOptionTransitionOptItem | SetOptionTransitionOptItem[]; type EventMethodName = 'on' | 'off'; @@ -267,7 +271,7 @@ let createExtensionAPI: (ecIns: ECharts) => ExtensionAPI; let enableConnect: (ecIns: ECharts) => void; let setTransitionOpt: ( chart: ECharts, - transitionOpt: SetOptionTransitionOptItem | SetOptionTransitionOptItem[] + transitionOpt: SetOptionTransitionOpt ) => void; let markStatusToUpdate: (ecIns: ECharts) => void; @@ -518,7 +522,7 @@ class ECharts extends Eventful { let silent; let replaceMerge; - let transitionOpt; + let transitionOpt: SetOptionTransitionOpt; if (isObject(notMerge)) { lazyUpdate = notMerge.lazyUpdate; silent = notMerge.silent; @@ -2284,24 +2288,18 @@ class ECharts extends Eventful { setTransitionOpt = function ( chart: ECharts, - transitionOpt: SetOptionTransitionOptItem | SetOptionTransitionOptItem[] + transitionOpt: SetOptionTransitionOpt ): void { const ecModel = chart._model; - zrUtil.each(modelUtil.normalizeToArray(transitionOpt), transOpt => { - - function normalizeFromTo(fromTo: DimensionLoose | SetOptionTransitionOptFinder) { - return (zrUtil.isString(fromTo) || zrUtil.isNumber(fromTo)) - ? { dimension: fromTo } - : fromTo; - } + zrUtil.each(modelUtil.normalizeToArray(transitionOpt), transOpt => { let errMsg; - const fromOpt = normalizeFromTo(transOpt.from); - const toOpt = normalizeFromTo(transOpt.to); + const fromOpt = transOpt.from; + const toOpt = transOpt.to; - if (fromOpt == null || toOpt == null) { + if (toOpt == null) { if (__DEV__) { - errMsg = '`transition.from` and `transition.to` must be specified.'; + errMsg = '`transition.to` must be specified.'; } throwError(errMsg); } @@ -2312,7 +2310,7 @@ class ECharts extends Eventful { enableAll: false, enableNone: false }; - const fromResult = modelUtil.parseFinder(ecModel, fromOpt, finderOpt); + const fromResult = fromOpt ? modelUtil.parseFinder(ecModel, fromOpt, finderOpt) : null; const toResult = modelUtil.parseFinder(ecModel, toOpt, finderOpt); const toSeries = toResult.seriesModel; @@ -2322,25 +2320,19 @@ class ECharts extends Eventful { errMsg = '`transition` is only supported on series.'; } } - if (fromResult.seriesModel !== toSeries) { + if (fromResult && fromResult.seriesModel !== toSeries) { errMsg = ''; if (__DEV__) { errMsg = '`transition.from` and `transition.to` must be specified to the same series.'; } } - if (fromOpt.dimension == null || toOpt.dimension == null) { - errMsg = ''; - if (__DEV__) { - errMsg = '`dimension` must be specified in `transition`.'; - } - } if (errMsg != null) { throwError(errMsg); } // Just a temp solution: mount them on series. toSeries.__transientTransitionOpt = { - from: fromOpt.dimension, + from: fromOpt ? fromOpt.dimension : null, to: toOpt.dimension }; }); diff --git a/src/model/Series.ts b/src/model/Series.ts index 3a0f774..78a6776 100644 --- a/src/model/Series.ts +++ b/src/model/Series.ts @@ -135,6 +135,8 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode // [MEMO] Currently only support single "from". If intending to // support multiple "from", if not hard to implement "merge morph", // but correspondingly not easy to implement "split morph". + + // Both from and to can be null/undefined, which meams no transform mapping. from: DimensionLoose; to: DimensionLoose; }; diff --git a/test/custom-shape-morphing2.html b/test/custom-shape-morphing2.html index 01e8bc2..e5a8cb7 100644 --- a/test/custom-shape-morphing2.html +++ b/test/custom-shape-morphing2.html @@ -22,10 +22,11 @@ under the License. <head> <meta charset='utf-8'> <meta name="viewport" content="width=device-width, initial-scale=1" /> - <script src='lib/esl.js'></script> - <script src='lib/config.js'></script> <script src='lib/jquery.min.js'></script> + <script src="../dist/echarts.js"></script> <script src="lib/testHelper.js"></script> + <!-- <script src="lib/ecStat.min.js"></script> --> + <script src="../../echarts-stat/dist/ecStat.js"></script> <link rel="stylesheet" href="lib/reset.css" /> </head> <body> @@ -38,15 +39,121 @@ under the License. <script> - require([ - 'echarts' - ], function (echarts) { + /** + * @usage + * + * dataset: [{ + * source: [ + * ['aa', 'bb', 'cc', 'tag'], + * [12, 0.33, 5200, 'AA'], + * [21, 0.65, 8100, 'AA'], + * ... + * ] + * }, { + * transform: { + * type: 'my:aggregate', + * config: { + * resultDimensions: [ + * // by default, use the same name with `from`. + * { from: 'aa', method: 'sum' }, + * { from: 'bb', method: 'count' }, + * { from: 'cc' }, // method by default: use the first value. + * { from: 'tag' } + * ], + * groupBy: 'tag' + * } + * } + * }] + */ + var sumTransform = { + type: 'my:aggregate', + transform: function (params) { + var source = params.source; + var config = params.config; + var resultDimensionsConfig = config.resultDimensions; + var groupBy = config.groupBy; + + var calcMap = {}; + var resultData = []; + + var groupByDimInfo = source.getDimensionInfo(groupBy); + var resultDimInfoList = []; + var resultDimensions = []; + for (var i = 0; i < resultDimensionsConfig.length; i++) { + var resultDimInfoConfig = resultDimensionsConfig[i]; + var resultDimInfo = source.getDimensionInfo(resultDimInfoConfig.from); + resultDimInfo.method = resultDimInfoConfig.method; + resultDimInfoList.push(resultDimInfo); + if (resultDimInfoConfig.name != null) { + resultDimInfo.name = resultDimInfoConfig.name; + } + resultDimensions.push(resultDimInfo.name); + } - const COLORS = [ - '#37A2DA', '#e06343', '#37a354', '#b55dba', '#b5bd48', '#8378EA', '#96BFFF' - ]; - var COUNT = 50; - var CONTENT_COLOR = '#37A2DA'; + for (var i = 0; i < source.count(); i++) { + var line = source.getRawDataItem(i); + var groupByVal = source.retrieveItemValue(line, groupByDimInfo.index); + + if (!calcMap.hasOwnProperty(groupByVal)) { + var newLine = []; + calcMap[groupByVal] = newLine; + resultData.push(newLine); + for (var j = 0; j < resultDimInfoList.length; j++) { + var resultDimInfo = resultDimInfoList[j]; + var method = resultDimInfo.method; + newLine[j] = resultDimInfo.index === groupByDimInfo.index + ? groupByVal + : (method === 'sum' || method === 'count') + ? 0 + // By default, method: 'first' + : source.retrieveItemValue(line, resultDimInfo.index); + } + } + else { + var newLine = calcMap[groupByVal]; + for (var j = 0; j < resultDimInfoList.length; j++) { + var resultDimInfo = resultDimInfoList[j]; + var method = resultDimInfo.method; + if (resultDimInfo.index !== groupByDimInfo.index) { + if (method === 'sum') { + newLine[j] += line[resultDimInfo.index]; + } + else if (method === 'count') { + newLine[j] += 1; + } + } + } + } + } + + return { + dimensions: resultDimensions, + data: resultData + }; + } + }; + + + function initRawData(count) { + var M_TAG_LIST = ['MA', 'MB', 'MC', 'MD']; + var Z_TAG_LIST = ['ZA', 'ZB', 'ZC', 'ZD', 'ZE']; + var data = []; + var currDate = +new Date(2015, 2, 1); + var ONE_DAY = 3600 * 24 * 1000; + for (var i = 0; i < count; i++, currDate += ONE_DAY) { + data.push([ + currDate, + makeRandomValue([10, 40], 0), + makeRandomValue([0.01, 0.99], 2), + makeRandomValue([1, 10], 1), + // makeRandomValue([1000, 9000], 0), + // makeRandomValue([1000, 9000], 0), + // makeRandomValue([10, 90], 0), + M_TAG_LIST[makeRandomValue([0, M_TAG_LIST.length - 1], 0)], + Z_TAG_LIST[makeRandomValue([0, Z_TAG_LIST.length - 1], 0)], + 'P' + i, + ]); + } function makeRandomValue(range, precision) { return +( @@ -54,89 +161,119 @@ under the License. ).toFixed(precision); } - var M_TAG_LIST = ['MA', 'MB', 'MC', 'MD']; - var Z_TAG_LIST = ['ZA', 'ZB', 'ZC', 'ZD', 'ZE']; + return data; + } + </script> + + + + <script> + (function () { + + echarts.registerTransform(sumTransform); + echarts.registerTransform(ecStat.transform.clustering); + + + const COLORS = [ + '#37A2DA', '#e06343', '#37a354', '#b55dba', '#b5bd48', '#8378EA', '#96BFFF' + ]; + var COUNT = 50; + var CONTENT_COLOR = '#37A2DA'; + var ANIMATION_DURATION_UPDATE = 1500; - function initRawData() { - var DIMENSION = { - DATE: 0, - ATA: 1, - STE: 2, - CTZ: 3, - M_TAG: 4, - Z_TAG: 5, - ID: 6 - }; - var data = []; - var currDate = +new Date(2015, 2, 1); - var ONE_DAY = 3600 * 24 * 1000; - for (var i = 0; i < COUNT; i++, currDate += ONE_DAY) { - var line = []; - data.push(line); - line[DIMENSION.DATE] = currDate; - line[DIMENSION.ATA] = makeRandomValue([10, 40], 0); - line[DIMENSION.STE] = makeRandomValue([0.01, 0.99], 2); - line[DIMENSION.CTZ] = makeRandomValue([1, 10], 1); - line[DIMENSION.M_TAG] = M_TAG_LIST[makeRandomValue([0, M_TAG_LIST.length - 1], 0)]; - line[DIMENSION.Z_TAG] = Z_TAG_LIST[makeRandomValue([0, Z_TAG_LIST.length - 1], 0)]; - line[DIMENSION.ID] = 'P' + i; + // var rawData = initRawData(COUNT); + // console.log(JSON.stringify(rawData)); + var rawData = [[1425139200000,34,0.13,2,"MD","ZD","P0"],[1425225600000,28,0.71,1.5,"MB","ZD","P1"],[1425312000000,23,0.9,2.8,"MA","ZC","P2"],[1425398400000,21,0.58,6,"MB","ZC","P3"],[1425484800000,14,0.1,1.6,"MC","ZA","P4"],[1425571200000,21,0.6,7.7,"MC","ZA","P5"],[1425657600000,23,0.31,2.6,"MC","ZC","P6"],[1425744000000,34,0.74,2.4,"MD","ZE","P7"],[1425830400000,14,0.59,2.3,"MB","ZD","P8"],[1425916800000,18,0.85,5.1,"MB","ZB","P9"],[1426003200000,36,0.96,1.2,"MC","ZC"," [...] + + var RAW_DATA_DIMENSIONS = ['DATE', 'ATA', 'STE', 'CTZ', 'M_TAG', 'Z_TAG', 'ID']; + var M_TAG_SUM_DIMENSIONS = ['ATA', 'STE', 'CTZ', 'M_TAG']; + var RAW_CLUSTER_DIMENSIONS = ['DATE', 'ATA', 'STE', 'CTZ', 'M_TAG', 'Z_TAG', 'ID', 'CLUSTER_IDX', 'CLUSTER_CENTER_ATA', 'CLUSTER_CENTER_STE']; + var RAW_CLUSTER_CENTERS_DIMENSIONS = ['COUNT', 'CLUSTER_IDX', 'CLUSTER_CENTER_ATA', 'CLUSTER_CENTER_STE']; + + // Key: datasetId + var TRANSITION_INFO = { + raw: { + dimensions: RAW_DATA_DIMENSIONS + }, + mTagSum: { + dimensions: M_TAG_SUM_DIMENSIONS, + uniqueKeyDimension: 'M_TAG', + }, + rawClusters: { + dimensions: RAW_CLUSTER_DIMENSIONS + }, + rawClusterCenters: { + dimensions: RAW_CLUSTER_CENTERS_DIMENSIONS, + uniqueKeyDimension: 'CLUSTER_IDX' } - return { - DIMENSION: DIMENSION, - data: data - }; - } - function aggregateSum(rawDataWrap, byDimProp, RESULT_DIMENSION) { - var map = {}; - var result = []; - var data = rawDataWrap.data; - - for (var i = 0; i < data.length; i++) { - var line = data[i]; - var byVal = line[rawDataWrap.DIMENSION[byDimProp]]; - if (!map.hasOwnProperty(byVal)) { - var newLine = []; - map[byVal] = newLine; - result.push(newLine); - newLine[RESULT_DIMENSION.ATA] = 0; - newLine[RESULT_DIMENSION.STE] = 0; - newLine[RESULT_DIMENSION.CTZ] = 0; - newLine[RESULT_DIMENSION[byDimProp]] = byVal; + }; + + var baseOption = { + dataset: [{ + id: 'raw', + dimensions: RAW_DATA_DIMENSIONS, + source: rawData + }, { + id: 'mTagSum', + fromDatasetId: 'raw', + transform: { + type: 'my:aggregate', + config: { + resultDimensions: [ + { from: 'ATA', method: 'sum' }, + { from: 'STE', method: 'sum' }, + { from: 'CTZ', method: 'sum' }, + { from: 'M_TAG' } + ], + groupBy: 'M_TAG' + } } - else { - var newLine = map[byVal]; - newLine[RESULT_DIMENSION.ATA] += line[rawDataWrap.DIMENSION.ATA]; - newLine[RESULT_DIMENSION.STE] += line[rawDataWrap.DIMENSION.STE]; - newLine[RESULT_DIMENSION.CTZ] += line[rawDataWrap.DIMENSION.CTZ]; + }, { + id: 'rawClusters', + fromDatasetId: 'raw', + transform: { + type: 'ecStat:clustering', + print: true, + config: { + clusterCount: 4, + dimensions: ['ATA', 'STE'], + outputClusterIndexDimension: { + index: RAW_CLUSTER_DIMENSIONS.indexOf('CLUSTER_IDX'), + name: 'CLUSTER_IDX' + }, + outputCentroidDimensions: [{ + index: RAW_CLUSTER_DIMENSIONS.indexOf('CLUSTER_CENTER_ATA'), + name: 'CLUSTER_CENTER_ATA' + }, { + index: RAW_CLUSTER_DIMENSIONS.indexOf('CLUSTER_CENTER_STE'), + name: 'CLUSTER_CENTER_STE' + }] + } } - } - - return { - DIMENSION: RESULT_DIMENSION, - data: result, - uniqueKey: 'M_TAG' - }; - } - - var rawDataWrap = initRawData(); - // console.log(JSON.stringify(rawDataWrap.data)); - rawDataWrap.data = [[1425139200000,34,0.13,2,"MD","ZD","P0"],[1425225600000,28,0.71,1.5,"MB","ZD","P1"],[1425312000000,23,0.9,2.8,"MA","ZC","P2"],[1425398400000,21,0.58,6,"MB","ZC","P3"],[1425484800000,14,0.1,1.6,"MC","ZA","P4"],[1425571200000,21,0.6,7.7,"MC","ZA","P5"],[1425657600000,23,0.31,2.6,"MC","ZC","P6"],[1425744000000,34,0.74,2.4,"MD","ZE","P7"],[1425830400000,14,0.59,2.3,"MB","ZD","P8"],[1425916800000,18,0.85,5.1,"MB","ZB","P9"],[1426003200000,36,0.96,1.2,"MC"," [...] - var mTagSumDataWrap = aggregateSum(rawDataWrap, 'M_TAG', { - ATA: 0, - STE: 1, - CTZ: 2, - M_TAG: 3 - }); - var zTagSumDataWrap = aggregateSum(rawDataWrap, 'Z_TAG', { - ATA: 0, - STE: 1, - CTZ: 2, - Z_TAG: 3 - }); + }, { + id: 'rawClusterCenters', + fromDatasetId: 'rawClusters', + transform: { + type: 'my:aggregate', + print: true, + config: { + resultDimensions: [ + { name: 'COUNT', from: 'ATA', method: 'count' }, + { from: 'CLUSTER_CENTER_ATA' }, + { from: 'CLUSTER_CENTER_STE' }, + { from: 'CLUSTER_IDX' } + ], + groupBy: 'CLUSTER_IDX' + } + } + }] + }; function create_Scatter_ATA_STE() { + // var datasetId = 'raw'; + var datasetId = 'rawClusters'; var option = { tooltip: {}, grid: { @@ -157,26 +294,23 @@ under the License. type: 'custom', coordinateSystem: 'cartesian2d', animationDurationUpdate: ANIMATION_DURATION_UPDATE, - data: rawDataWrap.data, + datasetId: datasetId, encode: { - itemName: rawDataWrap.DIMENSION.ID, - x: rawDataWrap.DIMENSION.STE, - y: rawDataWrap.DIMENSION.ATA, - tooltip: [rawDataWrap.DIMENSION.STE, rawDataWrap.DIMENSION.ATA] + itemName: 'ID', + x: 'STE', + y: 'ATA', + tooltip: ['STE', 'ATA'] }, renderItem: function (params, api) { var pos = api.coord([ - api.value(rawDataWrap.DIMENSION.STE), - api.value(rawDataWrap.DIMENSION.ATA) + api.value('STE'), + api.value('ATA') ]); + // var clusterIndex = api.value('CLUSTER_IDX'); return { type: 'circle', - // x: pos[0], - // y: pos[1], morph: true, shape: { - // cx: 0, - // cy: 0, cx: pos[0], cy: pos[1], r: 10, @@ -185,6 +319,7 @@ under the License. style: { transition: 'lineWidth', fill: CONTENT_COLOR, + // fill: COLORS[clusterIndex], stroke: '#555', lineWidth: 1 } @@ -195,11 +330,12 @@ under the License. return { option: option, - dataWrap: rawDataWrap + datasetId: datasetId }; } - function create_Bar_mSum_ATA(mTagSumDataWrap) { + function create_Bar_mSum_ATA() { + var datasetId = 'mTagSum'; var option = { tooltip: {}, grid: { @@ -219,15 +355,15 @@ under the License. type: 'custom', coordinateSystem: 'cartesian2d', animationDurationUpdate: ANIMATION_DURATION_UPDATE, - data: mTagSumDataWrap.data, + datasetId: datasetId, encode: { - x: mTagSumDataWrap.DIMENSION.M_TAG, - y: mTagSumDataWrap.DIMENSION.ATA, - tooltip: [mTagSumDataWrap.DIMENSION.M_TAG, mTagSumDataWrap.DIMENSION.ATA] + x: 'M_TAG', + y: 'ATA', + tooltip: ['M_TAG', 'ATA'] }, renderItem: function (params, api) { - var mTagVal = api.value(mTagSumDataWrap.DIMENSION.M_TAG); - var ataVal = api.value(mTagSumDataWrap.DIMENSION.ATA); + var mTagVal = api.value('M_TAG'); + var ataVal = api.value('ATA'); var tarPos = api.coord([mTagVal, ataVal]); var zeroPos = api.coord([mTagVal, 0]); var size = api.size([mTagVal, ataVal]); @@ -255,37 +391,45 @@ under the License. return { option: option, - dataWrap: mTagSumDataWrap + datasetId: datasetId }; } - function create_Pie_mSum_ATA(mTagSumDataWrap) { - var totalValue = mTagSumDataWrap.data.reduce(function (val, item) { - return val + item[mTagSumDataWrap.DIMENSION.ATA]; - }, 0); - let angles = []; - let currentAngle = -Math.PI / 2; - for (let i = 0; i < mTagSumDataWrap.data.length; i++) { - const angle = mTagSumDataWrap.data[i][mTagSumDataWrap.DIMENSION.ATA] / totalValue * Math.PI * 2; - angles.push([currentAngle, angle + currentAngle]); - currentAngle += angle; - } + function create_Pie_mSum_ATA() { + var datasetId = 'mTagSum'; var option = { tooltip: {}, series: { type: 'custom', coordinateSystem: 'none', animationDurationUpdate: ANIMATION_DURATION_UPDATE, - data: mTagSumDataWrap.data, + datasetId: datasetId, encode: { - itemName: mTagSumDataWrap.DIMENSION.M_TAG, - value: mTagSumDataWrap.DIMENSION.ATA, - tooltip: [mTagSumDataWrap.DIMENSION.ATA] + itemName: 'M_TAG', + value: 'ATA', + tooltip: 'ATA' }, renderItem: function (params, api) { - const width = chart.getWidth(); - const height = chart.getHeight(); + var context = params.context; + if (!context.layout) { + context.layout = true; + var totalValue = 0; + for (var i = 0; i < params.dataInsideLength; i++) { + totalValue += api.value('ATA', i); + } + var angles = []; + var currentAngle = -Math.PI / 2; + for (var i = 0; i < params.dataInsideLength; i++) { + var angle = api.value('ATA', i) / totalValue * Math.PI * 2; + angles.push([currentAngle, angle + currentAngle]); + currentAngle += angle; + } + context.angles = angles; + } + + var width = chart.getWidth(); + var height = chart.getHeight(); return { type: 'sector', morph: true, @@ -294,8 +438,8 @@ under the License. cy: height / 2, r: Math.min(width, height) / 3, r0: Math.min(width, height) / 5, - startAngle: angles[params.dataIndex][0], - endAngle: angles[params.dataIndex][1], + startAngle: context.angles[params.dataIndex][0], + endAngle: context.angles[params.dataIndex][1], clockwise: true }, style: { @@ -310,31 +454,111 @@ under the License. return { option: option, - dataWrap: mTagSumDataWrap + datasetId: datasetId }; } - function createScatter_zSum_ATA(zTagSumDataWrap) { - } + function create_Scatter_ATA_STE_Cluster_Center() { + var datasetId = 'rawClusterCenters'; + var option = { + tooltip: {}, + grid: { + containLabel: true + }, + xAxis: { + name: 'STE' + }, + yAxis: { + name: 'ATA' + }, + dataZoom: [{ + type: 'slider', + }, { + type: 'inside' + }], + series: { + type: 'custom', + coordinateSystem: 'cartesian2d', + animationDurationUpdate: ANIMATION_DURATION_UPDATE, + datasetId: datasetId, + encode: { + x: 'CLUSTER_CENTER_STE', + y: 'CLUSTER_CENTER_ATA', + tooltip: ['CLUSTER_CENTER_STE', 'CLUSTER_CENTER_ATA'] + }, + renderItem: function (params, api) { + var context = params.context; + if (!context.layout) { + context.layout = true; + context.totalCount = 0; + for (var i = 0; i < params.dataInsideLength; i++) { + context.totalCount += api.value('COUNT', i); + } + } + var pos = api.coord([ + api.value('CLUSTER_CENTER_STE'), + api.value('CLUSTER_CENTER_ATA') + ]); + var count = api.value('COUNT'); + var radius = count / context.totalCount * 100 + 10; + return { + type: 'circle', + morph: true, + shape: { + cx: pos[0], + cy: pos[1], + r: radius, + transition: ['cx', 'cy', 'r'] + }, + style: { + transition: 'lineWidth', + fill: CONTENT_COLOR, + stroke: '#555', + lineWidth: 0 + } + }; + } + } + }; + + return { + option: option, + datasetId: datasetId + }; + } - var currOptionName = 'Scatter_ATA_STE'; var optionInfoList = { - 'Scatter_ATA_STE': create_Scatter_ATA_STE(rawDataWrap), - 'Bar_mTagSum_ATA': create_Bar_mSum_ATA(mTagSumDataWrap), - 'Pie_mTagSum_ATA': create_Pie_mSum_ATA(mTagSumDataWrap), + 'Scatter_ATA_STE': create_Scatter_ATA_STE(), + 'Bar_mTagSum_ATA': create_Bar_mSum_ATA(), + 'Pie_mTagSum_ATA': create_Pie_mSum_ATA(), + 'Scatter_ATA_STE_Cluster_Center': create_Scatter_ATA_STE_Cluster_Center() }; + var currOptionName; function next(nextOptionName) { - const lastOptionInfo = optionInfoList[currOptionName]; - const nextOptionInfo = optionInfoList[nextOptionName]; + var lastOptionInfo = optionInfoList[currOptionName]; + var nextOptionInfo = optionInfoList[nextOptionName]; + var transitionOpt = { + to: { seriesIndex: 0 } + }; - const commonDimension = findCommonDimension(lastOptionInfo, nextOptionInfo) - || findCommonDimension(nextOptionInfo, lastOptionInfo); - const fromDimension = lastOptionInfo.dataWrap.DIMENSION[commonDimension]; - const toDimension = nextOptionInfo.dataWrap.DIMENSION[commonDimension]; - const transitionOpt = (fromDimension != null && toDimension != null) - ? { from: fromDimension, to: toDimension } : null; + if (lastOptionInfo) { + var commonDimension = findCommonDimension(lastOptionInfo, nextOptionInfo) + || findCommonDimension(nextOptionInfo, lastOptionInfo); + if (commonDimension != null) { + transitionOpt = { + from: { + seriesIndex: 0, + dimension: commonDimension + }, + to: { + seriesIndex: 0, + dimension: commonDimension + } + }; + } + } currOptionName = nextOptionName; @@ -342,15 +566,17 @@ under the License. replaceMerge: ['xAxis', 'yAxis'], transition: transitionOpt }); - } - function findCommonDimension(optionInfoA, optionInfoB) { - var uniqueKey = optionInfoB.dataWrap.uniqueKey; - if (uniqueKey != null && optionInfoA.dataWrap.DIMENSION[uniqueKey] != null) { - return uniqueKey; + function findCommonDimension(optionInfoA, optionInfoB) { + var metaA = TRANSITION_INFO[optionInfoA.datasetId]; + var metaB = TRANSITION_INFO[optionInfoB.datasetId]; + if (metaA.dimensions.indexOf(metaB.uniqueKeyDimension) >= 0) { + return metaB.uniqueKeyDimension; + } } } + var chart = testHelper.create(echarts, 'main0', { title: [ 'Test: buttons, should morph animation merge/split.', @@ -358,27 +584,35 @@ under the License. 'Test: click buttons **twice**, should no blink.', 'Test: use dataZoom, update animation should exist' ], - option: optionInfoList[currOptionName].option, + option: baseOption, + lazyUpdate: true, height: 600, buttons: [{ - text: 'ToBar', + text: 'Bar_mTagSum_ATA', onclick: function () { next('Bar_mTagSum_ATA'); } }, { - text: 'ToScatter', + text: 'Scatter_ATA_STE', onclick: function () { next('Scatter_ATA_STE'); } }, { - text: 'ToPie', + text: 'Pie_mTagSum_ATA', onclick: function () { next('Pie_mTagSum_ATA'); } + }, { + text: 'Scatter_ATA_STE_Cluster_Center', + onclick: function () { + next('Scatter_ATA_STE_Cluster_Center'); + } }] }); - }); + next('Scatter_ATA_STE'); + + })(); </script> --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org For additional commands, e-mail: commits-h...@echarts.apache.org