322 lines
13 KiB
JavaScript
322 lines
13 KiB
JavaScript
|
|
||
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
|
||
|
/**
|
||
|
* AUTO-GENERATED FILE. DO NOT MODIFY.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* 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 { VISUAL_DIMENSIONS } from '../../util/types.js';
|
||
|
import SeriesDimensionDefine from '../SeriesDimensionDefine.js';
|
||
|
import { createHashMap, defaults, each, extend, isObject, isString } from 'zrender/lib/core/util.js';
|
||
|
import { createSourceFromSeriesDataOption, isSourceInstance } from '../Source.js';
|
||
|
import { CtorInt32Array } from '../DataStore.js';
|
||
|
import { normalizeToArray } from '../../util/model.js';
|
||
|
import { BE_ORDINAL, guessOrdinal } from './sourceHelper.js';
|
||
|
import { createDimNameMap, ensureSourceDimNameMap, SeriesDataSchema, shouldOmitUnusedDimensions } from './SeriesDataSchema.js';
|
||
|
/**
|
||
|
* For outside usage compat (like echarts-gl are using it).
|
||
|
*/
|
||
|
export function createDimensions(source, opt) {
|
||
|
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, opt) {
|
||
|
if (!isSourceInstance(source)) {
|
||
|
source = createSourceFromSeriesDataOption(source);
|
||
|
}
|
||
|
opt = opt || {};
|
||
|
var sysDims = opt.coordDimensions || [];
|
||
|
var dimsDef = opt.dimensionsDefine || source.dimensionsDefine || [];
|
||
|
var coordDimNameMap = createHashMap();
|
||
|
var resultList = [];
|
||
|
var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimensionsCount);
|
||
|
// Try to ignore unused dimensions if sharing a high dimension datastore
|
||
|
// 30 is an experience value.
|
||
|
var omitUnusedDimensions = opt.canOmitUnusedDimensions && shouldOmitUnusedDimensions(dimCount);
|
||
|
var isUsingSourceDimensionsDef = dimsDef === source.dimensionsDefine;
|
||
|
var dataDimNameMap = isUsingSourceDimensionsDef ? ensureSourceDimNameMap(source) : createDimNameMap(dimsDef);
|
||
|
var encodeDef = opt.encodeDefine;
|
||
|
if (!encodeDef && opt.encodeDefaulter) {
|
||
|
encodeDef = opt.encodeDefaulter(source, dimCount);
|
||
|
}
|
||
|
var encodeDefMap = createHashMap(encodeDef);
|
||
|
var indicesMap = new CtorInt32Array(dimCount);
|
||
|
for (var i = 0; i < indicesMap.length; i++) {
|
||
|
indicesMap[i] = -1;
|
||
|
}
|
||
|
function getResultItem(dimIdx) {
|
||
|
var idx = indicesMap[dimIdx];
|
||
|
if (idx < 0) {
|
||
|
var dimDefItemRaw = dimsDef[dimIdx];
|
||
|
var dimDefItem = isObject(dimDefItemRaw) ? dimDefItemRaw : {
|
||
|
name: dimDefItemRaw
|
||
|
};
|
||
|
var resultItem = new SeriesDimensionDefine();
|
||
|
var 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 displayed vertically in
|
||
|
// tooltip by default.
|
||
|
resultItem.name = resultItem.displayName = userDimName;
|
||
|
}
|
||
|
dimDefItem.type != null && (resultItem.type = dimDefItem.type);
|
||
|
dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName);
|
||
|
var newIdx = resultList.length;
|
||
|
indicesMap[dimIdx] = newIdx;
|
||
|
resultItem.storeDimIndex = dimIdx;
|
||
|
resultList.push(resultItem);
|
||
|
return resultItem;
|
||
|
}
|
||
|
return resultList[idx];
|
||
|
}
|
||
|
if (!omitUnusedDimensions) {
|
||
|
for (var i = 0; i < dimCount; i++) {
|
||
|
getResultItem(i);
|
||
|
}
|
||
|
}
|
||
|
// Set `coordDim` and `coordDimIndex` by `encodeDefMap` and normalize `encodeDefMap`.
|
||
|
encodeDefMap.each(function (dataDimsRaw, coordDim) {
|
||
|
var dataDims = normalizeToArray(dataDimsRaw).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;
|
||
|
}
|
||
|
var validDataDims = encodeDefMap.set(coordDim, []);
|
||
|
each(dataDims, function (resultDimIdxOrName, idx) {
|
||
|
// The input resultDimIdx can be dim name or index.
|
||
|
var resultDimIdx = isString(resultDimIdxOrName) ? dataDimNameMap.get(resultDimIdxOrName) : resultDimIdxOrName;
|
||
|
if (resultDimIdx != null && resultDimIdx < dimCount) {
|
||
|
validDataDims[idx] = resultDimIdx;
|
||
|
applyDim(getResultItem(resultDimIdx), coordDim, idx);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
// Apply templates and default order from `sysDims`.
|
||
|
var availDimIdx = 0;
|
||
|
each(sysDims, function (sysDimItemRaw) {
|
||
|
var coordDim;
|
||
|
var sysDimItemDimsDef;
|
||
|
var sysDimItemOtherDims;
|
||
|
var sysDimItem;
|
||
|
if (isString(sysDimItemRaw)) {
|
||
|
coordDim = sysDimItemRaw;
|
||
|
sysDimItem = {};
|
||
|
} else {
|
||
|
sysDimItem = sysDimItemRaw;
|
||
|
coordDim = sysDimItem.name;
|
||
|
var 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;
|
||
|
}
|
||
|
var 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 (var 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) {
|
||
|
var 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) {
|
||
|
var 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, coordDim, coordDimIndex) {
|
||
|
if (VISUAL_DIMENSIONS.get(coordDim) != null) {
|
||
|
resultItem.otherDims[coordDim] = coordDimIndex;
|
||
|
} else {
|
||
|
resultItem.coordDim = coordDim;
|
||
|
resultItem.coordDimIndex = coordDimIndex;
|
||
|
coordDimNameMap.set(coordDim, true);
|
||
|
}
|
||
|
}
|
||
|
// Make sure the first extra dim is 'value'.
|
||
|
var generateCoord = opt.generateCoord;
|
||
|
var generateCoordCount = opt.generateCoordCount;
|
||
|
var fromZero = generateCoordCount != null;
|
||
|
generateCoordCount = generateCoord ? generateCoordCount || 1 : 0;
|
||
|
var extra = generateCoord || 'value';
|
||
|
function ifNoNameFillWithCoordName(resultItem) {
|
||
|
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 (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {
|
||
|
var resultItem = getResultItem(resultDimIdx);
|
||
|
var 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 column should better be treated as a "ordinal" although it
|
||
|
// might not be detected as an "ordinal" by `guessOrdinal`.
|
||
|
|| resultItem.isExtraCoord && (resultItem.otherDims.itemName != null || resultItem.otherDims.seriesName != null))) {
|
||
|
resultItem.type = 'ordinal';
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
each(resultList, function (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(function (item0, item1) {
|
||
|
return item0.storeDimIndex - item1.storeDimIndex;
|
||
|
});
|
||
|
}
|
||
|
removeDuplication(resultList);
|
||
|
return new SeriesDataSchema({
|
||
|
source: source,
|
||
|
dimensions: resultList,
|
||
|
fullDimensionCount: dimCount,
|
||
|
dimensionOmitted: omitUnusedDimensions
|
||
|
});
|
||
|
}
|
||
|
function removeDuplication(result) {
|
||
|
var duplicationMap = createHashMap();
|
||
|
for (var i = 0; i < result.length; i++) {
|
||
|
var dim = result[i];
|
||
|
var dimOriginalName = dim.name;
|
||
|
var 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 calculate 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, sysDims, dimsDef, optDimCount) {
|
||
|
// Note that the result dimCount should not small than columns count
|
||
|
// of data, otherwise `dataDimNameMap` checking will be incorrect.
|
||
|
var dimCount = Math.max(source.dimensionsDetectedCount || 1, sysDims.length, dimsDef.length, optDimCount || 0);
|
||
|
each(sysDims, function (sysDimItem) {
|
||
|
var sysDimItemDimsDef;
|
||
|
if (isObject(sysDimItem) && (sysDimItemDimsDef = sysDimItem.dimsDef)) {
|
||
|
dimCount = Math.max(dimCount, sysDimItemDimsDef.length);
|
||
|
}
|
||
|
});
|
||
|
return dimCount;
|
||
|
}
|
||
|
function genCoordDimName(name, map, fromZero) {
|
||
|
if (fromZero || map.hasKey(name)) {
|
||
|
var i = 0;
|
||
|
while (map.hasKey(name + i)) {
|
||
|
i++;
|
||
|
}
|
||
|
name += i;
|
||
|
}
|
||
|
map.set(name, true);
|
||
|
return name;
|
||
|
}
|