You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2020/09/27 17:46:54 UTC
[incubator-echarts] 03/05: fix: enhance transition morphing API.
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 <su...@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-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org