createDimensions.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { VISUAL_DIMENSIONS } from '../../util/types.js';
  41. import SeriesDimensionDefine from '../SeriesDimensionDefine.js';
  42. import { createHashMap, defaults, each, extend, isObject, isString } from 'zrender/lib/core/util.js';
  43. import { createSourceFromSeriesDataOption, isSourceInstance } from '../Source.js';
  44. import { CtorInt32Array } from '../DataStore.js';
  45. import { normalizeToArray } from '../../util/model.js';
  46. import { BE_ORDINAL, guessOrdinal } from './sourceHelper.js';
  47. import { createDimNameMap, ensureSourceDimNameMap, SeriesDataSchema, shouldOmitUnusedDimensions } from './SeriesDataSchema.js';
  48. /**
  49. * For outside usage compat (like echarts-gl are using it).
  50. */
  51. export function createDimensions(source, opt) {
  52. return prepareSeriesDataSchema(source, opt).dimensions;
  53. }
  54. /**
  55. * This method builds the relationship between:
  56. * + "what the coord sys or series requires (see `coordDimensions`)",
  57. * + "what the user defines (in `encode` and `dimensions`, see `opt.dimensionsDefine` and `opt.encodeDefine`)"
  58. * + "what the data source provids (see `source`)".
  59. *
  60. * Some guess strategy will be adapted if user does not define something.
  61. * If no 'value' dimension specified, the first no-named dimension will be
  62. * named as 'value'.
  63. *
  64. * @return The results are always sorted by `storeDimIndex` asc.
  65. */
  66. export default function prepareSeriesDataSchema(
  67. // TODO: TYPE completeDimensions type
  68. source, opt) {
  69. if (!isSourceInstance(source)) {
  70. source = createSourceFromSeriesDataOption(source);
  71. }
  72. opt = opt || {};
  73. var sysDims = opt.coordDimensions || [];
  74. var dimsDef = opt.dimensionsDefine || source.dimensionsDefine || [];
  75. var coordDimNameMap = createHashMap();
  76. var resultList = [];
  77. var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimensionsCount);
  78. // Try to ignore unused dimensions if sharing a high dimension datastore
  79. // 30 is an experience value.
  80. var omitUnusedDimensions = opt.canOmitUnusedDimensions && shouldOmitUnusedDimensions(dimCount);
  81. var isUsingSourceDimensionsDef = dimsDef === source.dimensionsDefine;
  82. var dataDimNameMap = isUsingSourceDimensionsDef ? ensureSourceDimNameMap(source) : createDimNameMap(dimsDef);
  83. var encodeDef = opt.encodeDefine;
  84. if (!encodeDef && opt.encodeDefaulter) {
  85. encodeDef = opt.encodeDefaulter(source, dimCount);
  86. }
  87. var encodeDefMap = createHashMap(encodeDef);
  88. var indicesMap = new CtorInt32Array(dimCount);
  89. for (var i = 0; i < indicesMap.length; i++) {
  90. indicesMap[i] = -1;
  91. }
  92. function getResultItem(dimIdx) {
  93. var idx = indicesMap[dimIdx];
  94. if (idx < 0) {
  95. var dimDefItemRaw = dimsDef[dimIdx];
  96. var dimDefItem = isObject(dimDefItemRaw) ? dimDefItemRaw : {
  97. name: dimDefItemRaw
  98. };
  99. var resultItem = new SeriesDimensionDefine();
  100. var userDimName = dimDefItem.name;
  101. if (userDimName != null && dataDimNameMap.get(userDimName) != null) {
  102. // Only if `series.dimensions` is defined in option
  103. // displayName, will be set, and dimension will be displayed vertically in
  104. // tooltip by default.
  105. resultItem.name = resultItem.displayName = userDimName;
  106. }
  107. dimDefItem.type != null && (resultItem.type = dimDefItem.type);
  108. dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName);
  109. var newIdx = resultList.length;
  110. indicesMap[dimIdx] = newIdx;
  111. resultItem.storeDimIndex = dimIdx;
  112. resultList.push(resultItem);
  113. return resultItem;
  114. }
  115. return resultList[idx];
  116. }
  117. if (!omitUnusedDimensions) {
  118. for (var i = 0; i < dimCount; i++) {
  119. getResultItem(i);
  120. }
  121. }
  122. // Set `coordDim` and `coordDimIndex` by `encodeDefMap` and normalize `encodeDefMap`.
  123. encodeDefMap.each(function (dataDimsRaw, coordDim) {
  124. var dataDims = normalizeToArray(dataDimsRaw).slice();
  125. // Note: It is allowed that `dataDims.length` is `0`, e.g., options is
  126. // `{encode: {x: -1, y: 1}}`. Should not filter anything in
  127. // this case.
  128. if (dataDims.length === 1 && !isString(dataDims[0]) && dataDims[0] < 0) {
  129. encodeDefMap.set(coordDim, false);
  130. return;
  131. }
  132. var validDataDims = encodeDefMap.set(coordDim, []);
  133. each(dataDims, function (resultDimIdxOrName, idx) {
  134. // The input resultDimIdx can be dim name or index.
  135. var resultDimIdx = isString(resultDimIdxOrName) ? dataDimNameMap.get(resultDimIdxOrName) : resultDimIdxOrName;
  136. if (resultDimIdx != null && resultDimIdx < dimCount) {
  137. validDataDims[idx] = resultDimIdx;
  138. applyDim(getResultItem(resultDimIdx), coordDim, idx);
  139. }
  140. });
  141. });
  142. // Apply templates and default order from `sysDims`.
  143. var availDimIdx = 0;
  144. each(sysDims, function (sysDimItemRaw) {
  145. var coordDim;
  146. var sysDimItemDimsDef;
  147. var sysDimItemOtherDims;
  148. var sysDimItem;
  149. if (isString(sysDimItemRaw)) {
  150. coordDim = sysDimItemRaw;
  151. sysDimItem = {};
  152. } else {
  153. sysDimItem = sysDimItemRaw;
  154. coordDim = sysDimItem.name;
  155. var ordinalMeta = sysDimItem.ordinalMeta;
  156. sysDimItem.ordinalMeta = null;
  157. sysDimItem = extend({}, sysDimItem);
  158. sysDimItem.ordinalMeta = ordinalMeta;
  159. // `coordDimIndex` should not be set directly.
  160. sysDimItemDimsDef = sysDimItem.dimsDef;
  161. sysDimItemOtherDims = sysDimItem.otherDims;
  162. sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex = sysDimItem.dimsDef = sysDimItem.otherDims = null;
  163. }
  164. var dataDims = encodeDefMap.get(coordDim);
  165. // negative resultDimIdx means no need to mapping.
  166. if (dataDims === false) {
  167. return;
  168. }
  169. dataDims = normalizeToArray(dataDims);
  170. // dimensions provides default dim sequences.
  171. if (!dataDims.length) {
  172. for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {
  173. while (availDimIdx < dimCount && getResultItem(availDimIdx).coordDim != null) {
  174. availDimIdx++;
  175. }
  176. availDimIdx < dimCount && dataDims.push(availDimIdx++);
  177. }
  178. }
  179. // Apply templates.
  180. each(dataDims, function (resultDimIdx, coordDimIndex) {
  181. var resultItem = getResultItem(resultDimIdx);
  182. // Coordinate system has a higher priority on dim type than source.
  183. if (isUsingSourceDimensionsDef && sysDimItem.type != null) {
  184. resultItem.type = sysDimItem.type;
  185. }
  186. applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex);
  187. if (resultItem.name == null && sysDimItemDimsDef) {
  188. var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex];
  189. !isObject(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = {
  190. name: sysDimItemDimsDefItem
  191. });
  192. resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name;
  193. resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip;
  194. }
  195. // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}}
  196. sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims);
  197. });
  198. });
  199. function applyDim(resultItem, coordDim, coordDimIndex) {
  200. if (VISUAL_DIMENSIONS.get(coordDim) != null) {
  201. resultItem.otherDims[coordDim] = coordDimIndex;
  202. } else {
  203. resultItem.coordDim = coordDim;
  204. resultItem.coordDimIndex = coordDimIndex;
  205. coordDimNameMap.set(coordDim, true);
  206. }
  207. }
  208. // Make sure the first extra dim is 'value'.
  209. var generateCoord = opt.generateCoord;
  210. var generateCoordCount = opt.generateCoordCount;
  211. var fromZero = generateCoordCount != null;
  212. generateCoordCount = generateCoord ? generateCoordCount || 1 : 0;
  213. var extra = generateCoord || 'value';
  214. function ifNoNameFillWithCoordName(resultItem) {
  215. if (resultItem.name == null) {
  216. // Duplication will be removed in the next step.
  217. resultItem.name = resultItem.coordDim;
  218. }
  219. }
  220. // Set dim `name` and other `coordDim` and other props.
  221. if (!omitUnusedDimensions) {
  222. for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {
  223. var resultItem = getResultItem(resultDimIdx);
  224. var coordDim = resultItem.coordDim;
  225. if (coordDim == null) {
  226. // TODO no need to generate coordDim for isExtraCoord?
  227. resultItem.coordDim = genCoordDimName(extra, coordDimNameMap, fromZero);
  228. resultItem.coordDimIndex = 0;
  229. // Series specified generateCoord is using out.
  230. if (!generateCoord || generateCoordCount <= 0) {
  231. resultItem.isExtraCoord = true;
  232. }
  233. generateCoordCount--;
  234. }
  235. ifNoNameFillWithCoordName(resultItem);
  236. if (resultItem.type == null && (guessOrdinal(source, resultDimIdx) === BE_ORDINAL.Must
  237. // Consider the case:
  238. // {
  239. // dataset: {source: [
  240. // ['2001', 123],
  241. // ['2002', 456],
  242. // ...
  243. // ['The others', 987],
  244. // ]},
  245. // series: {type: 'pie'}
  246. // }
  247. // The first column should better be treated as a "ordinal" although it
  248. // might not be detected as an "ordinal" by `guessOrdinal`.
  249. || resultItem.isExtraCoord && (resultItem.otherDims.itemName != null || resultItem.otherDims.seriesName != null))) {
  250. resultItem.type = 'ordinal';
  251. }
  252. }
  253. } else {
  254. each(resultList, function (resultItem) {
  255. // PENDING: guessOrdinal or let user specify type: 'ordinal' manually?
  256. ifNoNameFillWithCoordName(resultItem);
  257. });
  258. // Sort dimensions: there are some rule that use the last dim as label,
  259. // and for some latter travel process easier.
  260. resultList.sort(function (item0, item1) {
  261. return item0.storeDimIndex - item1.storeDimIndex;
  262. });
  263. }
  264. removeDuplication(resultList);
  265. return new SeriesDataSchema({
  266. source: source,
  267. dimensions: resultList,
  268. fullDimensionCount: dimCount,
  269. dimensionOmitted: omitUnusedDimensions
  270. });
  271. }
  272. function removeDuplication(result) {
  273. var duplicationMap = createHashMap();
  274. for (var i = 0; i < result.length; i++) {
  275. var dim = result[i];
  276. var dimOriginalName = dim.name;
  277. var count = duplicationMap.get(dimOriginalName) || 0;
  278. if (count > 0) {
  279. // Starts from 0.
  280. dim.name = dimOriginalName + (count - 1);
  281. }
  282. count++;
  283. duplicationMap.set(dimOriginalName, count);
  284. }
  285. }
  286. // ??? TODO
  287. // Originally detect dimCount by data[0]. Should we
  288. // optimize it to only by sysDims and dimensions and encode.
  289. // So only necessary dims will be initialized.
  290. // But
  291. // (1) custom series should be considered. where other dims
  292. // may be visited.
  293. // (2) sometimes user need to calculate bubble size or use visualMap
  294. // on other dimensions besides coordSys needed.
  295. // So, dims that is not used by system, should be shared in data store?
  296. function getDimCount(source, sysDims, dimsDef, optDimCount) {
  297. // Note that the result dimCount should not small than columns count
  298. // of data, otherwise `dataDimNameMap` checking will be incorrect.
  299. var dimCount = Math.max(source.dimensionsDetectedCount || 1, sysDims.length, dimsDef.length, optDimCount || 0);
  300. each(sysDims, function (sysDimItem) {
  301. var sysDimItemDimsDef;
  302. if (isObject(sysDimItem) && (sysDimItemDimsDef = sysDimItem.dimsDef)) {
  303. dimCount = Math.max(dimCount, sysDimItemDimsDef.length);
  304. }
  305. });
  306. return dimCount;
  307. }
  308. function genCoordDimName(name, map, fromZero) {
  309. if (fromZero || map.hasKey(name)) {
  310. var i = 0;
  311. while (map.hasKey(name + i)) {
  312. i++;
  313. }
  314. name += i;
  315. }
  316. map.set(name, true);
  317. return name;
  318. }