DataStore.js 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195
  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 { assert, clone, createHashMap, isFunction, keys, map, reduce } from 'zrender/lib/core/util';
  41. import { parseDataValue } from './helper/dataValueHelper';
  42. import { shouldRetrieveDataByName } from './Source';
  43. var UNDEFINED = 'undefined';
  44. /* global Float64Array, Int32Array, Uint32Array, Uint16Array */
  45. // Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is
  46. // different from the Ctor of typed array.
  47. export var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;
  48. export var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;
  49. export var CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array;
  50. export var CtorFloat64Array = typeof Float64Array === UNDEFINED ? Array : Float64Array;
  51. /**
  52. * Multi dimensional data store
  53. */
  54. var dataCtors = {
  55. 'float': CtorFloat64Array,
  56. 'int': CtorInt32Array,
  57. // Ordinal data type can be string or int
  58. 'ordinal': Array,
  59. 'number': Array,
  60. 'time': CtorFloat64Array
  61. };
  62. var defaultDimValueGetters;
  63. function getIndicesCtor(rawCount) {
  64. // The possible max value in this._indicies is always this._rawCount despite of filtering.
  65. return rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
  66. }
  67. ;
  68. function getInitialExtent() {
  69. return [Infinity, -Infinity];
  70. }
  71. ;
  72. function cloneChunk(originalChunk) {
  73. var Ctor = originalChunk.constructor; // Only shallow clone is enough when Array.
  74. return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);
  75. }
  76. function prepareStore(store, dimIdx, dimType, end, append) {
  77. var DataCtor = dataCtors[dimType || 'float'];
  78. if (append) {
  79. var oldStore = store[dimIdx];
  80. var oldLen = oldStore && oldStore.length;
  81. if (!(oldLen === end)) {
  82. var newStore = new DataCtor(end); // The cost of the copy is probably inconsiderable
  83. // within the initial chunkSize.
  84. for (var j = 0; j < oldLen; j++) {
  85. newStore[j] = oldStore[j];
  86. }
  87. store[dimIdx] = newStore;
  88. }
  89. } else {
  90. store[dimIdx] = new DataCtor(end);
  91. }
  92. }
  93. ;
  94. /**
  95. * Basically, DataStore API keep immutable.
  96. */
  97. var DataStore =
  98. /** @class */
  99. function () {
  100. function DataStore() {
  101. this._chunks = []; // It will not be calculated util needed.
  102. this._rawExtent = [];
  103. this._extent = [];
  104. this._count = 0;
  105. this._rawCount = 0;
  106. this._calcDimNameToIdx = createHashMap();
  107. }
  108. /**
  109. * Initialize from data
  110. */
  111. DataStore.prototype.initData = function (provider, inputDimensions, dimValueGetter) {
  112. if (process.env.NODE_ENV !== 'production') {
  113. assert(isFunction(provider.getItem) && isFunction(provider.count), 'Inavlid data provider.');
  114. }
  115. this._provider = provider; // Clear
  116. this._chunks = [];
  117. this._indices = null;
  118. this.getRawIndex = this._getRawIdxIdentity;
  119. var source = provider.getSource();
  120. var defaultGetter = this.defaultDimValueGetter = defaultDimValueGetters[source.sourceFormat]; // Default dim value getter
  121. this._dimValueGetter = dimValueGetter || defaultGetter; // Reset raw extent.
  122. this._rawExtent = [];
  123. var willRetrieveDataByName = shouldRetrieveDataByName(source);
  124. this._dimensions = map(inputDimensions, function (dim) {
  125. if (process.env.NODE_ENV !== 'production') {
  126. if (willRetrieveDataByName) {
  127. assert(dim.property != null);
  128. }
  129. }
  130. return {
  131. // Only pick these two props. Not leak other properties like orderMeta.
  132. type: dim.type,
  133. property: dim.property
  134. };
  135. });
  136. this._initDataFromProvider(0, provider.count());
  137. };
  138. DataStore.prototype.getProvider = function () {
  139. return this._provider;
  140. };
  141. /**
  142. * Caution: even when a `source` instance owned by a series, the created data store
  143. * may still be shared by different sereis (the source hash does not use all `source`
  144. * props, see `sourceManager`). In this case, the `source` props that are not used in
  145. * hash (like `source.dimensionDefine`) probably only belongs to a certain series and
  146. * thus should not be fetch here.
  147. */
  148. DataStore.prototype.getSource = function () {
  149. return this._provider.getSource();
  150. };
  151. /**
  152. * @caution Only used in dataStack.
  153. */
  154. DataStore.prototype.ensureCalculationDimension = function (dimName, type) {
  155. var calcDimNameToIdx = this._calcDimNameToIdx;
  156. var dimensions = this._dimensions;
  157. var calcDimIdx = calcDimNameToIdx.get(dimName);
  158. if (calcDimIdx != null) {
  159. if (dimensions[calcDimIdx].type === type) {
  160. return calcDimIdx;
  161. }
  162. } else {
  163. calcDimIdx = dimensions.length;
  164. }
  165. dimensions[calcDimIdx] = {
  166. type: type
  167. };
  168. calcDimNameToIdx.set(dimName, calcDimIdx);
  169. this._chunks[calcDimIdx] = new dataCtors[type || 'float'](this._rawCount);
  170. this._rawExtent[calcDimIdx] = getInitialExtent();
  171. return calcDimIdx;
  172. };
  173. DataStore.prototype.collectOrdinalMeta = function (dimIdx, ordinalMeta) {
  174. var chunk = this._chunks[dimIdx];
  175. var dim = this._dimensions[dimIdx];
  176. var rawExtents = this._rawExtent;
  177. var offset = dim.ordinalOffset || 0;
  178. var len = chunk.length;
  179. if (offset === 0) {
  180. // We need to reset the rawExtent if collect is from start.
  181. // Because this dimension may be guessed as number and calcuating a wrong extent.
  182. rawExtents[dimIdx] = getInitialExtent();
  183. }
  184. var dimRawExtent = rawExtents[dimIdx]; // Parse from previous data offset. len may be changed after appendData
  185. for (var i = offset; i < len; i++) {
  186. var val = chunk[i] = ordinalMeta.parseAndCollect(chunk[i]);
  187. dimRawExtent[0] = Math.min(val, dimRawExtent[0]);
  188. dimRawExtent[1] = Math.max(val, dimRawExtent[1]);
  189. }
  190. dim.ordinalMeta = ordinalMeta;
  191. dim.ordinalOffset = len;
  192. dim.type = 'ordinal'; // Force to be ordinal
  193. };
  194. DataStore.prototype.getOrdinalMeta = function (dimIdx) {
  195. var dimInfo = this._dimensions[dimIdx];
  196. var ordinalMeta = dimInfo.ordinalMeta;
  197. return ordinalMeta;
  198. };
  199. DataStore.prototype.getDimensionProperty = function (dimIndex) {
  200. var item = this._dimensions[dimIndex];
  201. return item && item.property;
  202. };
  203. /**
  204. * Caution: Can be only called on raw data (before `this._indices` created).
  205. */
  206. DataStore.prototype.appendData = function (data) {
  207. if (process.env.NODE_ENV !== 'production') {
  208. assert(!this._indices, 'appendData can only be called on raw data.');
  209. }
  210. var provider = this._provider;
  211. var start = this.count();
  212. provider.appendData(data);
  213. var end = provider.count();
  214. if (!provider.persistent) {
  215. end += start;
  216. }
  217. if (start < end) {
  218. this._initDataFromProvider(start, end, true);
  219. }
  220. return [start, end];
  221. };
  222. DataStore.prototype.appendValues = function (values, minFillLen) {
  223. var chunks = this._chunks;
  224. var dimensions = this._dimensions;
  225. var dimLen = dimensions.length;
  226. var rawExtent = this._rawExtent;
  227. var start = this.count();
  228. var end = start + Math.max(values.length, minFillLen || 0);
  229. for (var i = 0; i < dimLen; i++) {
  230. var dim = dimensions[i];
  231. prepareStore(chunks, i, dim.type, end, true);
  232. }
  233. var emptyDataItem = [];
  234. for (var idx = start; idx < end; idx++) {
  235. var sourceIdx = idx - start; // Store the data by dimensions
  236. for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {
  237. var dim = dimensions[dimIdx];
  238. var val = defaultDimValueGetters.arrayRows.call(this, values[sourceIdx] || emptyDataItem, dim.property, sourceIdx, dimIdx);
  239. chunks[dimIdx][idx] = val;
  240. var dimRawExtent = rawExtent[dimIdx];
  241. val < dimRawExtent[0] && (dimRawExtent[0] = val);
  242. val > dimRawExtent[1] && (dimRawExtent[1] = val);
  243. }
  244. }
  245. this._rawCount = this._count = end;
  246. return {
  247. start: start,
  248. end: end
  249. };
  250. };
  251. DataStore.prototype._initDataFromProvider = function (start, end, append) {
  252. var provider = this._provider;
  253. var chunks = this._chunks;
  254. var dimensions = this._dimensions;
  255. var dimLen = dimensions.length;
  256. var rawExtent = this._rawExtent;
  257. var dimNames = map(dimensions, function (dim) {
  258. return dim.property;
  259. });
  260. for (var i = 0; i < dimLen; i++) {
  261. var dim = dimensions[i];
  262. if (!rawExtent[i]) {
  263. rawExtent[i] = getInitialExtent();
  264. }
  265. prepareStore(chunks, i, dim.type, end, append);
  266. }
  267. if (provider.fillStorage) {
  268. provider.fillStorage(start, end, chunks, rawExtent);
  269. } else {
  270. var dataItem = [];
  271. for (var idx = start; idx < end; idx++) {
  272. // NOTICE: Try not to write things into dataItem
  273. dataItem = provider.getItem(idx, dataItem); // Each data item is value
  274. // [1, 2]
  275. // 2
  276. // Bar chart, line chart which uses category axis
  277. // only gives the 'y' value. 'x' value is the indices of category
  278. // Use a tempValue to normalize the value to be a (x, y) value
  279. // Store the data by dimensions
  280. for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {
  281. var dimStorage = chunks[dimIdx]; // PENDING NULL is empty or zero
  282. var val = this._dimValueGetter(dataItem, dimNames[dimIdx], idx, dimIdx);
  283. dimStorage[idx] = val;
  284. var dimRawExtent = rawExtent[dimIdx];
  285. val < dimRawExtent[0] && (dimRawExtent[0] = val);
  286. val > dimRawExtent[1] && (dimRawExtent[1] = val);
  287. }
  288. }
  289. }
  290. if (!provider.persistent && provider.clean) {
  291. // Clean unused data if data source is typed array.
  292. provider.clean();
  293. }
  294. this._rawCount = this._count = end; // Reset data extent
  295. this._extent = [];
  296. };
  297. DataStore.prototype.count = function () {
  298. return this._count;
  299. };
  300. /**
  301. * Get value. Return NaN if idx is out of range.
  302. */
  303. DataStore.prototype.get = function (dim, idx) {
  304. if (!(idx >= 0 && idx < this._count)) {
  305. return NaN;
  306. }
  307. var dimStore = this._chunks[dim];
  308. return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;
  309. };
  310. DataStore.prototype.getValues = function (dimensions, idx) {
  311. var values = [];
  312. var dimArr = [];
  313. if (idx == null) {
  314. idx = dimensions; // TODO get all from store?
  315. dimensions = []; // All dimensions
  316. for (var i = 0; i < this._dimensions.length; i++) {
  317. dimArr.push(i);
  318. }
  319. } else {
  320. dimArr = dimensions;
  321. }
  322. for (var i = 0, len = dimArr.length; i < len; i++) {
  323. values.push(this.get(dimArr[i], idx));
  324. }
  325. return values;
  326. };
  327. /**
  328. * @param dim concrete dim
  329. */
  330. DataStore.prototype.getByRawIndex = function (dim, rawIdx) {
  331. if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
  332. return NaN;
  333. }
  334. var dimStore = this._chunks[dim];
  335. return dimStore ? dimStore[rawIdx] : NaN;
  336. };
  337. /**
  338. * Get sum of data in one dimension
  339. */
  340. DataStore.prototype.getSum = function (dim) {
  341. var dimData = this._chunks[dim];
  342. var sum = 0;
  343. if (dimData) {
  344. for (var i = 0, len = this.count(); i < len; i++) {
  345. var value = this.get(dim, i);
  346. if (!isNaN(value)) {
  347. sum += value;
  348. }
  349. }
  350. }
  351. return sum;
  352. };
  353. /**
  354. * Get median of data in one dimension
  355. */
  356. DataStore.prototype.getMedian = function (dim) {
  357. var dimDataArray = []; // map all data of one dimension
  358. this.each([dim], function (val) {
  359. if (!isNaN(val)) {
  360. dimDataArray.push(val);
  361. }
  362. }); // TODO
  363. // Use quick select?
  364. var sortedDimDataArray = dimDataArray.sort(function (a, b) {
  365. return a - b;
  366. });
  367. var len = this.count(); // calculate median
  368. return len === 0 ? 0 : len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;
  369. };
  370. /**
  371. * Retreive the index with given raw data index
  372. */
  373. DataStore.prototype.indexOfRawIndex = function (rawIndex) {
  374. if (rawIndex >= this._rawCount || rawIndex < 0) {
  375. return -1;
  376. }
  377. if (!this._indices) {
  378. return rawIndex;
  379. } // Indices are ascending
  380. var indices = this._indices; // If rawIndex === dataIndex
  381. var rawDataIndex = indices[rawIndex];
  382. if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
  383. return rawIndex;
  384. }
  385. var left = 0;
  386. var right = this._count - 1;
  387. while (left <= right) {
  388. var mid = (left + right) / 2 | 0;
  389. if (indices[mid] < rawIndex) {
  390. left = mid + 1;
  391. } else if (indices[mid] > rawIndex) {
  392. right = mid - 1;
  393. } else {
  394. return mid;
  395. }
  396. }
  397. return -1;
  398. };
  399. /**
  400. * Retreive the index of nearest value
  401. * @param dim
  402. * @param value
  403. * @param [maxDistance=Infinity]
  404. * @return If and only if multiple indices has
  405. * the same value, they are put to the result.
  406. */
  407. DataStore.prototype.indicesOfNearest = function (dim, value, maxDistance) {
  408. var chunks = this._chunks;
  409. var dimData = chunks[dim];
  410. var nearestIndices = [];
  411. if (!dimData) {
  412. return nearestIndices;
  413. }
  414. if (maxDistance == null) {
  415. maxDistance = Infinity;
  416. }
  417. var minDist = Infinity;
  418. var minDiff = -1;
  419. var nearestIndicesLen = 0; // Check the test case of `test/ut/spec/data/SeriesData.js`.
  420. for (var i = 0, len = this.count(); i < len; i++) {
  421. var dataIndex = this.getRawIndex(i);
  422. var diff = value - dimData[dataIndex];
  423. var dist = Math.abs(diff);
  424. if (dist <= maxDistance) {
  425. // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,
  426. // we'd better not push both of them to `nearestIndices`, otherwise it is easy to
  427. // get more than one item in `nearestIndices` (more specifically, in `tooltip`).
  428. // So we chose the one that `diff >= 0` in this csae.
  429. // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them
  430. // should be push to `nearestIndices`.
  431. if (dist < minDist || dist === minDist && diff >= 0 && minDiff < 0) {
  432. minDist = dist;
  433. minDiff = diff;
  434. nearestIndicesLen = 0;
  435. }
  436. if (diff === minDiff) {
  437. nearestIndices[nearestIndicesLen++] = i;
  438. }
  439. }
  440. }
  441. nearestIndices.length = nearestIndicesLen;
  442. return nearestIndices;
  443. };
  444. DataStore.prototype.getIndices = function () {
  445. var newIndices;
  446. var indices = this._indices;
  447. if (indices) {
  448. var Ctor = indices.constructor;
  449. var thisCount = this._count; // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.
  450. if (Ctor === Array) {
  451. newIndices = new Ctor(thisCount);
  452. for (var i = 0; i < thisCount; i++) {
  453. newIndices[i] = indices[i];
  454. }
  455. } else {
  456. newIndices = new Ctor(indices.buffer, 0, thisCount);
  457. }
  458. } else {
  459. var Ctor = getIndicesCtor(this._rawCount);
  460. newIndices = new Ctor(this.count());
  461. for (var i = 0; i < newIndices.length; i++) {
  462. newIndices[i] = i;
  463. }
  464. }
  465. return newIndices;
  466. };
  467. /**
  468. * Data filter.
  469. */
  470. DataStore.prototype.filter = function (dims, cb) {
  471. if (!this._count) {
  472. return this;
  473. }
  474. var newStore = this.clone();
  475. var count = newStore.count();
  476. var Ctor = getIndicesCtor(newStore._rawCount);
  477. var newIndices = new Ctor(count);
  478. var value = [];
  479. var dimSize = dims.length;
  480. var offset = 0;
  481. var dim0 = dims[0];
  482. var chunks = newStore._chunks;
  483. for (var i = 0; i < count; i++) {
  484. var keep = void 0;
  485. var rawIdx = newStore.getRawIndex(i); // Simple optimization
  486. if (dimSize === 0) {
  487. keep = cb(i);
  488. } else if (dimSize === 1) {
  489. var val = chunks[dim0][rawIdx];
  490. keep = cb(val, i);
  491. } else {
  492. var k = 0;
  493. for (; k < dimSize; k++) {
  494. value[k] = chunks[dims[k]][rawIdx];
  495. }
  496. value[k] = i;
  497. keep = cb.apply(null, value);
  498. }
  499. if (keep) {
  500. newIndices[offset++] = rawIdx;
  501. }
  502. } // Set indices after filtered.
  503. if (offset < count) {
  504. newStore._indices = newIndices;
  505. }
  506. newStore._count = offset; // Reset data extent
  507. newStore._extent = [];
  508. newStore._updateGetRawIdx();
  509. return newStore;
  510. };
  511. /**
  512. * Select data in range. (For optimization of filter)
  513. * (Manually inline code, support 5 million data filtering in data zoom.)
  514. */
  515. DataStore.prototype.selectRange = function (range) {
  516. var newStore = this.clone();
  517. var len = newStore._count;
  518. if (!len) {
  519. return this;
  520. }
  521. var dims = keys(range);
  522. var dimSize = dims.length;
  523. if (!dimSize) {
  524. return this;
  525. }
  526. var originalCount = newStore.count();
  527. var Ctor = getIndicesCtor(newStore._rawCount);
  528. var newIndices = new Ctor(originalCount);
  529. var offset = 0;
  530. var dim0 = dims[0];
  531. var min = range[dim0][0];
  532. var max = range[dim0][1];
  533. var storeArr = newStore._chunks;
  534. var quickFinished = false;
  535. if (!newStore._indices) {
  536. // Extreme optimization for common case. About 2x faster in chrome.
  537. var idx = 0;
  538. if (dimSize === 1) {
  539. var dimStorage = storeArr[dims[0]];
  540. for (var i = 0; i < len; i++) {
  541. var val = dimStorage[i]; // NaN will not be filtered. Consider the case, in line chart, empty
  542. // value indicates the line should be broken. But for the case like
  543. // scatter plot, a data item with empty value will not be rendered,
  544. // but the axis extent may be effected if some other dim of the data
  545. // item has value. Fortunately it is not a significant negative effect.
  546. if (val >= min && val <= max || isNaN(val)) {
  547. newIndices[offset++] = idx;
  548. }
  549. idx++;
  550. }
  551. quickFinished = true;
  552. } else if (dimSize === 2) {
  553. var dimStorage = storeArr[dims[0]];
  554. var dimStorage2 = storeArr[dims[1]];
  555. var min2 = range[dims[1]][0];
  556. var max2 = range[dims[1]][1];
  557. for (var i = 0; i < len; i++) {
  558. var val = dimStorage[i];
  559. var val2 = dimStorage2[i]; // Do not filter NaN, see comment above.
  560. if ((val >= min && val <= max || isNaN(val)) && (val2 >= min2 && val2 <= max2 || isNaN(val2))) {
  561. newIndices[offset++] = idx;
  562. }
  563. idx++;
  564. }
  565. quickFinished = true;
  566. }
  567. }
  568. if (!quickFinished) {
  569. if (dimSize === 1) {
  570. for (var i = 0; i < originalCount; i++) {
  571. var rawIndex = newStore.getRawIndex(i);
  572. var val = storeArr[dims[0]][rawIndex]; // Do not filter NaN, see comment above.
  573. if (val >= min && val <= max || isNaN(val)) {
  574. newIndices[offset++] = rawIndex;
  575. }
  576. }
  577. } else {
  578. for (var i = 0; i < originalCount; i++) {
  579. var keep = true;
  580. var rawIndex = newStore.getRawIndex(i);
  581. for (var k = 0; k < dimSize; k++) {
  582. var dimk = dims[k];
  583. var val = storeArr[dimk][rawIndex]; // Do not filter NaN, see comment above.
  584. if (val < range[dimk][0] || val > range[dimk][1]) {
  585. keep = false;
  586. }
  587. }
  588. if (keep) {
  589. newIndices[offset++] = newStore.getRawIndex(i);
  590. }
  591. }
  592. }
  593. } // Set indices after filtered.
  594. if (offset < originalCount) {
  595. newStore._indices = newIndices;
  596. }
  597. newStore._count = offset; // Reset data extent
  598. newStore._extent = [];
  599. newStore._updateGetRawIdx();
  600. return newStore;
  601. }; // /**
  602. // * Data mapping to a plain array
  603. // */
  604. // mapArray(dims: DimensionIndex[], cb: MapArrayCb): any[] {
  605. // const result: any[] = [];
  606. // this.each(dims, function () {
  607. // result.push(cb && (cb as MapArrayCb).apply(null, arguments));
  608. // });
  609. // return result;
  610. // }
  611. /**
  612. * Data mapping to a new List with given dimensions
  613. */
  614. DataStore.prototype.map = function (dims, cb) {
  615. // TODO only clone picked chunks.
  616. var target = this.clone(dims);
  617. this._updateDims(target, dims, cb);
  618. return target;
  619. };
  620. /**
  621. * @caution Danger!! Only used in dataStack.
  622. */
  623. DataStore.prototype.modify = function (dims, cb) {
  624. this._updateDims(this, dims, cb);
  625. };
  626. DataStore.prototype._updateDims = function (target, dims, cb) {
  627. var targetChunks = target._chunks;
  628. var tmpRetValue = [];
  629. var dimSize = dims.length;
  630. var dataCount = target.count();
  631. var values = [];
  632. var rawExtent = target._rawExtent;
  633. for (var i = 0; i < dims.length; i++) {
  634. rawExtent[dims[i]] = getInitialExtent();
  635. }
  636. for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {
  637. var rawIndex = target.getRawIndex(dataIndex);
  638. for (var k = 0; k < dimSize; k++) {
  639. values[k] = targetChunks[dims[k]][rawIndex];
  640. }
  641. values[dimSize] = dataIndex;
  642. var retValue = cb && cb.apply(null, values);
  643. if (retValue != null) {
  644. // a number or string (in oridinal dimension)?
  645. if (typeof retValue !== 'object') {
  646. tmpRetValue[0] = retValue;
  647. retValue = tmpRetValue;
  648. }
  649. for (var i = 0; i < retValue.length; i++) {
  650. var dim = dims[i];
  651. var val = retValue[i];
  652. var rawExtentOnDim = rawExtent[dim];
  653. var dimStore = targetChunks[dim];
  654. if (dimStore) {
  655. dimStore[rawIndex] = val;
  656. }
  657. if (val < rawExtentOnDim[0]) {
  658. rawExtentOnDim[0] = val;
  659. }
  660. if (val > rawExtentOnDim[1]) {
  661. rawExtentOnDim[1] = val;
  662. }
  663. }
  664. }
  665. }
  666. };
  667. /**
  668. * Large data down sampling using largest-triangle-three-buckets
  669. * @param {string} valueDimension
  670. * @param {number} targetCount
  671. */
  672. DataStore.prototype.lttbDownSample = function (valueDimension, rate) {
  673. var target = this.clone([valueDimension], true);
  674. var targetStorage = target._chunks;
  675. var dimStore = targetStorage[valueDimension];
  676. var len = this.count();
  677. var sampledIndex = 0;
  678. var frameSize = Math.floor(1 / rate);
  679. var currentRawIndex = this.getRawIndex(0);
  680. var maxArea;
  681. var area;
  682. var nextRawIndex;
  683. var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize) + 2); // First frame use the first data.
  684. newIndices[sampledIndex++] = currentRawIndex;
  685. for (var i = 1; i < len - 1; i += frameSize) {
  686. var nextFrameStart = Math.min(i + frameSize, len - 1);
  687. var nextFrameEnd = Math.min(i + frameSize * 2, len);
  688. var avgX = (nextFrameEnd + nextFrameStart) / 2;
  689. var avgY = 0;
  690. for (var idx = nextFrameStart; idx < nextFrameEnd; idx++) {
  691. var rawIndex = this.getRawIndex(idx);
  692. var y = dimStore[rawIndex];
  693. if (isNaN(y)) {
  694. continue;
  695. }
  696. avgY += y;
  697. }
  698. avgY /= nextFrameEnd - nextFrameStart;
  699. var frameStart = i;
  700. var frameEnd = Math.min(i + frameSize, len);
  701. var pointAX = i - 1;
  702. var pointAY = dimStore[currentRawIndex];
  703. maxArea = -1;
  704. nextRawIndex = frameStart; // Find a point from current frame that construct a triangel with largest area with previous selected point
  705. // And the average of next frame.
  706. for (var idx = frameStart; idx < frameEnd; idx++) {
  707. var rawIndex = this.getRawIndex(idx);
  708. var y = dimStore[rawIndex];
  709. if (isNaN(y)) {
  710. continue;
  711. } // Calculate triangle area over three buckets
  712. area = Math.abs((pointAX - avgX) * (y - pointAY) - (pointAX - idx) * (avgY - pointAY));
  713. if (area > maxArea) {
  714. maxArea = area;
  715. nextRawIndex = rawIndex; // Next a is this b
  716. }
  717. }
  718. newIndices[sampledIndex++] = nextRawIndex;
  719. currentRawIndex = nextRawIndex; // This a is the next a (chosen b)
  720. } // First frame use the last data.
  721. newIndices[sampledIndex++] = this.getRawIndex(len - 1);
  722. target._count = sampledIndex;
  723. target._indices = newIndices;
  724. target.getRawIndex = this._getRawIdx;
  725. return target;
  726. };
  727. /**
  728. * Large data down sampling on given dimension
  729. * @param sampleIndex Sample index for name and id
  730. */
  731. DataStore.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) {
  732. var target = this.clone([dimension], true);
  733. var targetStorage = target._chunks;
  734. var frameValues = [];
  735. var frameSize = Math.floor(1 / rate);
  736. var dimStore = targetStorage[dimension];
  737. var len = this.count();
  738. var rawExtentOnDim = target._rawExtent[dimension] = getInitialExtent();
  739. var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize));
  740. var offset = 0;
  741. for (var i = 0; i < len; i += frameSize) {
  742. // Last frame
  743. if (frameSize > len - i) {
  744. frameSize = len - i;
  745. frameValues.length = frameSize;
  746. }
  747. for (var k = 0; k < frameSize; k++) {
  748. var dataIdx = this.getRawIndex(i + k);
  749. frameValues[k] = dimStore[dataIdx];
  750. }
  751. var value = sampleValue(frameValues);
  752. var sampleFrameIdx = this.getRawIndex(Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)); // Only write value on the filtered data
  753. dimStore[sampleFrameIdx] = value;
  754. if (value < rawExtentOnDim[0]) {
  755. rawExtentOnDim[0] = value;
  756. }
  757. if (value > rawExtentOnDim[1]) {
  758. rawExtentOnDim[1] = value;
  759. }
  760. newIndices[offset++] = sampleFrameIdx;
  761. }
  762. target._count = offset;
  763. target._indices = newIndices;
  764. target._updateGetRawIdx();
  765. return target;
  766. };
  767. /**
  768. * Data iteration
  769. * @param ctx default this
  770. * @example
  771. * list.each('x', function (x, idx) {});
  772. * list.each(['x', 'y'], function (x, y, idx) {});
  773. * list.each(function (idx) {})
  774. */
  775. DataStore.prototype.each = function (dims, cb) {
  776. if (!this._count) {
  777. return;
  778. }
  779. var dimSize = dims.length;
  780. var chunks = this._chunks;
  781. for (var i = 0, len = this.count(); i < len; i++) {
  782. var rawIdx = this.getRawIndex(i); // Simple optimization
  783. switch (dimSize) {
  784. case 0:
  785. cb(i);
  786. break;
  787. case 1:
  788. cb(chunks[dims[0]][rawIdx], i);
  789. break;
  790. case 2:
  791. cb(chunks[dims[0]][rawIdx], chunks[dims[1]][rawIdx], i);
  792. break;
  793. default:
  794. var k = 0;
  795. var value = [];
  796. for (; k < dimSize; k++) {
  797. value[k] = chunks[dims[k]][rawIdx];
  798. } // Index
  799. value[k] = i;
  800. cb.apply(null, value);
  801. }
  802. }
  803. };
  804. /**
  805. * Get extent of data in one dimension
  806. */
  807. DataStore.prototype.getDataExtent = function (dim) {
  808. // Make sure use concrete dim as cache name.
  809. var dimData = this._chunks[dim];
  810. var initialExtent = getInitialExtent();
  811. if (!dimData) {
  812. return initialExtent;
  813. } // Make more strict checkings to ensure hitting cache.
  814. var currEnd = this.count(); // Consider the most cases when using data zoom, `getDataExtent`
  815. // happened before filtering. We cache raw extent, which is not
  816. // necessary to be cleared and recalculated when restore data.
  817. var useRaw = !this._indices;
  818. var dimExtent;
  819. if (useRaw) {
  820. return this._rawExtent[dim].slice();
  821. }
  822. dimExtent = this._extent[dim];
  823. if (dimExtent) {
  824. return dimExtent.slice();
  825. }
  826. dimExtent = initialExtent;
  827. var min = dimExtent[0];
  828. var max = dimExtent[1];
  829. for (var i = 0; i < currEnd; i++) {
  830. var rawIdx = this.getRawIndex(i);
  831. var value = dimData[rawIdx];
  832. value < min && (min = value);
  833. value > max && (max = value);
  834. }
  835. dimExtent = [min, max];
  836. this._extent[dim] = dimExtent;
  837. return dimExtent;
  838. };
  839. /**
  840. * Get raw data item
  841. */
  842. DataStore.prototype.getRawDataItem = function (idx) {
  843. var rawIdx = this.getRawIndex(idx);
  844. if (!this._provider.persistent) {
  845. var val = [];
  846. var chunks = this._chunks;
  847. for (var i = 0; i < chunks.length; i++) {
  848. val.push(chunks[i][rawIdx]);
  849. }
  850. return val;
  851. } else {
  852. return this._provider.getItem(rawIdx);
  853. }
  854. };
  855. /**
  856. * Clone shallow.
  857. *
  858. * @param clonedDims Determine which dims to clone. Will share the data if not specified.
  859. */
  860. DataStore.prototype.clone = function (clonedDims, ignoreIndices) {
  861. var target = new DataStore();
  862. var chunks = this._chunks;
  863. var clonedDimsMap = clonedDims && reduce(clonedDims, function (obj, dimIdx) {
  864. obj[dimIdx] = true;
  865. return obj;
  866. }, {});
  867. if (clonedDimsMap) {
  868. for (var i = 0; i < chunks.length; i++) {
  869. // Not clone if dim is not picked.
  870. target._chunks[i] = !clonedDimsMap[i] ? chunks[i] : cloneChunk(chunks[i]);
  871. }
  872. } else {
  873. target._chunks = chunks;
  874. }
  875. this._copyCommonProps(target);
  876. if (!ignoreIndices) {
  877. target._indices = this._cloneIndices();
  878. }
  879. target._updateGetRawIdx();
  880. return target;
  881. };
  882. DataStore.prototype._copyCommonProps = function (target) {
  883. target._count = this._count;
  884. target._rawCount = this._rawCount;
  885. target._provider = this._provider;
  886. target._dimensions = this._dimensions;
  887. target._extent = clone(this._extent);
  888. target._rawExtent = clone(this._rawExtent);
  889. };
  890. DataStore.prototype._cloneIndices = function () {
  891. if (this._indices) {
  892. var Ctor = this._indices.constructor;
  893. var indices = void 0;
  894. if (Ctor === Array) {
  895. var thisCount = this._indices.length;
  896. indices = new Ctor(thisCount);
  897. for (var i = 0; i < thisCount; i++) {
  898. indices[i] = this._indices[i];
  899. }
  900. } else {
  901. indices = new Ctor(this._indices);
  902. }
  903. return indices;
  904. }
  905. return null;
  906. };
  907. DataStore.prototype._getRawIdxIdentity = function (idx) {
  908. return idx;
  909. };
  910. DataStore.prototype._getRawIdx = function (idx) {
  911. if (idx < this._count && idx >= 0) {
  912. return this._indices[idx];
  913. }
  914. return -1;
  915. };
  916. DataStore.prototype._updateGetRawIdx = function () {
  917. this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity;
  918. };
  919. DataStore.internalField = function () {
  920. function getDimValueSimply(dataItem, property, dataIndex, dimIndex) {
  921. return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]);
  922. }
  923. defaultDimValueGetters = {
  924. arrayRows: getDimValueSimply,
  925. objectRows: function (dataItem, property, dataIndex, dimIndex) {
  926. return parseDataValue(dataItem[property], this._dimensions[dimIndex]);
  927. },
  928. keyedColumns: getDimValueSimply,
  929. original: function (dataItem, property, dataIndex, dimIndex) {
  930. // Performance sensitive, do not use modelUtil.getDataItemValue.
  931. // If dataItem is an plain object with no value field, the let `value`
  932. // will be assigned with the object, but it will be tread correctly
  933. // in the `convertValue`.
  934. var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
  935. return parseDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array.
  936. : value, this._dimensions[dimIndex]);
  937. },
  938. typedArray: function (dataItem, property, dataIndex, dimIndex) {
  939. return dataItem[dimIndex];
  940. }
  941. };
  942. }();
  943. return DataStore;
  944. }();
  945. export default DataStore;