| /* | |
| * 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, createHashMap, assert, map} from 'zrender/src/core/util'; | |
| import SeriesData from '../SeriesData'; | |
| import { | |
| DimensionName, VISUAL_DIMENSIONS, DimensionType, DimensionIndex | |
| } from '../../util/types'; | |
| import { DataStoreDimensionType } from '../DataStore'; | |
| import { SeriesDataSchema } from './SeriesDataSchema'; | |
| export type DimensionSummaryEncode = { | |
| defaultedLabel: DimensionName[], | |
| defaultedTooltip: DimensionName[], | |
| [coordOrVisualDimName: string]: | |
| // index: coordDimIndex, value: dataDimName | |
| DimensionName[] | |
| }; | |
| export type DimensionSummary = { | |
| encode: DimensionSummaryEncode, | |
| // Those details that can be expose to users are put int `userOutput`. | |
| userOutput: DimensionUserOuput, | |
| // All of the data dim names that mapped by coordDim. | |
| dataDimsOnCoord: DimensionName[], | |
| dataDimIndicesOnCoord: DimensionIndex[], | |
| encodeFirstDimNotExtra: {[coordDim: string]: DimensionName}, | |
| }; | |
| export type DimensionUserOuputEncode = { | |
| // index: coordDimIndex, value: dataDimIndex | |
| [coordOrVisualDimName: string]: DimensionIndex[] | |
| }; | |
| class DimensionUserOuput { | |
| private _encode: DimensionUserOuputEncode; | |
| private _cachedDimNames: DimensionName[]; | |
| private _schema?: SeriesDataSchema; | |
| constructor( | |
| encode: DimensionUserOuputEncode, | |
| dimRequest?: SeriesDataSchema | |
| ) { | |
| this._encode = encode; | |
| this._schema = dimRequest; | |
| } | |
| get(): { | |
| fullDimensions: DimensionName[]; | |
| encode: DimensionUserOuputEncode; | |
| } { | |
| return { | |
| // Do not generate full dimension name until fist used. | |
| fullDimensions: this._getFullDimensionNames(), | |
| encode: this._encode | |
| }; | |
| } | |
| /** | |
| * Get all data store dimension names. | |
| * Theoretically a series data store is defined both by series and used dataset (if any). | |
| * If some dimensions are omitted for performance reason in `this.dimensions`, | |
| * the dimension name may not be auto-generated if user does not specify a dimension name. | |
| * In this case, the dimension name is `null`/`undefined`. | |
| */ | |
| private _getFullDimensionNames(): DimensionName[] { | |
| if (!this._cachedDimNames) { | |
| this._cachedDimNames = this._schema | |
| ? this._schema.makeOutputDimensionNames() | |
| : []; | |
| } | |
| return this._cachedDimNames; | |
| } | |
| }; | |
| export function summarizeDimensions( | |
| data: SeriesData, | |
| schema?: SeriesDataSchema | |
| ): DimensionSummary { | |
| const summary: DimensionSummary = {} as DimensionSummary; | |
| const encode = summary.encode = {} as DimensionSummaryEncode; | |
| const notExtraCoordDimMap = createHashMap<1, DimensionName>(); | |
| let defaultedLabel = [] as DimensionName[]; | |
| let defaultedTooltip = [] as DimensionName[]; | |
| const userOutputEncode = {} as DimensionUserOuputEncode; | |
| each(data.dimensions, function (dimName) { | |
| const dimItem = data.getDimensionInfo(dimName); | |
| const coordDim = dimItem.coordDim; | |
| if (coordDim) { | |
| if (__DEV__) { | |
| assert(VISUAL_DIMENSIONS.get(coordDim as any) == null); | |
| } | |
| const coordDimIndex = dimItem.coordDimIndex; | |
| getOrCreateEncodeArr(encode, coordDim)[coordDimIndex] = dimName; | |
| if (!dimItem.isExtraCoord) { | |
| notExtraCoordDimMap.set(coordDim, 1); | |
| // Use the last coord dim (and label friendly) as default label, | |
| // because when dataset is used, it is hard to guess which dimension | |
| // can be value dimension. If both show x, y on label is not look good, | |
| // and conventionally y axis is focused more. | |
| if (mayLabelDimType(dimItem.type)) { | |
| defaultedLabel[0] = dimName; | |
| } | |
| // User output encode do not contain generated coords. | |
| // And it only has index. User can use index to retrieve value from the raw item array. | |
| getOrCreateEncodeArr(userOutputEncode, coordDim)[coordDimIndex] = | |
| data.getDimensionIndex(dimItem.name); | |
| } | |
| if (dimItem.defaultTooltip) { | |
| defaultedTooltip.push(dimName); | |
| } | |
| } | |
| VISUAL_DIMENSIONS.each(function (v, otherDim) { | |
| const encodeArr = getOrCreateEncodeArr(encode, otherDim); | |
| const dimIndex = dimItem.otherDims[otherDim]; | |
| if (dimIndex != null && dimIndex !== false) { | |
| encodeArr[dimIndex] = dimItem.name; | |
| } | |
| }); | |
| }); | |
| let dataDimsOnCoord = [] as DimensionName[]; | |
| const encodeFirstDimNotExtra = {} as {[coordDim: string]: DimensionName}; | |
| notExtraCoordDimMap.each(function (v, coordDim) { | |
| const dimArr = encode[coordDim]; | |
| encodeFirstDimNotExtra[coordDim] = dimArr[0]; | |
| // Not necessary to remove duplicate, because a data | |
| // dim canot on more than one coordDim. | |
| dataDimsOnCoord = dataDimsOnCoord.concat(dimArr); | |
| }); | |
| summary.dataDimsOnCoord = dataDimsOnCoord; | |
| summary.dataDimIndicesOnCoord = map( | |
| dataDimsOnCoord, dimName => data.getDimensionInfo(dimName).storeDimIndex | |
| ); | |
| summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra; | |
| const encodeLabel = encode.label; | |
| // FIXME `encode.label` is not recommended, because formatter cannot be set | |
| // in this way. Use label.formatter instead. Maybe remove this approach someday. | |
| if (encodeLabel && encodeLabel.length) { | |
| defaultedLabel = encodeLabel.slice(); | |
| } | |
| const encodeTooltip = encode.tooltip; | |
| if (encodeTooltip && encodeTooltip.length) { | |
| defaultedTooltip = encodeTooltip.slice(); | |
| } | |
| else if (!defaultedTooltip.length) { | |
| defaultedTooltip = defaultedLabel.slice(); | |
| } | |
| encode.defaultedLabel = defaultedLabel; | |
| encode.defaultedTooltip = defaultedTooltip; | |
| summary.userOutput = new DimensionUserOuput(userOutputEncode, schema); | |
| return summary; | |
| } | |
| function getOrCreateEncodeArr( | |
| encode: DimensionSummaryEncode | DimensionUserOuputEncode, dim: DimensionName | |
| ): (DimensionIndex | DimensionName)[] { | |
| if (!encode.hasOwnProperty(dim)) { | |
| encode[dim] = []; | |
| } | |
| return encode[dim]; | |
| } | |
| // FIXME:TS should be type `AxisType` | |
| export function getDimensionTypeByAxis(axisType: string): DataStoreDimensionType { | |
| return axisType === 'category' | |
| ? 'ordinal' | |
| : axisType === 'time' | |
| ? 'time' | |
| : 'float'; | |
| } | |
| function mayLabelDimType(dimType: DimensionType): boolean { | |
| // In most cases, ordinal and time do not suitable for label. | |
| // Ordinal info can be displayed on axis. Time is too long. | |
| return !(dimType === 'ordinal' || dimType === 'time'); | |
| } | |
| // function findTheLastDimMayLabel(data) { | |
| // // Get last value dim | |
| // let dimensions = data.dimensions.slice(); | |
| // let valueType; | |
| // let valueDim; | |
| // while (dimensions.length && ( | |
| // valueDim = dimensions.pop(), | |
| // valueType = data.getDimensionInfo(valueDim).type, | |
| // valueType === 'ordinal' || valueType === 'time' | |
| // )) {} // jshint ignore:line | |
| // return valueDim; | |
| // } |