/* * 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 {each, isString} from 'zrender/src/core/util'; import SeriesDimensionDefine from '../SeriesDimensionDefine'; import SeriesModel from '../../model/Series'; import SeriesData, { DataCalculationInfo } from '../SeriesData'; import type { SeriesOption, SeriesStackOptionMixin, DimensionName } from '../../util/types'; import { isSeriesDataSchema, SeriesDataSchema } from './SeriesDataSchema'; import DataStore from '../DataStore'; type EnableDataStackDimensionsInput = { schema: SeriesDataSchema; // If given, stack dimension will be ensured on this store. // Otherwise, stack dimension will be appended at the tail, and should not // be used on a shared store, but should create a brand new storage later. store?: DataStore; }; type EnableDataStackDimensionsInputLegacy = (SeriesDimensionDefine | string)[]; /** * Note that it is too complicated to support 3d stack by value * (have to create two-dimension inverted index), so in 3d case * we just support that stacked by index. * * @param seriesModel * @param dimensionsInput The same as the input of . * The input will be modified. * @param opt * @param opt.stackedCoordDimension Specify a coord dimension if needed. * @param opt.byIndex=false * @return calculationInfo * { * stackedDimension: string * stackedByDimension: string * isStackedByIndex: boolean * stackedOverDimension: string * stackResultDimension: string * } */ export function enableDataStack( seriesModel: SeriesModel, dimensionsInput: EnableDataStackDimensionsInput | EnableDataStackDimensionsInputLegacy, opt?: { // Backward compat stackedCoordDimension?: string byIndex?: boolean } ): Pick< DataCalculationInfo, 'stackedDimension' | 'stackedByDimension' | 'isStackedByIndex' | 'stackedOverDimension' | 'stackResultDimension' > { opt = opt || {}; let byIndex = opt.byIndex; const stackedCoordDimension = opt.stackedCoordDimension; let dimensionDefineList: EnableDataStackDimensionsInputLegacy; let schema: SeriesDataSchema; let store: DataStore; if (isLegacyDimensionsInput(dimensionsInput)) { dimensionDefineList = dimensionsInput; } else { schema = dimensionsInput.schema; dimensionDefineList = schema.dimensions; store = dimensionsInput.store; } // Compatibal: when `stack` is set as '', do not stack. const mayStack = !!(seriesModel && seriesModel.get('stack')); let stackedByDimInfo: SeriesDimensionDefine; let stackedDimInfo: SeriesDimensionDefine; let stackResultDimension: string; let stackedOverDimension: string; each(dimensionDefineList, function (dimensionInfo, index) { if (isString(dimensionInfo)) { dimensionDefineList[index] = dimensionInfo = { name: dimensionInfo as string } as SeriesDimensionDefine; } if (mayStack && !dimensionInfo.isExtraCoord) { // Find the first ordinal dimension as the stackedByDimInfo. if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) { stackedByDimInfo = dimensionInfo; } // Find the first stackable dimension as the stackedDimInfo. if (!stackedDimInfo && dimensionInfo.type !== 'ordinal' && dimensionInfo.type !== 'time' && (!stackedCoordDimension || stackedCoordDimension === dimensionInfo.coordDim) ) { stackedDimInfo = dimensionInfo; } } }); if (stackedDimInfo && !byIndex && !stackedByDimInfo) { // Compatible with previous design, value axis (time axis) only stack by index. // It may make sense if the user provides elaborately constructed data. byIndex = true; } // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`. // That put stack logic in List is for using conveniently in echarts extensions, but it // might not be a good way. if (stackedDimInfo) { // Use a weird name that not duplicated with other names. // Also need to use seriesModel.id as postfix because different // series may share same data store. The stack dimension needs to be distinguished. stackResultDimension = '__\0ecstackresult_' + seriesModel.id; stackedOverDimension = '__\0ecstackedover_' + seriesModel.id; // Create inverted index to fast query index by value. if (stackedByDimInfo) { stackedByDimInfo.createInvertedIndices = true; } const stackedDimCoordDim = stackedDimInfo.coordDim; const stackedDimType = stackedDimInfo.type; let stackedDimCoordIndex = 0; each(dimensionDefineList, function (dimensionInfo: SeriesDimensionDefine) { if (dimensionInfo.coordDim === stackedDimCoordDim) { stackedDimCoordIndex++; } }); const stackedOverDimensionDefine: SeriesDimensionDefine = { name: stackResultDimension, coordDim: stackedDimCoordDim, coordDimIndex: stackedDimCoordIndex, type: stackedDimType, isExtraCoord: true, isCalculationCoord: true, storeDimIndex: dimensionDefineList.length }; const stackResultDimensionDefine: SeriesDimensionDefine = { name: stackedOverDimension, // This dimension contains stack base (generally, 0), so do not set it as // `stackedDimCoordDim` to avoid extent calculation, consider log scale. coordDim: stackedOverDimension, coordDimIndex: stackedDimCoordIndex + 1, type: stackedDimType, isExtraCoord: true, isCalculationCoord: true, storeDimIndex: dimensionDefineList.length + 1 }; if (schema) { if (store) { stackedOverDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackedOverDimension, stackedDimType); stackResultDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackResultDimension, stackedDimType); } schema.appendCalculationDimension(stackedOverDimensionDefine); schema.appendCalculationDimension(stackResultDimensionDefine); } else { dimensionDefineList.push(stackedOverDimensionDefine); dimensionDefineList.push(stackResultDimensionDefine); } } return { stackedDimension: stackedDimInfo && stackedDimInfo.name, stackedByDimension: stackedByDimInfo && stackedByDimInfo.name, isStackedByIndex: byIndex, stackedOverDimension: stackedOverDimension, stackResultDimension: stackResultDimension }; } function isLegacyDimensionsInput( dimensionsInput: Parameters[1] ): dimensionsInput is EnableDataStackDimensionsInputLegacy { return !isSeriesDataSchema((dimensionsInput as EnableDataStackDimensionsInput).schema); } export function isDimensionStacked(data: SeriesData, stackedDim: string): boolean { // Each single series only maps to one pair of axis. So we do not need to // check stackByDim, whatever stacked by a dimension or stacked by index. return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension'); } export function getStackedDimension(data: SeriesData, targetDim: string): DimensionName { return isDimensionStacked(data, targetDim) ? data.getCalculationInfo('stackResultDimension') : targetDim; }