| /* |
| * 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. |
| */ |
| |
| /* global Int32Array */ |
| |
| |
| import * as zrUtil from 'zrender/src/core/util'; |
| import {PathStyleProps} from 'zrender/src/graphic/Path'; |
| import Model from '../model/Model'; |
| import DataDiffer from './DataDiffer'; |
| import {DataProvider, DefaultDataProvider} from './helper/dataProvider'; |
| import {summarizeDimensions, DimensionSummary} from './helper/dimensionHelper'; |
| import SeriesDimensionDefine from './SeriesDimensionDefine'; |
| import {ArrayLike, Dictionary, FunctionPropertyNames} from 'zrender/src/core/types'; |
| import Element from 'zrender/src/Element'; |
| import { |
| DimensionIndex, DimensionName, DimensionLoose, OptionDataItem, |
| ParsedValue, ParsedValueNumeric, |
| ModelOption, SeriesDataType, OptionSourceData, SOURCE_FORMAT_TYPED_ARRAY, SOURCE_FORMAT_ORIGINAL, |
| DecalObject, |
| OrdinalNumber, |
| OrdinalRawValue |
| } from '../util/types'; |
| import {convertOptionIdName, isDataItemOption} from '../util/model'; |
| import { setCommonECData } from '../util/innerStore'; |
| import type Graph from './Graph'; |
| import type Tree from './Tree'; |
| import type { VisualMeta } from '../component/visualMap/VisualMapModel'; |
| import {isSourceInstance, Source} from './Source'; |
| import { LineStyleProps } from '../model/mixin/lineStyle'; |
| import DataStore, { DataStoreDimensionDefine, DimValueGetter } from './DataStore'; |
| import { isSeriesDataSchema, SeriesDataSchema } from './helper/SeriesDataSchema'; |
| |
| const isObject = zrUtil.isObject; |
| const map = zrUtil.map; |
| |
| const CtorInt32Array = typeof Int32Array === 'undefined' ? Array : Int32Array; |
| |
| // Use prefix to avoid index to be the same as otherIdList[idx], |
| // which will cause weird update animation. |
| const ID_PREFIX = 'e\0\0'; |
| |
| const INDEX_NOT_FOUND = -1; |
| |
| type NameRepeatCount = {[name: string]: number}; |
| type ItrParamDims = DimensionLoose | Array<DimensionLoose>; |
| // If Ctx not specified, use List as Ctx |
| type CtxOrList<Ctx> = unknown extends Ctx ? SeriesData : Ctx; |
| type EachCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => void; |
| type EachCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => void; |
| type EachCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => void; |
| type EachCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => void; |
| type FilterCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => boolean; |
| type FilterCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => boolean; |
| type FilterCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => boolean; |
| type FilterCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => boolean; |
| type MapArrayCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => any; |
| type MapArrayCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => any; |
| type MapArrayCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => any; |
| type MapArrayCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => any; |
| type MapCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => ParsedValue | ParsedValue[]; |
| type MapCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => |
| ParsedValue | ParsedValue[]; |
| type MapCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => ParsedValue | ParsedValue[]; |
| |
| type SeriesDimensionDefineLoose = string | object | SeriesDimensionDefine; |
| |
| // `SeriesDimensionLoose` and `SeriesDimensionName` is the dimension that is used by coordinate |
| // system or declared in `series.encode`, which will be saved in `SeriesData`. Other dimension |
| // might not be saved in `SeriesData` for performance consideration. See `createDimension` for |
| // more details. |
| type SeriesDimensionLoose = DimensionLoose; |
| type SeriesDimensionName = DimensionName; |
| // type SeriesDimensionIndex = DimensionIndex; |
| |
| |
| const TRANSFERABLE_PROPERTIES = [ |
| 'hasItemOption', '_nameList', '_idList', '_invertedIndicesMap', |
| '_dimSummary', 'userOutput', |
| '_rawData', '_dimValueGetter', |
| '_nameDimIdx', '_idDimIdx', '_nameRepeatCount' |
| ]; |
| |
| const CLONE_PROPERTIES = [ |
| '_approximateExtent' |
| ]; |
| |
| export interface DefaultDataVisual { |
| style: PathStyleProps |
| // Draw type determined which prop should be set with encoded color. |
| // It's only available on the global visual. Use getVisual('drawType') to access it. |
| // It will be set in visual/style.ts module in the first priority. |
| drawType: 'fill' | 'stroke' |
| |
| symbol?: string |
| symbolSize?: number | number[] |
| symbolRotate?: number |
| symbolKeepAspect?: boolean |
| symbolOffset?: string | number | (string | number)[] |
| |
| liftZ?: number |
| // For legend. |
| legendIcon?: string |
| legendLineStyle?: LineStyleProps |
| |
| // visualMap will inject visualMeta data |
| visualMeta?: VisualMeta[] |
| |
| // If color is encoded from palette |
| colorFromPalette?: boolean |
| |
| decal?: DecalObject |
| } |
| |
| export interface DataCalculationInfo<SERIES_MODEL> { |
| stackedDimension: DimensionName; |
| stackedByDimension: DimensionName; |
| isStackedByIndex: boolean; |
| stackedOverDimension: DimensionName; |
| stackResultDimension: DimensionName; |
| stackedOnSeries?: SERIES_MODEL; |
| } |
| |
| // ----------------------------- |
| // Internal method declarations: |
| // ----------------------------- |
| let prepareInvertedIndex: (data: SeriesData) => void; |
| let getId: (data: SeriesData, rawIndex: number) => string; |
| let getIdNameFromStore: (data: SeriesData, dimIdx: number, dataIdx: number) => string; |
| let normalizeDimensions: (dimensions: ItrParamDims) => Array<DimensionLoose>; |
| let transferProperties: (target: SeriesData, source: SeriesData) => void; |
| let cloneListForMapAndSample: (original: SeriesData) => SeriesData; |
| let makeIdFromName: (data: SeriesData, idx: number) => void; |
| |
| class SeriesData< |
| HostModel extends Model = Model, |
| Visual extends DefaultDataVisual = DefaultDataVisual |
| > { |
| |
| readonly type = 'list'; |
| |
| /** |
| * Name of dimensions list of SeriesData. |
| * |
| * @caution Carefully use the index of this array. |
| * Because when DataStore is an extra high dimension(>30) dataset. We will only pick |
| * the used dimensions from DataStore to avoid performance issue. |
| */ |
| readonly dimensions: SeriesDimensionName[]; |
| |
| // Information of each data dimension, like data type. |
| private _dimInfos: Record<SeriesDimensionName, SeriesDimensionDefine>; |
| |
| private _dimOmitted = false; |
| private _schema?: SeriesDataSchema; |
| /** |
| * @pending |
| * Actually we do not really need to convert dimensionIndex to dimensionName |
| * and do not need `_dimIdxToName` if we do everything internally based on dimension |
| * index rather than dimension name. |
| */ |
| private _dimIdxToName?: zrUtil.HashMap<DimensionName, DimensionIndex>; |
| |
| readonly hostModel: HostModel; |
| |
| /** |
| * @readonly |
| */ |
| dataType: SeriesDataType; |
| |
| /** |
| * @readonly |
| * Host graph if List is used to store graph nodes / edges. |
| */ |
| graph?: Graph; |
| |
| /** |
| * @readonly |
| * Host tree if List is used to store tree nodes. |
| */ |
| tree?: Tree; |
| |
| private _store: DataStore; |
| |
| private _nameList: string[] = []; |
| private _idList: string[] = []; |
| |
| // Models of data option is stored sparse for optimizing memory cost |
| // Never used yet (not used yet). |
| // private _optionModels: Model[] = []; |
| |
| // Global visual properties after visual coding |
| private _visual: Dictionary<any> = {}; |
| |
| // Global layout properties. |
| private _layout: Dictionary<any> = {}; |
| |
| // Item visual properties after visual coding |
| private _itemVisuals: Dictionary<any>[] = []; |
| |
| // Item layout properties after layout |
| private _itemLayouts: any[] = []; |
| |
| // Graphic elements |
| private _graphicEls: Element[] = []; |
| |
| // key: dim, value: extent |
| private _approximateExtent: Record<SeriesDimensionName, [number, number]> = {}; |
| |
| private _dimSummary: DimensionSummary; |
| |
| private _invertedIndicesMap: Record<SeriesDimensionName, ArrayLike<number>>; |
| |
| private _calculationInfo: DataCalculationInfo<HostModel> = {} as DataCalculationInfo<HostModel>; |
| |
| // User output info of this data. |
| // DO NOT use it in other places! |
| // When preparing user params for user callbacks, we have |
| // to clone these inner data structures to prevent users |
| // from modifying them to effect built-in logic. And for |
| // performance consideration we make this `userOutput` to |
| // avoid clone them too many times. |
| userOutput: DimensionSummary['userOutput']; |
| |
| // Having detected that there is data item is non primitive type |
| // (in type `OptionDataItemObject`). |
| // Like `data: [ { value: xx, itemStyle: {...} }, ...]` |
| // At present it only happen in `SOURCE_FORMAT_ORIGINAL`. |
| hasItemOption: boolean = false; |
| |
| // id or name is used on dynamic data, mapping old and new items. |
| // When generating id from name, avoid repeat. |
| private _nameRepeatCount: NameRepeatCount; |
| private _nameDimIdx: number; |
| private _idDimIdx: number; |
| |
| private __wrappedMethods: string[]; |
| |
| // Methods that create a new list based on this list should be listed here. |
| // Notice that those method should `RETURN` the new list. |
| TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'lttbDownSample', 'map'] as const; |
| // Methods that change indices of this list should be listed here. |
| CHANGABLE_METHODS = ['filterSelf', 'selectRange'] as const; |
| DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample'] as const; |
| |
| /** |
| * @param dimensionsInput.dimensions |
| * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...]. |
| * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius |
| */ |
| constructor( |
| dimensionsInput: SeriesDataSchema | SeriesDimensionDefineLoose[], |
| hostModel: HostModel |
| ) { |
| let dimensions: SeriesDimensionDefineLoose[]; |
| let assignStoreDimIdx = false; |
| if (isSeriesDataSchema(dimensionsInput)) { |
| dimensions = dimensionsInput.dimensions; |
| this._dimOmitted = dimensionsInput.isDimensionOmitted(); |
| this._schema = dimensionsInput; |
| } |
| else { |
| assignStoreDimIdx = true; |
| dimensions = dimensionsInput as SeriesDimensionDefineLoose[]; |
| } |
| |
| dimensions = dimensions || ['x', 'y']; |
| |
| const dimensionInfos: Dictionary<SeriesDimensionDefine> = {}; |
| const dimensionNames = []; |
| const invertedIndicesMap: Dictionary<number[]> = {}; |
| let needsHasOwn = false; |
| const emptyObj = {}; |
| |
| for (let i = 0; i < dimensions.length; i++) { |
| // Use the original dimensions[i], where other flag props may exists. |
| const dimInfoInput = dimensions[i]; |
| |
| const dimensionInfo: SeriesDimensionDefine = |
| zrUtil.isString(dimInfoInput) |
| ? new SeriesDimensionDefine({name: dimInfoInput}) |
| : !(dimInfoInput instanceof SeriesDimensionDefine) |
| ? new SeriesDimensionDefine(dimInfoInput) |
| : dimInfoInput; |
| |
| const dimensionName = dimensionInfo.name; |
| dimensionInfo.type = dimensionInfo.type || 'float'; |
| if (!dimensionInfo.coordDim) { |
| dimensionInfo.coordDim = dimensionName; |
| dimensionInfo.coordDimIndex = 0; |
| } |
| |
| const otherDims = dimensionInfo.otherDims = dimensionInfo.otherDims || {}; |
| dimensionNames.push(dimensionName); |
| dimensionInfos[dimensionName] = dimensionInfo; |
| if ((emptyObj as any)[dimensionName] != null) { |
| needsHasOwn = true; |
| } |
| |
| if (dimensionInfo.createInvertedIndices) { |
| invertedIndicesMap[dimensionName] = []; |
| } |
| if (otherDims.itemName === 0) { |
| this._nameDimIdx = i; |
| } |
| if (otherDims.itemId === 0) { |
| this._idDimIdx = i; |
| } |
| |
| if (__DEV__) { |
| zrUtil.assert(assignStoreDimIdx || dimensionInfo.storeDimIndex >= 0); |
| } |
| if (assignStoreDimIdx) { |
| dimensionInfo.storeDimIndex = i; |
| } |
| } |
| |
| this.dimensions = dimensionNames; |
| this._dimInfos = dimensionInfos; |
| this._initGetDimensionInfo(needsHasOwn); |
| |
| this.hostModel = hostModel; |
| |
| this._invertedIndicesMap = invertedIndicesMap; |
| |
| if (this._dimOmitted) { |
| const dimIdxToName = this._dimIdxToName = zrUtil.createHashMap<DimensionName, DimensionIndex>(); |
| zrUtil.each(dimensionNames, dimName => { |
| dimIdxToName.set(dimensionInfos[dimName].storeDimIndex, dimName); |
| }); |
| } |
| } |
| |
| /** |
| * |
| * Get concrete dimension name by dimension name or dimension index. |
| * If input a dimension name, do not validate whether the dimension name exits. |
| * |
| * @caution |
| * @param dim Must make sure the dimension is `SeriesDimensionLoose`. |
| * Because only those dimensions will have auto-generated dimension names if not |
| * have a user-specified name, and other dimensions will get a return of null/undefined. |
| * |
| * @notice Because of this reason, should better use `getDimensionIndex` instead, for examples: |
| * ```js |
| * const val = data.getStore().get(data.getDimensionIndex(dim), dataIdx); |
| * ``` |
| * |
| * @return Concrete dim name. |
| */ |
| getDimension(dim: SeriesDimensionLoose): DimensionName { |
| let dimIdx = this._recognizeDimIndex(dim); |
| if (dimIdx == null) { |
| return dim as DimensionName; |
| } |
| dimIdx = dim as DimensionIndex; |
| |
| if (!this._dimOmitted) { |
| return this.dimensions[dimIdx]; |
| } |
| |
| // Retrieve from series dimension definition because it probably contains |
| // generated dimension name (like 'x', 'y'). |
| const dimName = this._dimIdxToName.get(dimIdx); |
| if (dimName != null) { |
| return dimName; |
| } |
| |
| const sourceDimDef = this._schema.getSourceDimension(dimIdx); |
| if (sourceDimDef) { |
| return sourceDimDef.name; |
| } |
| } |
| |
| /** |
| * Get dimension index in data store. Return -1 if not found. |
| * Can be used to index value from getRawValue. |
| */ |
| getDimensionIndex(dim: DimensionLoose): DimensionIndex { |
| const dimIdx = this._recognizeDimIndex(dim); |
| if (dimIdx != null) { |
| return dimIdx; |
| } |
| |
| if (dim == null) { |
| return -1; |
| } |
| |
| const dimInfo = this._getDimInfo(dim as DimensionName); |
| return dimInfo |
| ? dimInfo.storeDimIndex |
| : this._dimOmitted |
| ? this._schema.getSourceDimensionIndex(dim as DimensionName) |
| : -1; |
| } |
| |
| /** |
| * The meanings of the input parameter `dim`: |
| * |
| * + If dim is a number (e.g., `1`), it means the index of the dimension. |
| * For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'. |
| * + If dim is a number-like string (e.g., `"1"`): |
| * + If there is the same concrete dim name defined in `series.dimensions` or `dataset.dimensions`, |
| * it means that concrete name. |
| * + If not, it will be converted to a number, which means the index of the dimension. |
| * (why? because of the backward compatibility. We have been tolerating number-like string in |
| * dimension setting, although now it seems that it is not a good idea.) |
| * For example, `visualMap[i].dimension: "1"` is the same meaning as `visualMap[i].dimension: 1`, |
| * if no dimension name is defined as `"1"`. |
| * + If dim is a not-number-like string, it means the concrete dim name. |
| * For example, it can be be default name `"x"`, `"y"`, `"z"`, `"lng"`, `"lat"`, `"angle"`, `"radius"`, |
| * or customized in `dimensions` property of option like `"age"`. |
| * |
| * @return recognized `DimensionIndex`. Otherwise return null/undefined (means that dim is `DimensionName`). |
| */ |
| private _recognizeDimIndex(dim: DimensionLoose): DimensionIndex { |
| if (zrUtil.isNumber(dim) |
| // If being a number-like string but not being defined as a dimension name. |
| || ( |
| dim != null |
| && !isNaN(dim as any) |
| && !this._getDimInfo(dim) |
| && (!this._dimOmitted || this._schema.getSourceDimensionIndex(dim) < 0) |
| ) |
| ) { |
| return +dim; |
| } |
| } |
| |
| private _getStoreDimIndex(dim: DimensionLoose): DimensionIndex { |
| const dimIdx = this.getDimensionIndex(dim); |
| if (__DEV__) { |
| if (dimIdx == null) { |
| throw new Error('Unknown dimension ' + dim); |
| } |
| } |
| return dimIdx; |
| } |
| |
| /** |
| * Get type and calculation info of particular dimension |
| * @param dim |
| * Dimension can be concrete names like x, y, z, lng, lat, angle, radius |
| * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius' |
| */ |
| getDimensionInfo(dim: SeriesDimensionLoose): SeriesDimensionDefine { |
| // Do not clone, because there may be categories in dimInfo. |
| return this._getDimInfo(this.getDimension(dim)); |
| } |
| |
| /** |
| * If `dimName` if from outside of `SeriesData`, |
| * use this method other than visit `this._dimInfos` directly. |
| */ |
| private _getDimInfo: (dimName: SeriesDimensionName) => SeriesDimensionDefine; |
| |
| private _initGetDimensionInfo(needsHasOwn: boolean): void { |
| const dimensionInfos = this._dimInfos; |
| this._getDimInfo = needsHasOwn |
| ? dimName => (dimensionInfos.hasOwnProperty(dimName) ? dimensionInfos[dimName] : undefined) |
| : dimName => dimensionInfos[dimName]; |
| } |
| |
| /** |
| * concrete dimension name list on coord. |
| */ |
| getDimensionsOnCoord(): SeriesDimensionName[] { |
| return this._dimSummary.dataDimsOnCoord.slice(); |
| } |
| |
| /** |
| * @param coordDim |
| * @param idx A coordDim may map to more than one data dim. |
| * If not specified, return the first dim not extra. |
| * @return concrete data dim. If not found, return null/undefined |
| */ |
| mapDimension(coordDim: SeriesDimensionName): SeriesDimensionName; |
| mapDimension(coordDim: SeriesDimensionName, idx: number): SeriesDimensionName; |
| mapDimension(coordDim: SeriesDimensionName, idx?: number): SeriesDimensionName { |
| const dimensionsSummary = this._dimSummary; |
| |
| if (idx == null) { |
| return dimensionsSummary.encodeFirstDimNotExtra[coordDim] as any; |
| } |
| |
| const dims = dimensionsSummary.encode[coordDim]; |
| return dims ? dims[idx as number] as any : null; |
| } |
| |
| mapDimensionsAll(coordDim: SeriesDimensionName): SeriesDimensionName[] { |
| const dimensionsSummary = this._dimSummary; |
| const dims = dimensionsSummary.encode[coordDim]; |
| return (dims || []).slice(); |
| } |
| |
| getStore() { |
| return this._store; |
| } |
| |
| /** |
| * Initialize from data |
| * @param data source or data or data store. |
| * @param nameList The name of a datum is used on data diff and |
| * default label/tooltip. |
| * A name can be specified in encode.itemName, |
| * or dataItem.name (only for series option data), |
| * or provided in nameList from outside. |
| */ |
| initData( |
| data: Source | OptionSourceData | DataStore | DataProvider, |
| nameList?: string[], |
| dimValueGetter?: DimValueGetter |
| ): void { |
| let store: DataStore; |
| if (data instanceof DataStore) { |
| store = data; |
| } |
| |
| if (!store) { |
| const dimensions = this.dimensions; |
| const provider = (isSourceInstance(data) || zrUtil.isArrayLike(data)) |
| ? new DefaultDataProvider(data as Source | OptionSourceData, dimensions.length) |
| : data as DataProvider; |
| store = new DataStore(); |
| const dimensionInfos: DataStoreDimensionDefine[] = map(dimensions, dimName => ({ |
| type: this._dimInfos[dimName].type, |
| property: dimName |
| })); |
| store.initData(provider, dimensionInfos, dimValueGetter); |
| } |
| |
| this._store = store; |
| |
| // Reset |
| this._nameList = (nameList || []).slice(); |
| this._idList = []; |
| this._nameRepeatCount = {}; |
| |
| this._doInit(0, store.count()); |
| |
| // Cache summary info for fast visit. See "dimensionHelper". |
| // Needs to be initialized after store is prepared. |
| this._dimSummary = summarizeDimensions(this, this._schema); |
| this.userOutput = this._dimSummary.userOutput; |
| } |
| |
| /** |
| * Caution: Can be only called on raw data (before `this._indices` created). |
| */ |
| appendData(data: ArrayLike<any>): void { |
| const range = this._store.appendData(data); |
| this._doInit(range[0], range[1]); |
| } |
| /** |
| * Caution: Can be only called on raw data (before `this._indices` created). |
| * This method does not modify `rawData` (`dataProvider`), but only |
| * add values to store. |
| * |
| * The final count will be increased by `Math.max(values.length, names.length)`. |
| * |
| * @param values That is the SourceType: 'arrayRows', like |
| * [ |
| * [12, 33, 44], |
| * [NaN, 43, 1], |
| * ['-', 'asdf', 0] |
| * ] |
| * Each item is exactly corresponding to a dimension. |
| */ |
| appendValues(values: any[][], names?: string[]): void { |
| const {start, end} = this._store.appendValues(values, names.length); |
| const shouldMakeIdFromName = this._shouldMakeIdFromName(); |
| |
| this._updateOrdinalMeta(); |
| |
| if (names) { |
| for (let idx = start; idx < end; idx++) { |
| const sourceIdx = idx - start; |
| this._nameList[idx] = names[sourceIdx]; |
| if (shouldMakeIdFromName) { |
| makeIdFromName(this, idx); |
| } |
| } |
| } |
| } |
| |
| private _updateOrdinalMeta(): void { |
| const store = this._store; |
| const dimensions = this.dimensions; |
| for (let i = 0; i < dimensions.length; i++) { |
| const dimInfo = this._dimInfos[dimensions[i]]; |
| if (dimInfo.ordinalMeta) { |
| store.collectOrdinalMeta(dimInfo.storeDimIndex, dimInfo.ordinalMeta); |
| } |
| } |
| } |
| |
| private _shouldMakeIdFromName(): boolean { |
| const provider = this._store.getProvider(); |
| return this._idDimIdx == null |
| && provider.getSource().sourceFormat !== SOURCE_FORMAT_TYPED_ARRAY |
| && !provider.fillStorage; |
| } |
| |
| private _doInit(start: number, end: number): void { |
| if (start >= end) { |
| return; |
| } |
| |
| const store = this._store; |
| const provider = store.getProvider(); |
| |
| this._updateOrdinalMeta(); |
| |
| const nameList = this._nameList; |
| const idList = this._idList; |
| const sourceFormat = provider.getSource().sourceFormat; |
| const isFormatOriginal = sourceFormat === SOURCE_FORMAT_ORIGINAL; |
| |
| // Each data item is value |
| // [1, 2] |
| // 2 |
| // Bar chart, line chart which uses category axis |
| // only gives the 'y' value. 'x' value is the indices of category |
| // Use a tempValue to normalize the value to be a (x, y) value |
| // If dataItem is {name: ...} or {id: ...}, it has highest priority. |
| // This kind of ids and names are always stored `_nameList` and `_idList`. |
| if (isFormatOriginal && !provider.pure) { |
| const sharedDataItem = [] as OptionDataItem; |
| for (let idx = start; idx < end; idx++) { |
| // NOTICE: Try not to write things into dataItem |
| const dataItem = provider.getItem(idx, sharedDataItem); |
| if (!this.hasItemOption && isDataItemOption(dataItem)) { |
| this.hasItemOption = true; |
| } |
| if (dataItem) { |
| const itemName = (dataItem as any).name; |
| if (nameList[idx] == null && itemName != null) { |
| nameList[idx] = convertOptionIdName(itemName, null); |
| } |
| const itemId = (dataItem as any).id; |
| if (idList[idx] == null && itemId != null) { |
| idList[idx] = convertOptionIdName(itemId, null); |
| } |
| } |
| } |
| } |
| |
| if (this._shouldMakeIdFromName()) { |
| for (let idx = start; idx < end; idx++) { |
| makeIdFromName(this, idx); |
| } |
| } |
| |
| prepareInvertedIndex(this); |
| } |
| |
| /** |
| * PENDING: In fact currently this function is only used to short-circuit |
| * the calling of `scale.unionExtentFromData` when data have been filtered by modules |
| * like "dataZoom". `scale.unionExtentFromData` is used to calculate data extent for series on |
| * an axis, but if a "axis related data filter module" is used, the extent of the axis have |
| * been fixed and no need to calling `scale.unionExtentFromData` actually. |
| * But if we add "custom data filter" in future, which is not "axis related", this method may |
| * be still needed. |
| * |
| * Optimize for the scenario that data is filtered by a given extent. |
| * Consider that if data amount is more than hundreds of thousand, |
| * extent calculation will cost more than 10ms and the cache will |
| * be erased because of the filtering. |
| */ |
| getApproximateExtent(dim: SeriesDimensionLoose): [number, number] { |
| return this._approximateExtent[dim] || this._store.getDataExtent(this._getStoreDimIndex(dim)); |
| } |
| |
| /** |
| * Calculate extent on a filtered data might be time consuming. |
| * Approximate extent is only used for: calculate extent of filtered data outside. |
| */ |
| setApproximateExtent(extent: [number, number], dim: SeriesDimensionLoose): void { |
| dim = this.getDimension(dim); |
| this._approximateExtent[dim] = extent.slice() as [number, number]; |
| } |
| |
| getCalculationInfo<CALC_INFO_KEY extends keyof DataCalculationInfo<HostModel>>( |
| key: CALC_INFO_KEY |
| ): DataCalculationInfo<HostModel>[CALC_INFO_KEY] { |
| return this._calculationInfo[key]; |
| } |
| |
| /** |
| * @param key or k-v object |
| */ |
| setCalculationInfo( |
| key: DataCalculationInfo<HostModel> |
| ): void; |
| setCalculationInfo<CALC_INFO_KEY extends keyof DataCalculationInfo<HostModel>>( |
| key: CALC_INFO_KEY, |
| value: DataCalculationInfo<HostModel>[CALC_INFO_KEY] |
| ): void; |
| setCalculationInfo( |
| key: (keyof DataCalculationInfo<HostModel>) | DataCalculationInfo<HostModel>, |
| value?: DataCalculationInfo<HostModel>[keyof DataCalculationInfo<HostModel>] |
| ): void { |
| isObject(key) |
| ? zrUtil.extend(this._calculationInfo, key as object) |
| : ((this._calculationInfo as any)[key] = value); |
| } |
| |
| /** |
| * @return Never be null/undefined. `number` will be converted to string. Because: |
| * In most cases, name is used in display, where returning a string is more convenient. |
| * In other cases, name is used in query (see `indexOfName`), where we can keep the |
| * rule that name `2` equals to name `'2'`. |
| */ |
| getName(idx: number): string { |
| const rawIndex = this.getRawIndex(idx); |
| let name = this._nameList[rawIndex]; |
| if (name == null && this._nameDimIdx != null) { |
| name = getIdNameFromStore(this, this._nameDimIdx, rawIndex); |
| } |
| if (name == null) { |
| name = ''; |
| } |
| return name; |
| } |
| |
| private _getCategory(dimIdx: number, idx: number): OrdinalRawValue { |
| const ordinal = this._store.get(dimIdx, idx); |
| const ordinalMeta = this._store.getOrdinalMeta(dimIdx); |
| if (ordinalMeta) { |
| return ordinalMeta.categories[ordinal as OrdinalNumber]; |
| } |
| return ordinal; |
| } |
| |
| /** |
| * @return Never null/undefined. `number` will be converted to string. Because: |
| * In all cases having encountered at present, id is used in making diff comparison, which |
| * are usually based on hash map. We can keep the rule that the internal id are always string |
| * (treat `2` is the same as `'2'`) to make the related logic simple. |
| */ |
| getId(idx: number): string { |
| return getId(this, this.getRawIndex(idx)); |
| } |
| |
| count(): number { |
| return this._store.count(); |
| } |
| |
| /** |
| * Get value. Return NaN if idx is out of range. |
| * |
| * @notice Should better to use `data.getStore().get(dimIndex, dataIdx)` instead. |
| */ |
| get(dim: SeriesDimensionName, idx: number): ParsedValue { |
| const store = this._store; |
| const dimInfo = this._dimInfos[dim]; |
| if (dimInfo) { |
| return store.get(dimInfo.storeDimIndex, idx); |
| } |
| } |
| |
| /** |
| * @notice Should better to use `data.getStore().getByRawIndex(dimIndex, dataIdx)` instead. |
| */ |
| getByRawIndex(dim: SeriesDimensionName, rawIdx: number): ParsedValue { |
| const store = this._store; |
| const dimInfo = this._dimInfos[dim]; |
| if (dimInfo) { |
| return store.getByRawIndex(dimInfo.storeDimIndex, rawIdx); |
| } |
| } |
| |
| getIndices() { |
| return this._store.getIndices(); |
| } |
| |
| getDataExtent(dim: DimensionLoose): [number, number] { |
| return this._store.getDataExtent(this._getStoreDimIndex(dim)); |
| } |
| |
| getSum(dim: DimensionLoose): number { |
| return this._store.getSum(this._getStoreDimIndex(dim)); |
| } |
| |
| getMedian(dim: DimensionLoose): number { |
| return this._store.getMedian(this._getStoreDimIndex(dim)); |
| } |
| /** |
| * Get value for multi dimensions. |
| * @param dimensions If ignored, using all dimensions. |
| */ |
| getValues(idx: number): ParsedValue[]; |
| getValues(dimensions: readonly DimensionName[], idx: number): ParsedValue[]; |
| getValues(dimensions: readonly DimensionName[] | number, idx?: number): ParsedValue[] { |
| const store = this._store; |
| return zrUtil.isArray(dimensions) |
| ? store.getValues(map(dimensions, dim => this._getStoreDimIndex(dim)), idx) |
| : store.getValues(dimensions as number); |
| } |
| |
| /** |
| * If value is NaN. Including '-' |
| * Only check the coord dimensions. |
| */ |
| hasValue(idx: number): boolean { |
| const dataDimIndicesOnCoord = this._dimSummary.dataDimIndicesOnCoord; |
| for (let i = 0, len = dataDimIndicesOnCoord.length; i < len; i++) { |
| // Ordinal type originally can be string or number. |
| // But when an ordinal type is used on coord, it can |
| // not be string but only number. So we can also use isNaN. |
| if (isNaN(this._store.get(dataDimIndicesOnCoord[i], idx) as any)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Retrieve the index with given name |
| */ |
| indexOfName(name: string): number { |
| for (let i = 0, len = this._store.count(); i < len; i++) { |
| if (this.getName(i) === name) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| getRawIndex(idx: number): number { |
| return this._store.getRawIndex(idx); |
| } |
| |
| indexOfRawIndex(rawIndex: number): number { |
| return this._store.indexOfRawIndex(rawIndex); |
| } |
| |
| /** |
| * Only support the dimension which inverted index created. |
| * Do not support other cases until required. |
| * @param dim concrete dim |
| * @param value ordinal index |
| * @return rawIndex |
| */ |
| rawIndexOf(dim: SeriesDimensionName, value: OrdinalNumber): number { |
| const invertedIndices = dim && this._invertedIndicesMap[dim]; |
| if (__DEV__) { |
| if (!invertedIndices) { |
| throw new Error('Do not supported yet'); |
| } |
| } |
| const rawIndex = invertedIndices[value]; |
| if (rawIndex == null || isNaN(rawIndex)) { |
| return INDEX_NOT_FOUND; |
| } |
| return rawIndex; |
| } |
| |
| /** |
| * Retrieve the index of nearest value |
| * @param dim |
| * @param value |
| * @param [maxDistance=Infinity] |
| * @return If and only if multiple indices has |
| * the same value, they are put to the result. |
| */ |
| indicesOfNearest(dim: DimensionLoose, value: number, maxDistance?: number): number[] { |
| return this._store.indicesOfNearest( |
| this._getStoreDimIndex(dim), |
| value, maxDistance |
| ); |
| } |
| /** |
| * Data iteration |
| * @param ctx default this |
| * @example |
| * list.each('x', function (x, idx) {}); |
| * list.each(['x', 'y'], function (x, y, idx) {}); |
| * list.each(function (idx) {}) |
| */ |
| each<Ctx>(cb: EachCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void; |
| each<Ctx>(dims: DimensionLoose, cb: EachCb1<Ctx>, ctx?: Ctx): void; |
| each<Ctx>(dims: [DimensionLoose], cb: EachCb1<Ctx>, ctx?: Ctx): void; |
| each<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: EachCb2<Ctx>, ctx?: Ctx): void; |
| each<Ctx>(dims: ItrParamDims, cb: EachCb<Ctx>, ctx?: Ctx): void; |
| each<Ctx>( |
| dims: ItrParamDims | EachCb<Ctx>, |
| cb: EachCb<Ctx> | Ctx, |
| ctx?: Ctx |
| ): void { |
| 'use strict'; |
| |
| if (zrUtil.isFunction(dims)) { |
| ctx = cb as Ctx; |
| cb = dims; |
| dims = []; |
| } |
| |
| // ctxCompat just for compat echarts3 |
| const fCtx = (ctx || this) as CtxOrList<Ctx>; |
| |
| const dimIndices = map(normalizeDimensions(dims), this._getStoreDimIndex, this); |
| |
| this._store.each(dimIndices, (fCtx |
| ? zrUtil.bind(cb as any, fCtx as any) |
| : cb) as any |
| ); |
| } |
| /** |
| * Data filter |
| */ |
| filterSelf<Ctx>(cb: FilterCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this; |
| filterSelf<Ctx>(dims: DimensionLoose, cb: FilterCb1<Ctx>, ctx?: Ctx): this; |
| filterSelf<Ctx>(dims: [DimensionLoose], cb: FilterCb1<Ctx>, ctx?: Ctx): this; |
| filterSelf<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: FilterCb2<Ctx>, ctx?: Ctx): this; |
| filterSelf<Ctx>(dims: ItrParamDims, cb: FilterCb<Ctx>, ctx?: Ctx): this; |
| filterSelf<Ctx>( |
| dims: ItrParamDims | FilterCb<Ctx>, |
| cb: FilterCb<Ctx> | Ctx, |
| ctx?: Ctx |
| ): SeriesData { |
| 'use strict'; |
| |
| if (zrUtil.isFunction(dims)) { |
| ctx = cb as Ctx; |
| cb = dims; |
| dims = []; |
| } |
| |
| // ctxCompat just for compat echarts3 |
| const fCtx = (ctx || this) as CtxOrList<Ctx>; |
| |
| const dimIndices = map(normalizeDimensions(dims), this._getStoreDimIndex, this); |
| |
| this._store = this._store.filter(dimIndices, (fCtx |
| ? zrUtil.bind(cb as any, fCtx as any) |
| : cb) as any |
| ); |
| |
| return this; |
| } |
| |
| /** |
| * Select data in range. (For optimization of filter) |
| * (Manually inline code, support 5 million data filtering in data zoom.) |
| */ |
| selectRange(range: Record<string, [number, number]>): SeriesData { |
| 'use strict'; |
| |
| const innerRange: Record<number, [number, number]> = {}; |
| const dims = zrUtil.keys(range); |
| const dimIndices: number[] = []; |
| zrUtil.each(dims, (dim) => { |
| const dimIdx = this._getStoreDimIndex(dim); |
| innerRange[dimIdx] = range[dim]; |
| dimIndices.push(dimIdx); |
| }); |
| |
| this._store = this._store.selectRange(innerRange); |
| return this; |
| } |
| |
| /** |
| * Data mapping to a plain array |
| */ |
| mapArray<Ctx, Cb extends MapArrayCb0<Ctx>>(cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[]; |
| /* eslint-disable max-len */ |
| mapArray<Ctx, Cb extends MapArrayCb1<Ctx>>(dims: DimensionLoose, cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[]; |
| mapArray<Ctx, Cb extends MapArrayCb1<Ctx>>(dims: [DimensionLoose], cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[]; |
| mapArray<Ctx, Cb extends MapArrayCb2<Ctx>>(dims: [DimensionLoose, DimensionLoose], cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[]; |
| mapArray<Ctx, Cb extends MapArrayCb<Ctx>>(dims: ItrParamDims, cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[]; |
| /* eslint-enable max-len */ |
| mapArray<Ctx>( |
| dims: ItrParamDims | MapArrayCb<Ctx>, |
| cb: MapArrayCb<Ctx> | Ctx, |
| ctx?: Ctx |
| ): unknown[] { |
| 'use strict'; |
| |
| if (zrUtil.isFunction(dims)) { |
| ctx = cb as Ctx; |
| cb = dims; |
| dims = []; |
| } |
| |
| // ctxCompat just for compat echarts3 |
| ctx = (ctx || this) as Ctx; |
| |
| const result: unknown[] = []; |
| this.each(dims, function () { |
| result.push(cb && (cb as MapArrayCb<Ctx>).apply(this, arguments)); |
| }, ctx); |
| return result; |
| } |
| |
| /** |
| * Data mapping to a new List with given dimensions |
| */ |
| map<Ctx>(dims: DimensionLoose, cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): SeriesData<HostModel>; |
| map<Ctx>(dims: [DimensionLoose], cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): SeriesData<HostModel>; |
| // eslint-disable-next-line max-len |
| map<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: MapCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): SeriesData<HostModel>; |
| map<Ctx>( |
| dims: ItrParamDims, |
| cb: MapCb<Ctx>, |
| ctx?: Ctx, |
| ctxCompat?: Ctx |
| ): SeriesData { |
| 'use strict'; |
| |
| // ctxCompat just for compat echarts3 |
| const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>; |
| |
| const dimIndices = map( |
| normalizeDimensions(dims), this._getStoreDimIndex, this |
| ); |
| |
| const list = cloneListForMapAndSample(this); |
| list._store = this._store.map( |
| dimIndices, |
| fCtx ? zrUtil.bind(cb, fCtx) : cb |
| ); |
| return list; |
| } |
| |
| /** |
| * !!Danger: used on stack dimension only. |
| */ |
| modify<Ctx>(dims: DimensionLoose, cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void; |
| modify<Ctx>(dims: [DimensionLoose], cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void; |
| modify<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: MapCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void; |
| modify<Ctx>( |
| dims: ItrParamDims, |
| cb: MapCb<Ctx>, |
| ctx?: Ctx, |
| ctxCompat?: Ctx |
| ): void { |
| // ctxCompat just for compat echarts3 |
| const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>; |
| |
| if (__DEV__) { |
| zrUtil.each(normalizeDimensions(dims), dim => { |
| const dimInfo = this.getDimensionInfo(dim); |
| if (!dimInfo.isCalculationCoord) { |
| console.error('Danger: only stack dimension can be modified'); |
| } |
| }); |
| } |
| |
| const dimIndices = map( |
| normalizeDimensions(dims), this._getStoreDimIndex, this |
| ); |
| |
| // If do shallow clone here, if there are too many stacked series, |
| // it still cost lots of memory, because `_store.dimensions` are not shared. |
| // We should consider there probably be shallow clone happen in each series |
| // in consequent filter/map. |
| this._store.modify( |
| dimIndices, |
| fCtx ? zrUtil.bind(cb, fCtx) : cb |
| ); |
| } |
| |
| /** |
| * Large data down sampling on given dimension |
| * @param sampleIndex Sample index for name and id |
| */ |
| downSample( |
| dimension: DimensionLoose, |
| rate: number, |
| sampleValue: (frameValues: ArrayLike<ParsedValue>) => ParsedValueNumeric, |
| sampleIndex: (frameValues: ArrayLike<ParsedValue>, value: ParsedValueNumeric) => number |
| ): SeriesData<HostModel> { |
| const list = cloneListForMapAndSample(this); |
| list._store = this._store.downSample( |
| this._getStoreDimIndex(dimension), |
| rate, |
| sampleValue, |
| sampleIndex |
| ); |
| return list as SeriesData<HostModel>; |
| } |
| |
| /** |
| * Large data down sampling using largest-triangle-three-buckets |
| * @param {string} valueDimension |
| * @param {number} targetCount |
| */ |
| lttbDownSample( |
| valueDimension: DimensionLoose, |
| rate: number |
| ): SeriesData<HostModel> { |
| const list = cloneListForMapAndSample(this); |
| list._store = this._store.lttbDownSample( |
| this._getStoreDimIndex(valueDimension), |
| rate |
| ); |
| return list as SeriesData<HostModel>; |
| } |
| |
| getRawDataItem(idx: number) { |
| return this._store.getRawDataItem(idx); |
| } |
| |
| /** |
| * Get model of one data item. |
| */ |
| // TODO: Type of data item |
| getItemModel<ItemOpts extends unknown = unknown>(idx: number): Model<ItemOpts |
| // Extract item option with value key. FIXME will cause incompatible issue |
| // Extract<HostModel['option']['data'][number], { value?: any }> |
| > { |
| const hostModel = this.hostModel; |
| const dataItem = this.getRawDataItem(idx) as ModelOption; |
| return new Model(dataItem, hostModel, hostModel && hostModel.ecModel); |
| } |
| |
| /** |
| * Create a data differ |
| */ |
| diff(otherList: SeriesData): DataDiffer { |
| const thisList = this; |
| |
| return new DataDiffer( |
| otherList ? otherList.getStore().getIndices() : [], |
| this.getStore().getIndices(), |
| function (idx: number) { |
| return getId(otherList, idx); |
| }, |
| function (idx: number) { |
| return getId(thisList, idx); |
| } |
| ); |
| } |
| |
| /** |
| * Get visual property. |
| */ |
| getVisual<K extends keyof Visual>(key: K): Visual[K] { |
| const visual = this._visual as Visual; |
| return visual && visual[key]; |
| } |
| |
| /** |
| * Set visual property |
| * |
| * @example |
| * setVisual('color', color); |
| * setVisual({ |
| * 'color': color |
| * }); |
| */ |
| setVisual<K extends keyof Visual>(key: K, val: Visual[K]): void; |
| setVisual(kvObj: Partial<Visual>): void; |
| setVisual(kvObj: string | Partial<Visual>, val?: any): void { |
| this._visual = this._visual || {}; |
| if (isObject(kvObj)) { |
| zrUtil.extend(this._visual, kvObj); |
| } |
| else { |
| this._visual[kvObj as string] = val; |
| } |
| } |
| |
| /** |
| * Get visual property of single data item |
| */ |
| // eslint-disable-next-line |
| getItemVisual<K extends keyof Visual>(idx: number, key: K): Visual[K] { |
| const itemVisual = this._itemVisuals[idx] as Visual; |
| const val = itemVisual && itemVisual[key]; |
| if (val == null) { |
| // Use global visual property |
| return this.getVisual(key); |
| } |
| return val; |
| } |
| |
| /** |
| * If exists visual property of single data item |
| */ |
| hasItemVisual() { |
| return this._itemVisuals.length > 0; |
| } |
| |
| /** |
| * Make sure itemVisual property is unique |
| */ |
| // TODO: use key to save visual to reduce memory. |
| ensureUniqueItemVisual<K extends keyof Visual>(idx: number, key: K): Visual[K] { |
| const itemVisuals = this._itemVisuals; |
| let itemVisual = itemVisuals[idx] as Visual; |
| if (!itemVisual) { |
| itemVisual = itemVisuals[idx] = {} as Visual; |
| } |
| let val = itemVisual[key]; |
| if (val == null) { |
| val = this.getVisual(key); |
| |
| // TODO Performance? |
| if (zrUtil.isArray(val)) { |
| val = val.slice() as unknown as Visual[K]; |
| } |
| else if (isObject(val)) { |
| val = zrUtil.extend({}, val); |
| } |
| |
| itemVisual[key] = val; |
| } |
| return val; |
| } |
| /** |
| * Set visual property of single data item |
| * |
| * @param {number} idx |
| * @param {string|Object} key |
| * @param {*} [value] |
| * |
| * @example |
| * setItemVisual(0, 'color', color); |
| * setItemVisual(0, { |
| * 'color': color |
| * }); |
| */ |
| // eslint-disable-next-line |
| setItemVisual<K extends keyof Visual>(idx: number, key: K, value: Visual[K]): void; |
| setItemVisual(idx: number, kvObject: Partial<Visual>): void; |
| // eslint-disable-next-line |
| setItemVisual<K extends keyof Visual>(idx: number, key: K | Partial<Visual>, value?: Visual[K]): void { |
| const itemVisual = this._itemVisuals[idx] || {}; |
| this._itemVisuals[idx] = itemVisual; |
| |
| if (isObject(key)) { |
| zrUtil.extend(itemVisual, key); |
| } |
| else { |
| itemVisual[key as string] = value; |
| } |
| } |
| |
| /** |
| * Clear itemVisuals and list visual. |
| */ |
| clearAllVisual(): void { |
| this._visual = {}; |
| this._itemVisuals = []; |
| } |
| |
| /** |
| * Set layout property. |
| */ |
| setLayout(key: string, val: any): void; |
| setLayout(kvObj: Dictionary<any>): void; |
| setLayout(key: string | Dictionary<any>, val?: any): void { |
| isObject(key) |
| ? zrUtil.extend(this._layout, key) |
| : (this._layout[key] = val); |
| } |
| |
| /** |
| * Get layout property. |
| */ |
| getLayout(key: string): any { |
| return this._layout[key]; |
| } |
| |
| /** |
| * Get layout of single data item |
| */ |
| getItemLayout(idx: number): any { |
| return this._itemLayouts[idx]; |
| } |
| |
| /** |
| * Set layout of single data item |
| */ |
| setItemLayout<M = false>( |
| idx: number, |
| layout: (M extends true ? Dictionary<any> : any), |
| merge?: M |
| ): void { |
| this._itemLayouts[idx] = merge |
| ? zrUtil.extend(this._itemLayouts[idx] || {}, layout) |
| : layout; |
| } |
| |
| /** |
| * Clear all layout of single data item |
| */ |
| clearItemLayouts(): void { |
| this._itemLayouts.length = 0; |
| } |
| |
| /** |
| * Set graphic element relative to data. It can be set as null |
| */ |
| setItemGraphicEl(idx: number, el: Element): void { |
| const seriesIndex = this.hostModel && (this.hostModel as any).seriesIndex; |
| |
| setCommonECData(seriesIndex, this.dataType, idx, el); |
| |
| this._graphicEls[idx] = el; |
| } |
| |
| getItemGraphicEl(idx: number): Element { |
| return this._graphicEls[idx]; |
| } |
| |
| eachItemGraphicEl<Ctx = unknown>( |
| cb: (this: Ctx, el: Element, idx: number) => void, |
| context?: Ctx |
| ): void { |
| zrUtil.each(this._graphicEls, function (el, idx) { |
| if (el) { |
| cb && cb.call(context, el, idx); |
| } |
| }); |
| } |
| |
| /** |
| * Shallow clone a new list except visual and layout properties, and graph elements. |
| * New list only change the indices. |
| */ |
| cloneShallow(list?: SeriesData<HostModel>): SeriesData<HostModel> { |
| if (!list) { |
| list = new SeriesData( |
| this._schema |
| ? this._schema |
| : map(this.dimensions, this._getDimInfo, this), |
| this.hostModel |
| ); |
| } |
| |
| transferProperties(list, this); |
| list._store = this._store; |
| |
| return list; |
| } |
| |
| /** |
| * Wrap some method to add more feature |
| */ |
| wrapMethod( |
| methodName: FunctionPropertyNames<SeriesData>, |
| injectFunction: (...args: any) => any |
| ): void { |
| const originalMethod = this[methodName]; |
| if (!zrUtil.isFunction(originalMethod)) { |
| return; |
| } |
| this.__wrappedMethods = this.__wrappedMethods || []; |
| this.__wrappedMethods.push(methodName); |
| this[methodName] = function () { |
| const res = (originalMethod as any).apply(this, arguments); |
| return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments))); |
| }; |
| } |
| |
| |
| // ---------------------------------------------------------- |
| // A work around for internal method visiting private member. |
| // ---------------------------------------------------------- |
| private static internalField = (function () { |
| |
| prepareInvertedIndex = function (data: SeriesData): void { |
| const invertedIndicesMap = data._invertedIndicesMap; |
| zrUtil.each(invertedIndicesMap, function (invertedIndices, dim) { |
| const dimInfo = data._dimInfos[dim]; |
| // Currently, only dimensions that has ordinalMeta can create inverted indices. |
| const ordinalMeta = dimInfo.ordinalMeta; |
| const store = data._store; |
| if (ordinalMeta) { |
| invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array( |
| ordinalMeta.categories.length |
| ); |
| // The default value of TypedArray is 0. To avoid miss |
| // mapping to 0, we should set it as INDEX_NOT_FOUND. |
| for (let i = 0; i < invertedIndices.length; i++) { |
| invertedIndices[i] = INDEX_NOT_FOUND; |
| } |
| for (let i = 0; i < store.count(); i++) { |
| // Only support the case that all values are distinct. |
| invertedIndices[store.get(dimInfo.storeDimIndex, i) as number] = i; |
| } |
| } |
| }); |
| }; |
| |
| getIdNameFromStore = function ( |
| data: SeriesData, dimIdx: number, idx: number |
| ): string { |
| return convertOptionIdName(data._getCategory(dimIdx, idx), null); |
| }; |
| |
| /** |
| * @see the comment of `List['getId']`. |
| */ |
| getId = function (data: SeriesData, rawIndex: number): string { |
| let id = data._idList[rawIndex]; |
| if (id == null && data._idDimIdx != null) { |
| id = getIdNameFromStore(data, data._idDimIdx, rawIndex); |
| } |
| if (id == null) { |
| id = ID_PREFIX + rawIndex; |
| } |
| return id; |
| }; |
| |
| normalizeDimensions = function ( |
| dimensions: ItrParamDims |
| ): Array<DimensionLoose> { |
| if (!zrUtil.isArray(dimensions)) { |
| dimensions = dimensions != null ? [dimensions] : []; |
| } |
| return dimensions; |
| }; |
| |
| /** |
| * Data in excludeDimensions is copied, otherwise transferred. |
| */ |
| cloneListForMapAndSample = function (original: SeriesData): SeriesData { |
| const list = new SeriesData( |
| original._schema |
| ? original._schema |
| : map(original.dimensions, original._getDimInfo, original), |
| original.hostModel |
| ); |
| // FIXME If needs stackedOn, value may already been stacked |
| transferProperties(list, original); |
| return list; |
| }; |
| |
| transferProperties = function (target: SeriesData, source: SeriesData): void { |
| zrUtil.each( |
| TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), |
| function (propName) { |
| if (source.hasOwnProperty(propName)) { |
| (target as any)[propName] = (source as any)[propName]; |
| } |
| } |
| ); |
| |
| target.__wrappedMethods = source.__wrappedMethods; |
| |
| zrUtil.each(CLONE_PROPERTIES, function (propName) { |
| (target as any)[propName] = zrUtil.clone((source as any)[propName]); |
| }); |
| |
| target._calculationInfo = zrUtil.extend({}, source._calculationInfo); |
| }; |
| makeIdFromName = function (data: SeriesData, idx: number): void { |
| const nameList = data._nameList; |
| const idList = data._idList; |
| const nameDimIdx = data._nameDimIdx; |
| const idDimIdx = data._idDimIdx; |
| |
| let name = nameList[idx]; |
| let id = idList[idx]; |
| |
| if (name == null && nameDimIdx != null) { |
| nameList[idx] = name = getIdNameFromStore(data, nameDimIdx, idx); |
| } |
| if (id == null && idDimIdx != null) { |
| idList[idx] = id = getIdNameFromStore(data, idDimIdx, idx); |
| } |
| if (id == null && name != null) { |
| const nameRepeatCount = data._nameRepeatCount; |
| const nmCnt = nameRepeatCount[name] = (nameRepeatCount[name] || 0) + 1; |
| id = name; |
| if (nmCnt > 1) { |
| id += '__ec__' + nmCnt; |
| } |
| idList[idx] = id; |
| } |
| }; |
| })(); |
| |
| } |
| |
| interface SeriesData { |
| getLinkedData(dataType?: SeriesDataType): SeriesData; |
| getLinkedDataAll(): { data: SeriesData, type?: SeriesDataType }[]; |
| } |
| |
| export default SeriesData; |