di-echarts
Version: 
Apache ECharts is a powerful, interactive charting and data visualization library for browser
216 lines (194 loc) • 8.59 kB
text/typescript
/*
* 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 <module:echarts/data/SeriesData>.
 *        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<SeriesOption & SeriesStackOptionMixin>,
    dimensionsInput: EnableDataStackDimensionsInput | EnableDataStackDimensionsInputLegacy,
    opt?: {
        // Backward compat
        stackedCoordDimension?: string
        byIndex?: boolean
    }
): Pick<
    DataCalculationInfo<unknown>,
    '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<typeof enableDataStack>[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;
}