DataStore.js 33 KB

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