diff --git a/src/chart/pie/PieView.ts b/src/chart/pie/PieView.ts index 9118f9267b..900c104b49 100644 --- a/src/chart/pie/PieView.ts +++ b/src/chart/pie/PieView.ts @@ -95,7 +95,7 @@ class PiePiece extends graphic.Sector { } }, seriesModel, idx); } - // Expansion + // Expansion. else { if (startAngle != null) { sector.setShape({ startAngle, endAngle: startAngle }); diff --git a/src/component/dataset/install.ts b/src/component/dataset/install.ts index 2e5d7be1d2..65c52e7b0e 100644 --- a/src/component/dataset/install.ts +++ b/src/component/dataset/install.ts @@ -29,8 +29,8 @@ import ComponentModel from '../../model/Component'; import ComponentView from '../../view/Component'; import { - SERIES_LAYOUT_BY_COLUMN, ComponentOption, SeriesEncodeOptionMixin, - OptionSourceData, SeriesLayoutBy, OptionSourceHeader + SOURCE_LAYOUT_BY_COLUMN, ComponentOption, SeriesEncodeOptionMixin, + OptionSourceData, SourceLayout, OptionSourceHeader } from '../../util/types'; import { DataTransformOption, PipedDataTransformOption } from '../../data/helper/transform'; import GlobalModel from '../../model/Global'; @@ -44,10 +44,16 @@ export interface DatasetOption extends Pick { mainType?: 'dataset'; - seriesLayoutBy?: SeriesLayoutBy; + sourceLayout?: SourceLayout; sourceHeader?: OptionSourceHeader; source?: OptionSourceData; + /** + * @deprecated + * Use sourceLayout instead + */ + seriesLayoutBy?: SourceLayout + fromDatasetIndex?: number; fromDatasetId?: string; transform?: DataTransformOption | PipedDataTransformOption; @@ -63,7 +69,7 @@ export class DatasetModel extends Co static type = 'dataset'; static defaultOption: DatasetOption = { - seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN + sourceLayout: SOURCE_LAYOUT_BY_COLUMN }; private _sourceManager: SourceManager; diff --git a/src/component/transform/aggregateTransform.ts b/src/component/transform/aggregateTransform.ts new file mode 100644 index 0000000000..9bab9f09b4 --- /dev/null +++ b/src/component/transform/aggregateTransform.ts @@ -0,0 +1,403 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +import { assert, each, find, isArray, keys, map, retrieve2 } from 'zrender/src/core/util'; +import { + DataTransformOption, + ExternalDataTransform, + ExternalDimensionDefinition, + ExternalSource +} from '../../data/helper/transform'; +import { warn } from '../../util/log'; +import { asc, quantile } from '../../util/number'; +import { DimensionLoose, DimensionName, OptionDataValue } from '../../util/types'; + + +const GROUP_KEY_SEPARATER = '$EC$'; +/** + * @usage + * + * ```js + * dataset: [{ + * source: [ + * ['aa', 'bb', 'cc', 'tag'], + * [12, 0.33, 5200, 'AA'], + * [21, 0.65, 7100, 'AA'], + * [51, 0.15, 1100, 'BB'], + * [71, 0.75, 9100, 'BB'], + * ... + * ] + * }, { + * transform: { + * type: 'ecSimpleTransform:aggregate', + * config: { + * output: [ + * // 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: 'dd', method: 'Q1' }, + * { from: 'tag' } + * ], + * groupBy: 'tag' + * } + * } + * // Then the result data will be: + * // [ + * // ['aa', 'bb', 'cc', 'tag'], + * // [12, 0.33, 5200, 'AA'], + * // [21, 0.65, 8100, 'BB'], + * // ... + * // ] + * }] + * ``` + */ + +export interface AggregateTransformOption extends DataTransformOption { + type: 'echarts:aggregate'; + config: { + // Mandatory + output: { + // Optional. The name of the result dimensions. + // If not provided, inherit the name from `from`. + name: DimensionName; + // Mandatory. `from` is used to reference dimension from `source`. + from: DimensionLoose; + // Optional. Aggregate method. Currently only these method supported. + // If not provided, use `'first'`. + method: AggregateMethodLoose; + }[]; + // Optional + groupBy: DimensionLoose | DimensionLoose[]; + }; +} + +type AggregateMethodInternal = + 'SUM' | 'COUNT' | 'FIRST' | 'AVERAGE' | 'Q1' | 'Q2' | 'Q3' | 'MEDIAN' | 'MIN' | 'MAX' | 'VALUES'; +type AggregateMethodLoose = + AggregateMethodInternal + | 'sum' | 'count' | 'first' | 'average' | 'Q1' | 'Q2' | 'Q3' | 'median' | 'min' | 'max'; + +type AggregateResultValue = OptionDataValue | OptionDataValue[]; +type AggregateResultValueByGroup = Record; +class AggregateResult { + + readonly method: AggregateMethodInternal; + readonly name: DimensionName; + readonly index: number; + /** + * Index in the upstream + */ + readonly fromIndex: number; + + /** + * Deps applied before this method + */ + readonly dep: AggregateResult; + + readonly groupBy: ExternalDimensionDefinition[]; + + + values: AggregateResultValue | AggregateResultValueByGroup; + + constructor( + index: number, + indexInUpstream: number, + method: AggregateMethodInternal, + name: DimensionName, + groupBy: ExternalDimensionDefinition[], + dep?: AggregateResult + ) { + this.method = method; + this.name = name; + this.index = index; + this.fromIndex = indexInUpstream; + this.dep = dep; + this.groupBy = groupBy; + + let valuesByGroup: AggregateResultValueByGroup = {}; + if (groupBy) { + valuesByGroup = this.values = {}; + } + + this.set = groupBy + ? (groupByVal, value) => valuesByGroup[groupByVal as string] = value + : (groupByVal, value) => this.values = value; + + this.get = groupBy + ? (groupByVal) => valuesByGroup[groupByVal as string] + : () => this.values as AggregateResultValue; + + } + + set: (groupByVal: OptionDataValue, value: AggregateResultValue) => void; + get: (groupByVal: OptionDataValue) => AggregateResultValue; +} + +export const aggregateTransform: ExternalDataTransform = { + + type: 'echarts:aggregate', + + transform: function (params) { + const upstream = params.upstream; + const config = params.config; + + const { aggResults, groupByDims } = prepare(config, upstream); + + // Calculate + doAggregate(groupByDims, upstream, aggResults); + + // Convert to output row format. + let data: OptionDataValue[][]; + + if (groupByDims && aggResults.length) { + const groupKeys = keys(aggResults[0].values as any); + data = map(groupKeys, key => []); + + each(aggResults, (agg, idx0) => { + each(groupKeys, (key, idx1) => { + data[idx1][idx0] = (agg.values as any)[key]; + }); + }); + } + else { + data = [map(aggResults, dim => dim.values) as OptionDataValue[]]; + } + + return { + dimensions: map(aggResults, dim => dim.name), + // TODO Not provide values to developers? + data + }; + } +}; + +function prepare( + config: AggregateTransformOption['config'], + upstream: ExternalSource +): { + aggResults: AggregateResult[]; + groupByDims?: ExternalDimensionDefinition[] +} { + const outputConfig = config.output; + const aggResults: AggregateResult[] = []; + + let groupByConfig = config.groupBy; + let groupByDims: ExternalDimensionDefinition[]; + if (groupByConfig != null) { + if (!isArray(groupByConfig)) { + groupByConfig = [groupByConfig]; + } + groupByDims = map(groupByConfig, g => upstream.getDimensionInfo(g)); + assert(groupByDims, 'Can not find dimension by `groupBy`: ' + groupByConfig); + } + + each(outputConfig, resultDimInfoConfig => { + + const dimInfoInUpstream = upstream.getDimensionInfo(resultDimInfoConfig.from); + if (__DEV__) { + assert(dimInfoInUpstream, 'Can not find dimension by `from`: ' + resultDimInfoConfig.from); + + if (resultDimInfoConfig.method != null + && find(groupByDims, gbDim => gbDim.index === dimInfoInUpstream.index) + ) { + warn(`Dimension ${dimInfoInUpstream.name} is used as "groupBy" dimension, "method" will be ignored.`); + } + } + + const methodName = (resultDimInfoConfig.method || '').toUpperCase() as AggregateMethodInternal + || 'FIRST'; + const method = methods[methodName]; + if (__DEV__) { + assert(method, `Illegal method ${methodName}.`); + } + + + const name = retrieve2(resultDimInfoConfig.name, dimInfoInUpstream.name); + const indexInUpStream = dimInfoInUpstream.index; + + const finalResultDimInfo = new AggregateResult( + aggResults.length, + indexInUpStream, + methodName, + name, + groupByDims, + method.dep && new AggregateResult( + -1, indexInUpStream, method.dep, name, groupByDims + ) + ); + aggResults.push(finalResultDimInfo); + }); + + return { aggResults, groupByDims }; +} + +function doAggregate( + groupByDims: ExternalDimensionDefinition[] | undefined, + upstream: ExternalSource, + aggResultDims: AggregateResult[] +) { + + function doCreate(isGroupByDim: boolean, aggResult: AggregateResult, val: OptionDataValue, groupByVal?: string) { + aggResult.set(groupByVal, isGroupByDim ? val : methods[aggResult.method].init(val, aggResult, groupByVal)); + }; + function doUpdate(aggResult: AggregateResult, val: OptionDataValue, groupByVal?: string) { + const method = methods[aggResult.method]; + if (method.add) { + aggResult.set(groupByVal, method.add(aggResult.get(groupByVal), val, aggResult, groupByVal)); + } + }; + + for (let i = 0; i < aggResultDims.length; i++) { + const aggResult = aggResultDims[i]; + + // TODO share dep result + if (aggResult.dep) { + doAggregate(groupByDims, upstream, [aggResult.dep]); + } + + if (groupByDims) { + const isGroupByDim = isGroupByDimension(groupByDims, aggResult); + const groupCreated: Record = {}; + const keyArr: string[] = []; + outer: for (let dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) { + for (let i = 0; i < groupByDims.length; i++) { + keyArr[i] = upstream.retrieveValue(dataIndex, groupByDims[i].index) as string; + if (keyArr[i] == null) { + // PENDING: when value is null/undefined + continue outer; + } + } + // TODO key conflicts? + const groupByVal = keyArr.join(GROUP_KEY_SEPARATER); + const val = upstream.retrieveValue(dataIndex, aggResult.fromIndex); + + if (!groupCreated[groupByVal]) { + doCreate(isGroupByDim, aggResult, val, groupByVal); + groupCreated[groupByVal + ''] = true; + } + else if (!isGroupByDim) { + doUpdate(aggResult, val, groupByVal); + } + } + } + else { + for (let dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) { + const val = upstream.retrieveValue(dataIndex, aggResult.fromIndex); + dataIndex + ? doUpdate(aggResult, val) + : doCreate(false, aggResult, val); + } + } + } +} + + +function isGroupByDimension( + groupByDims: ExternalDimensionDefinition[], + targetDimInfo: AggregateResult +): boolean { + return !!find(groupByDims, dim => dim.index === targetDimInfo.fromIndex); +} + +type MethodInit = ( + curr: OptionDataValue, + aggResult: AggregateResult, + groupByVal: OptionDataValue +) => AggregateResultValue; +type MethodAdd = ( + prev: AggregateResultValue, + curr: OptionDataValue, + dimInfo: AggregateResult, + groupByVal: OptionDataValue +) => AggregateResultValue; + +type Method = { + init: MethodInit + add?: MethodAdd + dep?: AggregateMethodInternal +}; + + +function quantileMethod( + percent: number, + aggResult: AggregateResult, + groupByVal: OptionDataValue +) { + return quantile(asc(aggResult.get(groupByVal) as number[]), percent); +} +const Q2Method: Method = { + init(curr, aggResult, groupByVal) { + return quantileMethod(0.5, aggResult.dep, groupByVal); + }, + dep: 'VALUES' +}; + +function identity(val: any) { + return val; +} + +const methods: { + [key in AggregateMethodInternal]: Method +} = { + VALUES: { + init: (curr) => [curr], + add(prev, curr) { + // FIXME: handle other types + (prev as OptionDataValue[]).push(curr); + return prev; + } + }, + SUM: { + init: identity, + add: (prev: number, curr: number) => prev + curr + }, + COUNT: { + init: () => 1, + add: (prev: number) => prev + 1 + }, + FIRST: { + init: identity, + add: identity + }, + MIN: { + init: identity, + add: (prev: number, curr: number) => Math.min(prev, curr) + }, + MAX: { + init: identity, + add: (prev: number, curr: number) => Math.max(prev, curr) + }, + AVERAGE: { + init: (curr: number, aggResult, groupByVal) => curr / (aggResult.dep.get(groupByVal) as number), + add: (prev: number, curr: number, aggResult, groupByVal) => + prev + curr / (aggResult.dep.get(groupByVal) as number), + dep: 'COUNT' + }, + Q1: { + init: (curr, aggResult, groupByVal) => quantileMethod(0.25, aggResult.dep, groupByVal), + dep: 'VALUES' + }, + Q2: Q2Method, + // Alias + MEDIAN: Q2Method, + Q3: { + init: (curr, aggResult, groupByVal) => quantileMethod(0.75, aggResult.dep, groupByVal), + dep: 'VALUES' + } +}; \ No newline at end of file diff --git a/src/component/transform/idTransform.ts b/src/component/transform/idTransform.ts new file mode 100644 index 0000000000..0366d8f3e2 --- /dev/null +++ b/src/component/transform/idTransform.ts @@ -0,0 +1,93 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import { DataTransformOption, ExternalDataTransform } from '../../data/helper/transform'; +import { + DimensionDefinitionLoose, + DimensionIndex, + DimensionName, + OptionSourceDataArrayRows +} from '../../util/types'; + + +/** + * @usage + * + * ```js + * dataset: [{ + * source: [ + * ['aa', 'bb', 'cc', 'tag'], + * [12, 0.33, 5200, 'AA'], + * [21, 0.65, 8100, 'AA'], + * ... + * ] + * }, { + * transform: { + * type: 'ecSimpleTransform:id', + * config: { + * dimensionIndex: 4, + * dimensionName: 'ID' + * } + * } + * // Then the result data will be: + * // [ + * // ['aa', 'bb', 'cc', 'tag', 'ID'], + * // [12, 0.33, 5200, 'AA', 0], + * // [21, 0.65, 8100, 'BB', 1], + * // ... + * // ] + * }] + * ``` + */ + +export interface IdTransformOption extends DataTransformOption { + type: 'echarts:id'; + config: { + // Mandatory. Specify where to put the new id dimension. + dimensionIndex: DimensionIndex; + // Optional. If not provided, left the dimension name not defined. + dimensionName: DimensionName; + }; +} + +export const idTransform: ExternalDataTransform = { + + type: 'echarts:id', + + transform: function (params) { + const {upstream, config} = params; + const {dimensionIndex, dimensionName} = config; + + const dimensions = upstream.cloneAllDimensionInfo() as DimensionDefinitionLoose[]; + dimensions[dimensionIndex] = dimensionName; + + const data = upstream.cloneRawData() as OptionSourceDataArrayRows; + + // TODO: support objectRows + for (let i = 0, len = data.length; i < len; i++) { + const line = data[i]; + line[dimensionIndex] = i; + } + + return { + dimensions, + data + }; + } +}; \ No newline at end of file diff --git a/src/component/transform/install.ts b/src/component/transform/install.ts index 208f6e0add..01c1074431 100644 --- a/src/component/transform/install.ts +++ b/src/component/transform/install.ts @@ -20,8 +20,12 @@ import { EChartsExtensionInstallRegisters } from '../../extension'; import {filterTransform} from './filterTransform'; import {sortTransform} from './sortTransform'; +import {aggregateTransform} from './aggregateTransform'; +import { idTransform } from './idTransform'; export function install(registers: EChartsExtensionInstallRegisters) { registers.registerTransform(filterTransform); registers.registerTransform(sortTransform); + registers.registerTransform(aggregateTransform); + registers.registerTransform(idTransform); } \ No newline at end of file diff --git a/src/component/transform/sortTransform.ts b/src/component/transform/sortTransform.ts index dbe246c693..5442964004 100644 --- a/src/component/transform/sortTransform.ts +++ b/src/component/transform/sortTransform.ts @@ -114,13 +114,6 @@ export const sortTransform: ExternalDataTransform = { throwError(errMsg); } - if (order !== 'asc' && order !== 'desc') { - if (__DEV__) { - errMsg = 'Sort transform config must has "order" specified.' + sampleLog; - } - throwError(errMsg); - } - if (incomparable && (incomparable !== 'min' && incomparable !== 'max')) { let errMsg = ''; if (__DEV__) { diff --git a/src/data/SeriesData.ts b/src/data/SeriesData.ts index 83421ccb53..b1565c2a33 100644 --- a/src/data/SeriesData.ts +++ b/src/data/SeriesData.ts @@ -473,7 +473,7 @@ class SeriesData< private _initGetDimensionInfo(needsHasOwn: boolean): void { const dimensionInfos = this._dimInfos; this._getDimInfo = needsHasOwn - ? dimName => (dimensionInfos.hasOwnProperty(dimName) ? dimensionInfos[dimName] : undefined) + ? dimName => ((zrUtil.hasOwn, dimName) ? dimensionInfos[dimName] : undefined) : dimName => dimensionInfos[dimName]; } diff --git a/src/data/Source.ts b/src/data/Source.ts index a530a50811..12588abd39 100644 --- a/src/data/Source.ts +++ b/src/data/Source.ts @@ -22,10 +22,10 @@ import { hasOwn, assert, each, map, isNumber, isString } from 'zrender/src/core/util'; import { - SourceFormat, SeriesLayoutBy, DimensionDefinition, + SourceFormat, SourceLayout as SourceLayout, DimensionDefinition, OptionEncodeValue, OptionSourceData, SOURCE_FORMAT_ORIGINAL, - SERIES_LAYOUT_BY_COLUMN, + SOURCE_LAYOUT_BY_COLUMN, SOURCE_FORMAT_UNKNOWN, SOURCE_FORMAT_KEYED_COLUMNS, SOURCE_FORMAT_TYPED_ARRAY, @@ -38,7 +38,7 @@ import { OptionSourceDataObjectRows, OptionDataValue, OptionSourceDataArrayRows, - SERIES_LAYOUT_BY_ROW, + SOURCE_LAYOUT_BY_ROW, OptionSourceDataOriginal, OptionSourceDataKeyedColumns } from '../util/types'; @@ -83,7 +83,7 @@ import { BE_ORDINAL, guessOrdinal } from './helper/sourceHelper'; */ export interface SourceMetaRawOption { - seriesLayoutBy: SeriesLayoutBy; + sourceLayout: SourceLayout; sourceHeader: OptionSourceHeader; dimensions: DimensionDefinitionLoose[]; } @@ -108,7 +108,7 @@ class SourceImpl { * 'row' or 'column' * Not null/undefined. */ - readonly seriesLayoutBy: SeriesLayoutBy; + readonly layout: SourceLayout; /** * dimensions definition from: @@ -145,7 +145,7 @@ class SourceImpl { sourceFormat: SourceFormat, // default: SOURCE_FORMAT_UNKNOWN // Visit config are optional: - seriesLayoutBy?: SeriesLayoutBy, // default: 'column' + layout?: SourceLayout, // default: 'column' dimensionsDefine?: DimensionDefinition[], startIndex?: number, // default: 0 dimensionsDetectedCount?: number, @@ -165,23 +165,18 @@ class SourceImpl { this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN; // Visit config - this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN; + this.layout = fields.layout || SOURCE_LAYOUT_BY_COLUMN; this.startIndex = fields.startIndex || 0; this.dimensionsDetectedCount = fields.dimensionsDetectedCount; this.metaRawOption = fields.metaRawOption; - const dimensionsDefine = this.dimensionsDefine = fields.dimensionsDefine; - - if (dimensionsDefine) { - for (let i = 0; i < dimensionsDefine.length; i++) { - const dim = dimensionsDefine[i]; - if (dim.type == null) { - if (guessOrdinal(this, i) === BE_ORDINAL.Must) { - dim.type = 'ordinal'; - } + each(this.dimensionsDefine = fields.dimensionsDefine, (dim, i) => { + if (dim.type == null) { + if (guessOrdinal(this, i) === BE_ORDINAL.Must) { + dim.type = 'ordinal'; } } - } + }); } } @@ -201,11 +196,11 @@ export function createSource( sourceFormat: SourceFormat ): Source { sourceFormat = sourceFormat || detectSourceFormat(sourceData); - const seriesLayoutBy = thisMetaRawOption.seriesLayoutBy; + const layout = thisMetaRawOption.sourceLayout; const determined = determineSourceDimensions( sourceData, sourceFormat, - seriesLayoutBy, + layout, thisMetaRawOption.sourceHeader, thisMetaRawOption.dimensions ); @@ -213,7 +208,7 @@ export function createSource( data: sourceData, sourceFormat: sourceFormat, - seriesLayoutBy: seriesLayoutBy, + layout: layout, dimensionsDefine: determined.dimensionsDefine, startIndex: determined.startIndex, dimensionsDetectedCount: determined.dimensionsDetectedCount, @@ -243,7 +238,7 @@ export function cloneSourceShallow(source: Source): Source { data: source.data, sourceFormat: source.sourceFormat, - seriesLayoutBy: source.seriesLayoutBy, + layout: source.layout, dimensionsDefine: clone(source.dimensionsDefine), startIndex: source.startIndex, dimensionsDetectedCount: source.dimensionsDetectedCount @@ -300,7 +295,7 @@ export function detectSourceFormat(data: DatasetOption['source']): SourceFormat function determineSourceDimensions( data: OptionSourceData, sourceFormat: SourceFormat, - seriesLayoutBy: SeriesLayoutBy, + layout: SourceLayout, sourceHeader: OptionSourceHeader, // standalone raw dimensions definition, like: // { @@ -349,7 +344,7 @@ function determineSourceDimensions( } } // 10 is an experience number, avoid long loop. - }, seriesLayoutBy, dataArrayRows, 10); + }, layout, dataArrayRows, 10); } else { startIndex = isNumber(sourceHeader) ? sourceHeader : sourceHeader ? 1 : 0; @@ -359,16 +354,16 @@ function determineSourceDimensions( dimensionsDefine = []; arrayRowsTravelFirst(function (val, index) { dimensionsDefine[index] = (val != null ? val + '' : '') as DimensionName; - }, seriesLayoutBy, dataArrayRows, Infinity); + }, layout, dataArrayRows, Infinity); } dimensionsDetectedCount = dimensionsDefine ? dimensionsDefine.length - : seriesLayoutBy === SERIES_LAYOUT_BY_ROW - ? dataArrayRows.length - : dataArrayRows[0] - ? dataArrayRows[0].length - : null; + : layout === SOURCE_LAYOUT_BY_ROW + ? dataArrayRows.length + : dataArrayRows[0] + ? dataArrayRows[0].length + : null; } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) { if (!dimensionsDefine) { @@ -463,11 +458,11 @@ function normalizeDimensionsOption(dimensionsDefine: DimensionDefinitionLoose[]) function arrayRowsTravelFirst( cb: (val: OptionDataValue, idx: number) => void, - seriesLayoutBy: SeriesLayoutBy, + layout: SourceLayout, data: OptionSourceDataArrayRows, maxLoop: number ): void { - if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) { + if (layout === SOURCE_LAYOUT_BY_ROW) { for (let i = 0; i < data.length && i < maxLoop; i++) { cb(data[i] ? data[i][0] : null, i); } diff --git a/src/data/helper/SeriesDataSchema.ts b/src/data/helper/SeriesDataSchema.ts index 67e9f1fc9a..ac18d6c4d5 100644 --- a/src/data/helper/SeriesDataSchema.ts +++ b/src/data/helper/SeriesDataSchema.ts @@ -185,11 +185,11 @@ export class SeriesDataSchema { } // Source from endpoint(usually series) will be read differently - // when seriesLayoutBy or startIndex(which is affected by sourceHeader) are different. + // when layout or startIndex(which is affected by sourceHeader) are different. // So we use this three props as key. const source = this.source; const hash = [ - source.seriesLayoutBy, + source.layout, source.startIndex, dimHash ].join('$$'); diff --git a/src/data/helper/dataProvider.ts b/src/data/helper/dataProvider.ts index 37247edb2c..dbd98aa9be 100644 --- a/src/data/helper/dataProvider.ts +++ b/src/data/helper/dataProvider.ts @@ -31,10 +31,10 @@ import { SOURCE_FORMAT_KEYED_COLUMNS, SOURCE_FORMAT_TYPED_ARRAY, SOURCE_FORMAT_ARRAY_ROWS, - SERIES_LAYOUT_BY_COLUMN, - SERIES_LAYOUT_BY_ROW, + SOURCE_LAYOUT_BY_COLUMN, + SOURCE_LAYOUT_BY_ROW, DimensionName, DimensionIndex, OptionSourceData, - OptionDataItem, OptionDataValue, SourceFormat, SeriesLayoutBy, ParsedValue, DimensionLoose, NullUndefined + OptionDataItem, OptionDataValue, SourceFormat, SourceLayout, ParsedValue, DimensionLoose, NullUndefined } from '../../util/types'; import SeriesData from '../SeriesData'; @@ -150,11 +150,11 @@ export class DefaultDataProvider implements DataProvider { mountMethods = function (provider, data, source) { const sourceFormat = source.sourceFormat; - const seriesLayoutBy = source.seriesLayoutBy; + const sourceLayout = source.layout; const startIndex = source.startIndex; const dimsDef = source.dimensionsDefine; - const methods = providerMethods[getMethodMapKey(sourceFormat, seriesLayoutBy)]; + const methods = providerMethods[getMethodMapKey(sourceFormat, sourceLayout)]; if (__DEV__) { assert(methods, 'Invalide sourceFormat: ' + sourceFormat); } @@ -167,9 +167,9 @@ export class DefaultDataProvider implements DataProvider { provider.fillStorage = fillStorageForTypedArray; } else { - const rawItemGetter = getRawSourceItemGetter(sourceFormat, seriesLayoutBy); + const rawItemGetter = getRawSourceItemGetter(sourceFormat, sourceLayout); provider.getItem = bind(rawItemGetter, null, data, startIndex, dimsDef); - const rawCounter = getRawSourceDataCounter(sourceFormat, seriesLayoutBy); + const rawCounter = getRawSourceDataCounter(sourceFormat, sourceLayout); provider.count = bind(rawCounter, null, data, startIndex, dimsDef); } }; @@ -220,15 +220,15 @@ export class DefaultDataProvider implements DataProvider { providerMethods = { - [SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN]: { + [SOURCE_FORMAT_ARRAY_ROWS + '_' + SOURCE_LAYOUT_BY_COLUMN]: { pure: true, appendData: appendDataSimply }, - [SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW]: { + [SOURCE_FORMAT_ARRAY_ROWS + '_' + SOURCE_LAYOUT_BY_ROW]: { pure: true, appendData: function () { - throw new Error('Do not support appendData when set seriesLayoutBy: "row".'); + throw new Error('Do not support appendData when set sourceLayout: "row".'); } }, @@ -304,12 +304,12 @@ const getItemSimply: RawSourceItemGetter = function ( }; const rawSourceItemGetterMap: Dictionary = { - [SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN]: function ( + [SOURCE_FORMAT_ARRAY_ROWS + '_' + SOURCE_LAYOUT_BY_COLUMN]: function ( rawData, startIndex, dimsDef, idx ) { return (rawData as OptionDataValue[][])[idx + startIndex]; }, - [SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW]: function ( + [SOURCE_FORMAT_ARRAY_ROWS + '_' + SOURCE_LAYOUT_BY_ROW]: function ( rawData, startIndex, dimsDef, idx, out ) { idx += startIndex; @@ -342,11 +342,11 @@ const rawSourceItemGetterMap: Dictionary = { }; export function getRawSourceItemGetter( - sourceFormat: SourceFormat, seriesLayoutBy: SeriesLayoutBy + sourceFormat: SourceFormat, sourceLayout: SourceLayout ): RawSourceItemGetter { - const method = rawSourceItemGetterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)]; + const method = rawSourceItemGetterMap[getMethodMapKey(sourceFormat, sourceLayout)]; if (__DEV__) { - assert(method, 'Do not support get item on "' + sourceFormat + '", "' + seriesLayoutBy + '".'); + assert(method, 'Do not support get item on "' + sourceFormat + '", "' + sourceLayout + '".'); } return method; } @@ -367,12 +367,12 @@ const countSimply: RawSourceDataCounter = function ( }; const rawSourceDataCounterMap: Dictionary = { - [SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN]: function ( + [SOURCE_FORMAT_ARRAY_ROWS + '_' + SOURCE_LAYOUT_BY_COLUMN]: function ( rawData, startIndex, dimsDef ) { return Math.max(0, (rawData as OptionDataItem[][]).length - startIndex); }, - [SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW]: function ( + [SOURCE_FORMAT_ARRAY_ROWS + '_' + SOURCE_LAYOUT_BY_ROW]: function ( rawData, startIndex, dimsDef ) { const row = (rawData as OptionDataValue[][])[0]; @@ -395,11 +395,11 @@ const rawSourceDataCounterMap: Dictionary = { }; export function getRawSourceDataCounter( - sourceFormat: SourceFormat, seriesLayoutBy: SeriesLayoutBy + sourceFormat: SourceFormat, sourceLayout: SourceLayout ): RawSourceDataCounter { - const method = rawSourceDataCounterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)]; + const method = rawSourceDataCounterMap[getMethodMapKey(sourceFormat, sourceLayout)]; if (__DEV__) { - assert(method, 'Do not suppport count on "' + sourceFormat + '", "' + seriesLayoutBy + '".'); + assert(method, 'Do not suppport count on "' + sourceFormat + '", "' + sourceLayout + '".'); } return method; } @@ -452,9 +452,9 @@ export function getRawSourceValueGetter(sourceFormat: SourceFormat): RawSourceVa } -function getMethodMapKey(sourceFormat: SourceFormat, seriesLayoutBy: SeriesLayoutBy): string { +function getMethodMapKey(sourceFormat: SourceFormat, sourceLayout: SourceLayout): string { return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS - ? sourceFormat + '_' + seriesLayoutBy + ? sourceFormat + '_' + sourceLayout : sourceFormat; } diff --git a/src/data/helper/sourceHelper.ts b/src/data/helper/sourceHelper.ts index e0ef9ddd0b..449ff1b5e6 100644 --- a/src/data/helper/sourceHelper.ts +++ b/src/data/helper/sourceHelper.ts @@ -26,7 +26,8 @@ import { isString, isObject, isTypedArray, - HashMap + HashMap, + filter } from 'zrender/src/core/util'; import { Source } from '../Source'; @@ -34,7 +35,7 @@ import { SOURCE_FORMAT_ORIGINAL, SOURCE_FORMAT_ARRAY_ROWS, SOURCE_FORMAT_OBJECT_ROWS, - SERIES_LAYOUT_BY_ROW, + SOURCE_LAYOUT_BY_ROW, SOURCE_FORMAT_KEYED_COLUMNS, DimensionName, OptionSourceDataArrayRows, @@ -114,7 +115,7 @@ export function makeSeriesEncodeForAxisCoordSys( const ecModel = seriesModel.ecModel; const datasetMap = innerGlobalModel(ecModel).datasetMap; - const key = datasetModel.uid + '_' + source.seriesLayoutBy; + const key = datasetModel.uid + '_' + source.layout; let baseCategoryDimIndex: number; let categoryWayValueDimStart; @@ -225,7 +226,7 @@ export function makeSeriesEncodeForNameBased( // 5 is an experience value. for (let i = 0, len = Math.min(5, dimCount); i < len; i++) { const guessResult = doGuessOrdinal( - source.data, sourceFormat, source.seriesLayoutBy, + source.data, sourceFormat, source.layout, dimensionsDefine, source.startIndex, i ); guessRecords.push(guessResult); @@ -322,7 +323,7 @@ export function queryDatasetUpstreamDatasetModels( return []; } - return queryReferringComponents( + return filter(queryReferringComponents( datasetModel.ecModel, 'dataset', { @@ -330,7 +331,7 @@ export function queryDatasetUpstreamDatasetModels( id: datasetModel.get('fromDatasetId', true) }, SINGLE_REFERRING - ).models as DatasetModel[]; + ).models as DatasetModel[], model => model !== datasetModel); } /** @@ -342,7 +343,7 @@ export function guessOrdinal(source: Source, dimIndex: DimensionIndex): BeOrdina return doGuessOrdinal( source.data, source.sourceFormat, - source.seriesLayoutBy, + source.layout, source.dimensionsDefine, source.startIndex, dimIndex @@ -354,7 +355,7 @@ export function guessOrdinal(source: Source, dimIndex: DimensionIndex): BeOrdina function doGuessOrdinal( data: Source['data'], sourceFormat: Source['sourceFormat'], - seriesLayoutBy: Source['seriesLayoutBy'], + layout: Source['layout'], dimensionsDefine: Source['dimensionsDefine'], startIndex: Source['startIndex'], dimIndex: DimensionIndex @@ -388,7 +389,7 @@ function doGuessOrdinal( if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) { const dataArrayRows = data as OptionSourceDataArrayRows; - if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) { + if (layout === SOURCE_LAYOUT_BY_ROW) { const sample = dataArrayRows[dimIndex]; for (let i = 0; i < (sample || []).length && i < maxLoop; i++) { if ((result = detectValue(sample[startIndex + i])) != null) { diff --git a/src/data/helper/sourceManager.ts b/src/data/helper/sourceManager.ts index 611238a67f..b34d225524 100644 --- a/src/data/helper/sourceManager.ts +++ b/src/data/helper/sourceManager.ts @@ -26,7 +26,7 @@ import { SourceMetaRawOption, Source, createSource, cloneSourceShallow } from '. import { SeriesEncodableModel, OptionSourceData, SOURCE_FORMAT_TYPED_ARRAY, SOURCE_FORMAT_ORIGINAL, - SourceFormat, SeriesLayoutBy, OptionSourceHeader, + SourceFormat, SourceLayout, OptionSourceHeader, DimensionDefinitionLoose, Dictionary } from '../../util/types'; import { @@ -36,12 +36,13 @@ import { applyDataTransform } from './transform'; import DataStore, { DataStoreDimensionDefine } from '../DataStore'; import { DefaultDataProvider } from './dataProvider'; import { SeriesDataSchema } from './SeriesDataSchema'; +import { deprecateReplaceLog } from '../../util/log'; type DataStoreMap = Dictionary; /** * [REQUIREMENT_MEMO]: - * (0) `metaRawOption` means `dimensions`/`sourceHeader`/`seriesLayoutBy` in raw option. + * (0) `metaRawOption` means `dimensions`/`sourceHeader`/`sourceLayout` in raw option. * (1) Keep support the feature: `metaRawOption` can be specified both on `series` and * `root-dataset`. Them on `series` has higher priority. * (2) Do not support to set `metaRawOption` on a `non-root-dataset`, because it might @@ -85,7 +86,7 @@ type DataStoreMap = Dictionary; * series: { * encode: {...}, * dimensions: [...] - * seriesLayoutBy: 'row', + * sourceLayout: 'row', * data: [[...]] * } * ``` @@ -97,7 +98,7 @@ type DataStoreMap = Dictionary; * // and the dimensions defination in dataset is used * }, { * encode: {...}, - * seriesLayoutBy: 'column', + * sourceLayout: 'column', * datasetIndex: 1 * }] * ``` @@ -228,21 +229,21 @@ export class SourceManager { // See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root. const newMetaRawOption = this._getSourceMetaRawOption() || {} as SourceMetaRawOption; const upMetaRawOption = upSource && upSource.metaRawOption || {} as SourceMetaRawOption; - const seriesLayoutBy = retrieve2(newMetaRawOption.seriesLayoutBy, upMetaRawOption.seriesLayoutBy) || null; + const sourceLayout = retrieve2(newMetaRawOption.sourceLayout, upMetaRawOption.sourceLayout) || null; const sourceHeader = retrieve2(newMetaRawOption.sourceHeader, upMetaRawOption.sourceHeader); // Note here we should not use `upSource.dimensionsDefine`. Consider the case: - // `upSource.dimensionsDefine` is detected by `seriesLayoutBy: 'column'`, - // but series need `seriesLayoutBy: 'row'`. + // `upSource.dimensionsDefine` is detected by `sourceLayout: 'column'`, + // but series need `sourceLayout: 'row'`. const dimensions = retrieve2(newMetaRawOption.dimensions, upMetaRawOption.dimensions); // We share source with dataset as much as possible // to avoid extra memroy cost of high dimensional data. - const needsCreateSource = seriesLayoutBy !== upMetaRawOption.seriesLayoutBy + const needsCreateSource = sourceLayout !== upMetaRawOption.sourceLayout || !!sourceHeader !== !!upMetaRawOption.sourceHeader || dimensions; resultSourceList = needsCreateSource ? [createSource( data, - { seriesLayoutBy, sourceHeader, dimensions }, + { sourceLayout: sourceLayout, sourceHeader, dimensions }, sourceFormat )] : []; } @@ -274,9 +275,7 @@ export class SourceManager { this._setLocalSource(resultSourceList, upstreamSignList); } - private _applyTransform( - upMgrList: SourceManager[] - ): { + private _applyTransform(upMgrList: SourceManager[]): { sourceList: Source[], upstreamSignList: string[] } { @@ -446,22 +445,25 @@ export class SourceManager { private _getSourceMetaRawOption(): SourceMetaRawOption { const sourceHost = this._sourceHost; - let seriesLayoutBy: SeriesLayoutBy; + let sourceLayout: SourceLayout; let sourceHeader: OptionSourceHeader; let dimensions: DimensionDefinitionLoose[]; - if (isSeries(sourceHost)) { - seriesLayoutBy = sourceHost.get('seriesLayoutBy', true); - sourceHeader = sourceHost.get('sourceHeader', true); - dimensions = sourceHost.get('dimensions', true); - } - // See [REQUIREMENT_MEMO], `non-root-dataset` do not support them. - else if (!this._getUpstreamSourceManagers().length) { - const model = sourceHost as DatasetModel; - seriesLayoutBy = model.get('seriesLayoutBy', true); - sourceHeader = model.get('sourceHeader', true); - dimensions = model.get('dimensions', true); + if (isSeries(sourceHost) + // See [REQUIREMENT_MEMO], `non-root-dataset` do not support them. + || !this._getUpstreamSourceManagers().length + ) { + sourceLayout = (sourceHost as DatasetModel).get('sourceLayout', true); + sourceHeader = (sourceHost as DatasetModel).get('sourceHeader', true); + dimensions = (sourceHost as DatasetModel).get('dimensions', true); + + if (sourceLayout == null) { + sourceLayout = (sourceHost as DatasetModel).get('seriesLayoutBy', true); + if (__DEV__ && sourceLayout) { + deprecateReplaceLog('seriesLayoutBy', 'sourceLayout', 'dataset'); + } + } } - return { seriesLayoutBy, sourceHeader, dimensions }; + return { sourceLayout: sourceLayout, sourceHeader, dimensions }; } } diff --git a/src/data/helper/transform.ts b/src/data/helper/transform.ts index cfb2b188be..8a84e5b4e6 100644 --- a/src/data/helper/transform.ts +++ b/src/data/helper/transform.ts @@ -21,7 +21,7 @@ import { Dictionary, DimensionDefinitionLoose, SourceFormat, DimensionDefinition, DimensionIndex, OptionDataValue, DimensionLoose, DimensionName, ParsedValue, - SERIES_LAYOUT_BY_COLUMN, SOURCE_FORMAT_OBJECT_ROWS, SOURCE_FORMAT_ARRAY_ROWS, + SOURCE_LAYOUT_BY_COLUMN, SOURCE_FORMAT_OBJECT_ROWS, SOURCE_FORMAT_ARRAY_ROWS, OptionSourceDataObjectRows, OptionSourceDataArrayRows } from '../../util/types'; import { normalizeToArray } from '../../util/model'; @@ -167,12 +167,12 @@ function createExternalSource(internalSource: Source, externalTransform: Externa const sourceHeaderCount = internalSource.startIndex; let errMsg = ''; - if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) { + if (internalSource.layout !== SOURCE_LAYOUT_BY_COLUMN) { // For the logic simplicity in transformer, only 'culumn' is // supported in data transform. Otherwise, the `dimensionsDefine` // might be detected by 'row', which probably confuses users. if (__DEV__) { - errMsg = '`seriesLayoutBy` of upstream dataset can only be "column" in data transform.'; + errMsg = '`sourceLayout` of upstream dataset can only be "column" in data transform.'; } throwError(errMsg); } @@ -224,7 +224,7 @@ function createExternalSource(internalSource: Source, externalTransform: Externa } // Implement public methods: - const rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN); + const rawItemGetter = getRawSourceItemGetter(sourceFormat, SOURCE_LAYOUT_BY_COLUMN); if (externalTransform.__isBuiltIn) { extSource.getRawDataItem = function (dataIndex) { return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex) as DataTransformDataItem; @@ -234,7 +234,7 @@ function createExternalSource(internalSource: Source, externalTransform: Externa extSource.cloneRawData = bind(cloneRawData, null, internalSource); - const rawCounter = getRawSourceDataCounter(sourceFormat, SERIES_LAYOUT_BY_COLUMN); + const rawCounter = getRawSourceDataCounter(sourceFormat, SOURCE_LAYOUT_BY_COLUMN); extSource.count = bind(rawCounter, null, data, sourceHeaderCount, dimensions); const rawValueGetter = getRawSourceValueGetter(sourceFormat); @@ -508,8 +508,8 @@ function applySingleDataTransform( // We copy the header of upstream to the result becuase: // (1) The returned data always does not contain header line and can not be used // as dimension-detection. In this case we can not use "detected dimensions" of - // upstream directly, because it might be detected based on different `seriesLayoutBy`. - // (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`. + // upstream directly, because it might be detected based on different `sourceLayout`. + // (2) We should support that the series read the upstream source in `sourceLayout: 'row'`. // So the original detected header should be add to the result, otherwise they can not be read. if (startIndex) { result.data = (firstUpSource.data as []).slice(0, startIndex) @@ -517,14 +517,14 @@ function applySingleDataTransform( } resultMetaRawOption = { - seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN, + sourceLayout: SOURCE_LAYOUT_BY_COLUMN, sourceHeader: startIndex, dimensions: firstUpSource.metaRawOption.dimensions }; } else { resultMetaRawOption = { - seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN, + sourceLayout: SOURCE_LAYOUT_BY_COLUMN, sourceHeader: 0, dimensions: result.dimensions }; diff --git a/src/processor/dataSample.ts b/src/processor/dataSample.ts index 28579a652e..7695defd9d 100644 --- a/src/processor/dataSample.ts +++ b/src/processor/dataSample.ts @@ -22,7 +22,7 @@ import { Dictionary } from 'zrender/src/core/types'; import SeriesModel from '../model/Series'; import { isFunction, isString } from 'zrender/src/core/util'; - +// TODO Merge with methods in aggregate transform type Sampler = (frame: ArrayLike) => number; const samplers: Dictionary = { average: function (frame) { diff --git a/src/util/types.ts b/src/util/types.ts index dbaafc81b6..61a01b88e4 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -467,10 +467,10 @@ export type SourceFormat = | typeof SOURCE_FORMAT_TYPED_ARRAY | typeof SOURCE_FORMAT_UNKNOWN; -export const SERIES_LAYOUT_BY_COLUMN = 'column' as const; -export const SERIES_LAYOUT_BY_ROW = 'row' as const; +export const SOURCE_LAYOUT_BY_COLUMN = 'column' as const; +export const SOURCE_LAYOUT_BY_ROW = 'row' as const; -export type SeriesLayoutBy = typeof SERIES_LAYOUT_BY_COLUMN | typeof SERIES_LAYOUT_BY_ROW; +export type SourceLayout = typeof SOURCE_LAYOUT_BY_COLUMN | typeof SOURCE_LAYOUT_BY_ROW; // null/undefined/'auto': auto detect header, see "src/data/helper/sourceHelper". // If number, means header lines count, or say, `startIndex`. // Like `sourceHeader: 2`, means line 0 and line 1 are header, data start from line 2. @@ -1592,13 +1592,6 @@ export interface SeriesOption< hoverLayerThreshold?: number - /** - * When dataset is used, seriesLayoutBy specifies whether the column or the row of dataset is mapped to the series - * namely, the series is "layout" on columns or rows - * @default 'column' - */ - seriesLayoutBy?: 'column' | 'row' - labelLine?: LabelLineOption /** @@ -1672,8 +1665,15 @@ export interface SeriesSamplingOptionMixin { export interface SeriesEncodeOptionMixin { datasetIndex?: number; datasetId?: string | number; - seriesLayoutBy?: SeriesLayoutBy; + sourceLayout?: SourceLayout; sourceHeader?: OptionSourceHeader; + + /** + * @deprecated + * Use sourceLayout instead + */ + seriesLayoutBy?: SourceLayout + dimensions?: DimensionDefinitionLoose[]; encode?: OptionEncode } diff --git a/test/custom-shape-morphing2.html b/test/custom-shape-morphing2.html index 8b5d2bcdff..91e889046d 100644 --- a/test/custom-shape-morphing2.html +++ b/test/custom-shape-morphing2.html @@ -75,10 +75,9 @@ + + + + + + + + + + + + +
+ + + + + + + + + + + + diff --git a/test/data/map/decode.js b/test/data/map/decode.js index 6f61e70428..6e7bbc1903 100644 --- a/test/data/map/decode.js +++ b/test/data/map/decode.js @@ -18,7 +18,7 @@ */ function decode(json) { - if (json.UTF8Encoding) { + if (!json.UTF8Encoding) { return; } var jsonCompressed = json; @@ -48,7 +48,7 @@ function decode(json) { decodeRings(coordinates, encodeOffsets, encodeScale); break; case 'MultiPolygon': - zrUtil.each(coordinates, function (rings, idx) { + coordinates.forEach(function (rings, idx) { return decodeRings(rings, encodeOffsets[idx], encodeScale) }); } diff --git a/test/lib/config.js b/test/lib/config.js index 1c99b359ac..2b3eeb8c1e 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -58,7 +58,6 @@ 'echarts': ecDistPath, 'zrender': 'node_modules/zrender/dist/zrender', 'ecStat': 'lib/ecStat.min', - 'ecSimpleTransform': 'lib/ecSimpleTransform', 'ecSimpleOptionPlayer': 'lib/ecSimpleOptionPlayer', // 'ecStat': 'http://localhost:8001/echarts/echarts-stat/dist/ecStat', 'geoJson': '../geoData/geoJson', diff --git a/test/lib/ecSimpleTransform.js b/test/lib/ecSimpleTransform.js deleted file mode 100644 index d780fc484b..0000000000 --- a/test/lib/ecSimpleTransform.js +++ /dev/null @@ -1,362 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ecSimpleTransform = {})); -}(this, (function (exports) { 'use strict'; - - var transform = { - type: 'ecSimpleTransform:id', - transform: function (params) { - var upstream = params.upstream; - var config = params.config; - var dimensionIndex = config.dimensionIndex; - var dimensionName = config.dimensionName; - var dimsDef = upstream.cloneAllDimensionInfo(); - dimsDef[dimensionIndex] = dimensionName; - var data = upstream.cloneRawData(); - for (var i = 0, len = data.length; i < len; i++) { - var line = data[i]; - line[dimensionIndex] = i; - } - return { - dimensions: dimsDef, - data: data - }; - } - }; - - function assert(condition, message) { - if (!condition) { - throw new Error(message); - } - } - function hasOwn(own, prop) { - return own.hasOwnProperty(prop); - } - function quantile(ascArr, p) { - var H = (ascArr.length - 1) * p + 1; - var h = Math.floor(H); - var v = +ascArr[h - 1]; - var e = H - h; - return e ? v + e * (ascArr[h] - v) : v; - } - - var METHOD_INTERNAL = { - 'SUM': true, - 'COUNT': true, - 'FIRST': true, - 'AVERAGE': true, - 'Q1': true, - 'Q2': true, - 'Q3': true, - 'MIN': true, - 'MAX': true - }; - var METHOD_NEEDS_COLLECT = { - AVERAGE: ['COUNT'] - }; - var METHOD_NEEDS_GATHER_VALUES = { - Q1: true, - Q2: true, - Q3: true - }; - var METHOD_ALIAS = { - MEDIAN: 'Q2' - }; - var ResultDimInfoInternal = (function () { - function ResultDimInfoInternal(index, indexInUpstream, method, name, needGatherValues) { - this.collectionInfoList = []; - this.gatheredValuesByGroup = {}; - this.gatheredValuesNoGroup = []; - this.needGatherValues = false; - this._collectionInfoMap = {}; - this.method = method; - this.name = name; - this.index = index; - this.indexInUpstream = indexInUpstream; - this.needGatherValues = needGatherValues; - } - ResultDimInfoInternal.prototype.addCollectionInfo = function (item) { - this._collectionInfoMap[item.method] = this.collectionInfoList.length; - this.collectionInfoList.push(item); - }; - ResultDimInfoInternal.prototype.getCollectionInfo = function (method) { - return this.collectionInfoList[this._collectionInfoMap[method]]; - }; - ResultDimInfoInternal.prototype.gatherValue = function (groupByDimInfo, groupVal, value) { - value = +value; - if (groupByDimInfo) { - if (groupVal != null) { - var groupValStr = groupVal + ''; - var values = this.gatheredValuesByGroup[groupValStr] - || (this.gatheredValuesByGroup[groupValStr] = []); - values.push(value); - } - } - else { - this.gatheredValuesNoGroup.push(value); - } - }; - return ResultDimInfoInternal; - }()); - var transform$1 = { - type: 'ecSimpleTransform:aggregate', - transform: function (params) { - var upstream = params.upstream; - var config = params.config; - var groupByDimInfo = prepareGroupByDimInfo(config, upstream); - var _a = prepareDimensions(config, upstream, groupByDimInfo), finalResultDimInfoList = _a.finalResultDimInfoList, collectionDimInfoList = _a.collectionDimInfoList; - var collectionResult; - if (collectionDimInfoList.length) { - collectionResult = travel(groupByDimInfo, upstream, collectionDimInfoList, createCollectionResultLine, updateCollectionResultLine); - } - for (var i = 0; i < collectionDimInfoList.length; i++) { - var dimInfo = collectionDimInfoList[i]; - dimInfo.__collectionResult = collectionResult; - asc(dimInfo.gatheredValuesNoGroup); - var gatheredValuesByGroup = dimInfo.gatheredValuesByGroup; - for (var key in gatheredValuesByGroup) { - if (hasOwn(gatheredValuesByGroup, key)) { - asc(gatheredValuesByGroup[key]); - } - } - } - var finalResult = travel(groupByDimInfo, upstream, finalResultDimInfoList, createFinalResultLine, updateFinalResultLine); - var dimensions = []; - for (var i = 0; i < finalResultDimInfoList.length; i++) { - dimensions.push(finalResultDimInfoList[i].name); - } - return { - dimensions: dimensions, - data: finalResult.outList - }; - } - }; - function prepareDimensions(config, upstream, groupByDimInfo) { - var resultDimensionsConfig = config.resultDimensions; - var finalResultDimInfoList = []; - var collectionDimInfoList = []; - var gIndexInLine = 0; - for (var i = 0; i < resultDimensionsConfig.length; i++) { - var resultDimInfoConfig = resultDimensionsConfig[i]; - var dimInfoInUpstream = upstream.getDimensionInfo(resultDimInfoConfig.from); - assert(dimInfoInUpstream, 'Can not find dimension by `from`: ' + resultDimInfoConfig.from); - var rawMethod = resultDimInfoConfig.method; - assert(groupByDimInfo.index !== dimInfoInUpstream.index || rawMethod == null, "Dimension " + dimInfoInUpstream.name + " is the \"groupBy\" dimension, must not have any \"method\"."); - var method = normalizeMethod(rawMethod); - assert(method, 'method is required'); - var name_1 = resultDimInfoConfig.name != null ? resultDimInfoConfig.name : dimInfoInUpstream.name; - var finalResultDimInfo = new ResultDimInfoInternal(finalResultDimInfoList.length, dimInfoInUpstream.index, method, name_1, hasOwn(METHOD_NEEDS_GATHER_VALUES, method)); - finalResultDimInfoList.push(finalResultDimInfo); - var needCollect = false; - if (hasOwn(METHOD_NEEDS_COLLECT, method)) { - needCollect = true; - var collectionTargetMethods = METHOD_NEEDS_COLLECT[method]; - for (var j = 0; j < collectionTargetMethods.length; j++) { - finalResultDimInfo.addCollectionInfo({ - method: collectionTargetMethods[j], - indexInLine: gIndexInLine++ - }); - } - } - if (hasOwn(METHOD_NEEDS_GATHER_VALUES, method)) { - needCollect = true; - } - if (needCollect) { - collectionDimInfoList.push(finalResultDimInfo); - } - } - return { collectionDimInfoList: collectionDimInfoList, finalResultDimInfoList: finalResultDimInfoList }; - } - function prepareGroupByDimInfo(config, upstream) { - var groupByConfig = config.groupBy; - var groupByDimInfo; - if (groupByConfig != null) { - groupByDimInfo = upstream.getDimensionInfo(groupByConfig); - assert(groupByDimInfo, 'Can not find dimension by `groupBy`: ' + groupByConfig); - } - return groupByDimInfo; - } - function travel(groupByDimInfo, upstream, resultDimInfoList, doCreate, doUpdate) { - var outList = []; - var mapByGroup; - if (groupByDimInfo) { - mapByGroup = {}; - for (var dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) { - var groupByVal = upstream.retrieveValue(dataIndex, groupByDimInfo.index); - if (groupByVal == null) { - continue; - } - var groupByValStr = groupByVal + ''; - if (!hasOwn(mapByGroup, groupByValStr)) { - var newLine = doCreate(upstream, dataIndex, resultDimInfoList, groupByDimInfo, groupByVal); - outList.push(newLine); - mapByGroup[groupByValStr] = newLine; - } - else { - var targetLine = mapByGroup[groupByValStr]; - doUpdate(upstream, dataIndex, targetLine, resultDimInfoList, groupByDimInfo, groupByVal); - } - } - } - else { - var targetLine = doCreate(upstream, 0, resultDimInfoList); - outList.push(targetLine); - for (var dataIndex = 1, len = upstream.count(); dataIndex < len; dataIndex++) { - doUpdate(upstream, dataIndex, targetLine, resultDimInfoList); - } - } - return { mapByGroup: mapByGroup, outList: outList }; - } - function normalizeMethod(method) { - if (method == null) { - return 'FIRST'; - } - var methodInternal = method.toUpperCase(); - methodInternal = hasOwn(METHOD_ALIAS, methodInternal) - ? METHOD_ALIAS[methodInternal] - : methodInternal; - assert(hasOwn(METHOD_INTERNAL, methodInternal), "Illegal method " + method + "."); - return methodInternal; - } - var createCollectionResultLine = function (upstream, dataIndex, collectionDimInfoList, groupByDimInfo, groupByVal) { - var newLine = []; - for (var i = 0; i < collectionDimInfoList.length; i++) { - var dimInfo = collectionDimInfoList[i]; - var collectionInfoList = dimInfo.collectionInfoList; - for (var j = 0; j < collectionInfoList.length; j++) { - var collectionInfo = collectionInfoList[j]; - newLine[collectionInfo.indexInLine] = +lineCreator[collectionInfo.method](upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal); - } - if (dimInfo.needGatherValues) { - var val = upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream); - dimInfo.gatherValue(groupByDimInfo, groupByVal, val); - } - } - return newLine; - }; - var updateCollectionResultLine = function (upstream, dataIndex, targetLine, collectionDimInfoList, groupByDimInfo, groupByVal) { - for (var i = 0; i < collectionDimInfoList.length; i++) { - var dimInfo = collectionDimInfoList[i]; - var collectionInfoList = dimInfo.collectionInfoList; - for (var j = 0; j < collectionInfoList.length; j++) { - var collectionInfo = collectionInfoList[j]; - var indexInLine = collectionInfo.indexInLine; - targetLine[indexInLine] = +lineUpdater[collectionInfo.method](targetLine[indexInLine], upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal); - } - if (dimInfo.needGatherValues) { - var val = upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream); - dimInfo.gatherValue(groupByDimInfo, groupByVal, val); - } - } - }; - var createFinalResultLine = function (upstream, dataIndex, finalResultDimInfoList, groupByDimInfo, groupByVal) { - var newLine = []; - for (var i = 0; i < finalResultDimInfoList.length; i++) { - var dimInfo = finalResultDimInfoList[i]; - var method = dimInfo.method; - newLine[i] = isGroupByDimension(groupByDimInfo, dimInfo) - ? groupByVal - : lineCreator[method](upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal); - } - return newLine; - }; - var updateFinalResultLine = function (upstream, dataIndex, targetLine, finalResultDimInfoList, groupByDimInfo, groupByVal) { - for (var i = 0; i < finalResultDimInfoList.length; i++) { - var dimInfo = finalResultDimInfoList[i]; - if (isGroupByDimension(groupByDimInfo, dimInfo)) { - continue; - } - var method = dimInfo.method; - targetLine[i] = lineUpdater[method](targetLine[i], upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal); - } - }; - function isGroupByDimension(groupByDimInfo, targetDimInfo) { - return groupByDimInfo && targetDimInfo.indexInUpstream === groupByDimInfo.index; - } - function asc(list) { - list.sort(function (a, b) { - return a - b; - }); - } - var lineCreator = { - 'SUM': function () { - return 0; - }, - 'COUNT': function () { - return 1; - }, - 'FIRST': function (upstream, dataIndex, dimInfo) { - return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream); - }, - 'MIN': function (upstream, dataIndex, dimInfo) { - return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream); - }, - 'MAX': function (upstream, dataIndex, dimInfo) { - return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream); - }, - 'AVERAGE': function (upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) { - var collectLine = groupByDimInfo - ? dimInfo.__collectionResult.mapByGroup[groupByVal + ''] - : dimInfo.__collectionResult.outList[0]; - return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream) - / collectLine[dimInfo.getCollectionInfo('COUNT').indexInLine]; - }, - 'Q1': function (upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) { - return lineCreatorForQ(0.25, dimInfo, groupByDimInfo, groupByVal); - }, - 'Q2': function (upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) { - return lineCreatorForQ(0.5, dimInfo, groupByDimInfo, groupByVal); - }, - 'Q3': function (upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) { - return lineCreatorForQ(0.75, dimInfo, groupByDimInfo, groupByVal); - } - }; - var lineUpdater = { - 'SUM': function (val, upstream, dataIndex, dimInfo) { - return val + upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream); - }, - 'COUNT': function (val) { - return val + 1; - }, - 'FIRST': function (val) { - return val; - }, - 'MIN': function (val, upstream, dataIndex, dimInfo) { - return Math.min(val, upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream)); - }, - 'MAX': function (val, upstream, dataIndex, dimInfo) { - return Math.max(val, upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream)); - }, - 'AVERAGE': function (val, upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) { - var collectLine = groupByDimInfo - ? dimInfo.__collectionResult.mapByGroup[groupByVal + ''] - : dimInfo.__collectionResult.outList[0]; - return val - + upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream) - / collectLine[dimInfo.getCollectionInfo('COUNT').indexInLine]; - }, - 'Q1': function (val, upstream, dataIndex, dimInfo) { - return val; - }, - 'Q2': function (val, upstream, dataIndex, dimInfo) { - return val; - }, - 'Q3': function (val, upstream, dataIndex, dimInfo) { - return val; - } - }; - function lineCreatorForQ(percent, dimInfo, groupByDimInfo, groupByVal) { - var gatheredValues = groupByDimInfo - ? dimInfo.gatheredValuesByGroup[groupByVal + ''] - : dimInfo.gatheredValuesNoGroup; - return quantile(gatheredValues, percent); - } - - exports.aggregate = transform$1; - exports.id = transform; - - Object.defineProperty(exports, '__esModule', { value: true }); - -}))); -//# sourceMappingURL=index.js.map diff --git a/test/universalTransition2.html b/test/universalTransition2.html index ef3a998a13..ff8c21e92d 100644 --- a/test/universalTransition2.html +++ b/test/universalTransition2.html @@ -165,12 +165,10 @@