echarts createDimensions 源码
echarts createDimensions 代码
文件路径:/src/data/helper/createDimensions.ts
/*
* 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 {
DimensionDefinitionLoose, OptionEncode, OptionEncodeValue,
EncodeDefaulter,
OptionSourceData,
DimensionName,
DimensionDefinition,
DataVisualDimensions,
DimensionIndex,
VISUAL_DIMENSIONS
} from '../../util/types';
import SeriesDimensionDefine from '../SeriesDimensionDefine';
import {
createHashMap, defaults, each, extend, HashMap, isObject, isString
} from 'zrender/src/core/util';
import OrdinalMeta from '../OrdinalMeta';
import { createSourceFromSeriesDataOption, isSourceInstance, Source } from '../Source';
import { CtorInt32Array } from '../DataStore';
import { normalizeToArray } from '../../util/model';
import { BE_ORDINAL, guessOrdinal } from './sourceHelper';
import {
createDimNameMap, ensureSourceDimNameMap, SeriesDataSchema, shouldOmitUnusedDimensions
} from './SeriesDataSchema';
export interface CoordDimensionDefinition extends DimensionDefinition {
dimsDef?: (DimensionName | { name: DimensionName, defaultTooltip?: boolean })[];
otherDims?: DataVisualDimensions;
ordinalMeta?: OrdinalMeta;
coordDim?: DimensionName;
coordDimIndex?: DimensionIndex;
}
export type CoordDimensionDefinitionLoose = CoordDimensionDefinition['name'] | CoordDimensionDefinition;
export type PrepareSeriesDataSchemaParams = {
coordDimensions?: CoordDimensionDefinitionLoose[],
/**
* Will use `source.dimensionsDefine` if not given.
*/
dimensionsDefine?: DimensionDefinitionLoose[],
/**
* Will use `source.encodeDefine` if not given.
*/
encodeDefine?: HashMap<OptionEncodeValue, DimensionName> | OptionEncode,
dimensionsCount?: number,
/**
* Make default encode if user not specified.
*/
encodeDefaulter?: EncodeDefaulter,
generateCoord?: string,
generateCoordCount?: number,
/**
* If be able to omit unused dimension
* Used to improve the performance on high dimension data.
*/
canOmitUnusedDimensions?: boolean
};
/**
* For outside usage compat (like echarts-gl are using it).
*/
export function createDimensions(
source: Source | OptionSourceData,
opt?: PrepareSeriesDataSchemaParams
): SeriesDimensionDefine[] {
return prepareSeriesDataSchema(source, opt).dimensions;
}
/**
* This method builds the relationship between:
* + "what the coord sys or series requires (see `coordDimensions`)",
* + "what the user defines (in `encode` and `dimensions`, see `opt.dimensionsDefine` and `opt.encodeDefine`)"
* + "what the data source provids (see `source`)".
*
* Some guess strategy will be adapted if user does not define something.
* If no 'value' dimension specified, the first no-named dimension will be
* named as 'value'.
*
* @return The results are always sorted by `storeDimIndex` asc.
*/
export default function prepareSeriesDataSchema(
// TODO: TYPE completeDimensions type
source: Source | OptionSourceData,
opt?: PrepareSeriesDataSchemaParams
): SeriesDataSchema {
if (!isSourceInstance(source)) {
source = createSourceFromSeriesDataOption(source as OptionSourceData);
}
opt = opt || {};
const sysDims = opt.coordDimensions || [];
const dimsDef = opt.dimensionsDefine || source.dimensionsDefine || [];
const coordDimNameMap = createHashMap<true, DimensionName>();
const resultList: SeriesDimensionDefine[] = [];
const dimCount = getDimCount(source, sysDims, dimsDef, opt.dimensionsCount);
// Try to ignore unsed dimensions if sharing a high dimension datastore
// 30 is an experience value.
const omitUnusedDimensions = opt.canOmitUnusedDimensions && shouldOmitUnusedDimensions(dimCount);
const isUsingSourceDimensionsDef = dimsDef === source.dimensionsDefine;
const dataDimNameMap = isUsingSourceDimensionsDef
? ensureSourceDimNameMap(source) : createDimNameMap(dimsDef);
let encodeDef = opt.encodeDefine;
if (!encodeDef && opt.encodeDefaulter) {
encodeDef = opt.encodeDefaulter(source, dimCount);
}
const encodeDefMap = createHashMap<DimensionIndex[] | false, DimensionName>(encodeDef as any);
const indicesMap = new CtorInt32Array(dimCount);
for (let i = 0; i < indicesMap.length; i++) {
indicesMap[i] = -1;
}
function getResultItem(dimIdx: number) {
const idx = indicesMap[dimIdx];
if (idx < 0) {
const dimDefItemRaw = dimsDef[dimIdx];
const dimDefItem = isObject(dimDefItemRaw) ? dimDefItemRaw : { name: dimDefItemRaw };
const resultItem = new SeriesDimensionDefine();
const userDimName = dimDefItem.name;
if (userDimName != null && dataDimNameMap.get(userDimName) != null) {
// Only if `series.dimensions` is defined in option
// displayName, will be set, and dimension will be diplayed vertically in
// tooltip by default.
resultItem.name = resultItem.displayName = userDimName;
}
dimDefItem.type != null && (resultItem.type = dimDefItem.type);
dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName);
const newIdx = resultList.length;
indicesMap[dimIdx] = newIdx;
resultItem.storeDimIndex = dimIdx;
resultList.push(resultItem);
return resultItem;
}
return resultList[idx];
}
if (!omitUnusedDimensions) {
for (let i = 0; i < dimCount; i++) {
getResultItem(i);
}
}
// Set `coordDim` and `coordDimIndex` by `encodeDefMap` and normalize `encodeDefMap`.
encodeDefMap.each(function (dataDimsRaw, coordDim) {
const dataDims = normalizeToArray(dataDimsRaw as []).slice();
// Note: It is allowed that `dataDims.length` is `0`, e.g., options is
// `{encode: {x: -1, y: 1}}`. Should not filter anything in
// this case.
if (dataDims.length === 1 && !isString(dataDims[0]) && dataDims[0] < 0) {
encodeDefMap.set(coordDim, false);
return;
}
const validDataDims = encodeDefMap.set(coordDim, []) as DimensionIndex[];
each(dataDims, function (resultDimIdxOrName, idx) {
// The input resultDimIdx can be dim name or index.
const resultDimIdx = isString(resultDimIdxOrName)
? dataDimNameMap.get(resultDimIdxOrName)
: resultDimIdxOrName;
if (resultDimIdx != null && resultDimIdx < dimCount) {
validDataDims[idx] = resultDimIdx;
applyDim(getResultItem(resultDimIdx), coordDim, idx);
}
});
});
// Apply templetes and default order from `sysDims`.
let availDimIdx = 0;
each(sysDims, function (sysDimItemRaw) {
let coordDim: DimensionName;
let sysDimItemDimsDef: CoordDimensionDefinition['dimsDef'];
let sysDimItemOtherDims: CoordDimensionDefinition['otherDims'];
let sysDimItem: CoordDimensionDefinition;
if (isString(sysDimItemRaw)) {
coordDim = sysDimItemRaw;
sysDimItem = {} as CoordDimensionDefinition;
}
else {
sysDimItem = sysDimItemRaw;
coordDim = sysDimItem.name;
const ordinalMeta = sysDimItem.ordinalMeta;
sysDimItem.ordinalMeta = null;
sysDimItem = extend({}, sysDimItem);
sysDimItem.ordinalMeta = ordinalMeta;
// `coordDimIndex` should not be set directly.
sysDimItemDimsDef = sysDimItem.dimsDef;
sysDimItemOtherDims = sysDimItem.otherDims;
sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex =
sysDimItem.dimsDef = sysDimItem.otherDims = null;
}
let dataDims = encodeDefMap.get(coordDim);
// negative resultDimIdx means no need to mapping.
if (dataDims === false) {
return;
}
dataDims = normalizeToArray(dataDims);
// dimensions provides default dim sequences.
if (!dataDims.length) {
for (let i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {
while (availDimIdx < dimCount && getResultItem(availDimIdx).coordDim != null) {
availDimIdx++;
}
availDimIdx < dimCount && dataDims.push(availDimIdx++);
}
}
// Apply templates.
each(dataDims, function (resultDimIdx, coordDimIndex) {
const resultItem = getResultItem(resultDimIdx);
// Coordinate system has a higher priority on dim type than source.
if (isUsingSourceDimensionsDef && sysDimItem.type != null) {
resultItem.type = sysDimItem.type;
}
applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex);
if (resultItem.name == null && sysDimItemDimsDef) {
let sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex];
!isObject(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = {
name: sysDimItemDimsDefItem
});
resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name;
resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip;
}
// FIXME refactor, currently only used in case: {otherDims: {tooltip: false}}
sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims);
});
});
function applyDim(resultItem: SeriesDimensionDefine, coordDim: DimensionName, coordDimIndex: DimensionIndex) {
if (VISUAL_DIMENSIONS.get(coordDim as keyof DataVisualDimensions) != null) {
resultItem.otherDims[coordDim as keyof DataVisualDimensions] = coordDimIndex;
}
else {
resultItem.coordDim = coordDim;
resultItem.coordDimIndex = coordDimIndex;
coordDimNameMap.set(coordDim, true);
}
}
// Make sure the first extra dim is 'value'.
const generateCoord = opt.generateCoord;
let generateCoordCount = opt.generateCoordCount;
const fromZero = generateCoordCount != null;
generateCoordCount = generateCoord ? (generateCoordCount || 1) : 0;
const extra = generateCoord || 'value';
function ifNoNameFillWithCoordName(resultItem: SeriesDimensionDefine): void {
if (resultItem.name == null) {
// Duplication will be removed in the next step.
resultItem.name = resultItem.coordDim;
}
}
// Set dim `name` and other `coordDim` and other props.
if (!omitUnusedDimensions) {
for (let resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {
const resultItem = getResultItem(resultDimIdx);
const coordDim = resultItem.coordDim;
if (coordDim == null) {
// TODO no need to generate coordDim for isExtraCoord?
resultItem.coordDim = genCoordDimName(
extra, coordDimNameMap, fromZero
);
resultItem.coordDimIndex = 0;
// Series specified generateCoord is using out.
if (!generateCoord || generateCoordCount <= 0) {
resultItem.isExtraCoord = true;
}
generateCoordCount--;
}
ifNoNameFillWithCoordName(resultItem);
if (resultItem.type == null
&& (
guessOrdinal(source, resultDimIdx) === BE_ORDINAL.Must
// Consider the case:
// {
// dataset: {source: [
// ['2001', 123],
// ['2002', 456],
// ...
// ['The others', 987],
// ]},
// series: {type: 'pie'}
// }
// The first colum should better be treated as a "ordinal" although it
// might not able to be detected as an "ordinal" by `guessOrdinal`.
|| (resultItem.isExtraCoord
&& (resultItem.otherDims.itemName != null
|| resultItem.otherDims.seriesName != null
)
)
)
) {
resultItem.type = 'ordinal';
}
}
}
else {
each(resultList, resultItem => {
// PENDING: guessOrdinal or let user specify type: 'ordinal' manually?
ifNoNameFillWithCoordName(resultItem);
});
// Sort dimensions: there are some rule that use the last dim as label,
// and for some latter travel process easier.
resultList.sort((item0, item1) => item0.storeDimIndex - item1.storeDimIndex);
}
removeDuplication(resultList);
return new SeriesDataSchema({
source,
dimensions: resultList,
fullDimensionCount: dimCount,
dimensionOmitted: omitUnusedDimensions
});
}
function removeDuplication(result: SeriesDimensionDefine[]) {
const duplicationMap = createHashMap<number>();
for (let i = 0; i < result.length; i++) {
const dim = result[i];
const dimOriginalName = dim.name;
let count = duplicationMap.get(dimOriginalName) || 0;
if (count > 0) {
// Starts from 0.
dim.name = dimOriginalName + (count - 1);
}
count++;
duplicationMap.set(dimOriginalName, count);
}
}
// ??? TODO
// Originally detect dimCount by data[0]. Should we
// optimize it to only by sysDims and dimensions and encode.
// So only necessary dims will be initialized.
// But
// (1) custom series should be considered. where other dims
// may be visited.
// (2) sometimes user need to calcualte bubble size or use visualMap
// on other dimensions besides coordSys needed.
// So, dims that is not used by system, should be shared in data store?
function getDimCount(
source: Source,
sysDims: CoordDimensionDefinitionLoose[],
dimsDef: DimensionDefinitionLoose[],
optDimCount?: number
): number {
// Note that the result dimCount should not small than columns count
// of data, otherwise `dataDimNameMap` checking will be incorrect.
let dimCount = Math.max(
source.dimensionsDetectedCount || 1,
sysDims.length,
dimsDef.length,
optDimCount || 0
);
each(sysDims, function (sysDimItem) {
let sysDimItemDimsDef;
if (isObject(sysDimItem) && (sysDimItemDimsDef = sysDimItem.dimsDef)) {
dimCount = Math.max(dimCount, sysDimItemDimsDef.length);
}
});
return dimCount;
}
function genCoordDimName(
name: DimensionName,
map: HashMap<unknown, DimensionName>,
fromZero: boolean
) {
const mapData = map.data;
if (fromZero || mapData.hasOwnProperty(name)) {
let i = 0;
while (mapData.hasOwnProperty(name + i)) {
i++;
}
name += i;
}
map.set(name, true);
return name;
}
相关信息
相关文章
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦