DataStore.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008
  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. DataStore.prototype.getIndices = function () {
  418. var newIndices;
  419. var indices = this._indices;
  420. if (indices) {
  421. var Ctor = indices.constructor;
  422. var thisCount = this._count;
  423. // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.
  424. if (Ctor === Array) {
  425. newIndices = new Ctor(thisCount);
  426. for (var i = 0; i < thisCount; i++) {
  427. newIndices[i] = indices[i];
  428. }
  429. } else {
  430. newIndices = new Ctor(indices.buffer, 0, thisCount);
  431. }
  432. } else {
  433. var Ctor = getIndicesCtor(this._rawCount);
  434. newIndices = new Ctor(this.count());
  435. for (var i = 0; i < newIndices.length; i++) {
  436. newIndices[i] = i;
  437. }
  438. }
  439. return newIndices;
  440. };
  441. /**
  442. * Data filter.
  443. */
  444. DataStore.prototype.filter = function (dims, cb) {
  445. if (!this._count) {
  446. return this;
  447. }
  448. var newStore = this.clone();
  449. var count = newStore.count();
  450. var Ctor = getIndicesCtor(newStore._rawCount);
  451. var newIndices = new Ctor(count);
  452. var value = [];
  453. var dimSize = dims.length;
  454. var offset = 0;
  455. var dim0 = dims[0];
  456. var chunks = newStore._chunks;
  457. for (var i = 0; i < count; i++) {
  458. var keep = void 0;
  459. var rawIdx = newStore.getRawIndex(i);
  460. // Simple optimization
  461. if (dimSize === 0) {
  462. keep = cb(i);
  463. } else if (dimSize === 1) {
  464. var val = chunks[dim0][rawIdx];
  465. keep = cb(val, i);
  466. } else {
  467. var k = 0;
  468. for (; k < dimSize; k++) {
  469. value[k] = chunks[dims[k]][rawIdx];
  470. }
  471. value[k] = i;
  472. keep = cb.apply(null, value);
  473. }
  474. if (keep) {
  475. newIndices[offset++] = rawIdx;
  476. }
  477. }
  478. // Set indices after filtered.
  479. if (offset < count) {
  480. newStore._indices = newIndices;
  481. }
  482. newStore._count = offset;
  483. // Reset data extent
  484. newStore._extent = [];
  485. newStore._updateGetRawIdx();
  486. return newStore;
  487. };
  488. /**
  489. * Select data in range. (For optimization of filter)
  490. * (Manually inline code, support 5 million data filtering in data zoom.)
  491. */
  492. DataStore.prototype.selectRange = function (range) {
  493. var newStore = this.clone();
  494. var len = newStore._count;
  495. if (!len) {
  496. return this;
  497. }
  498. var dims = keys(range);
  499. var dimSize = dims.length;
  500. if (!dimSize) {
  501. return this;
  502. }
  503. var originalCount = newStore.count();
  504. var Ctor = getIndicesCtor(newStore._rawCount);
  505. var newIndices = new Ctor(originalCount);
  506. var offset = 0;
  507. var dim0 = dims[0];
  508. var min = range[dim0][0];
  509. var max = range[dim0][1];
  510. var storeArr = newStore._chunks;
  511. var quickFinished = false;
  512. if (!newStore._indices) {
  513. // Extreme optimization for common case. About 2x faster in chrome.
  514. var idx = 0;
  515. if (dimSize === 1) {
  516. var dimStorage = storeArr[dims[0]];
  517. for (var i = 0; i < len; i++) {
  518. var val = dimStorage[i];
  519. // NaN will not be filtered. Consider the case, in line chart, empty
  520. // value indicates the line should be broken. But for the case like
  521. // scatter plot, a data item with empty value will not be rendered,
  522. // but the axis extent may be effected if some other dim of the data
  523. // item has value. Fortunately it is not a significant negative effect.
  524. if (val >= min && val <= max || isNaN(val)) {
  525. newIndices[offset++] = idx;
  526. }
  527. idx++;
  528. }
  529. quickFinished = true;
  530. } else if (dimSize === 2) {
  531. var dimStorage = storeArr[dims[0]];
  532. var dimStorage2 = storeArr[dims[1]];
  533. var min2 = range[dims[1]][0];
  534. var max2 = range[dims[1]][1];
  535. for (var i = 0; i < len; i++) {
  536. var val = dimStorage[i];
  537. var val2 = dimStorage2[i];
  538. // Do not filter NaN, see comment above.
  539. if ((val >= min && val <= max || isNaN(val)) && (val2 >= min2 && val2 <= max2 || isNaN(val2))) {
  540. newIndices[offset++] = idx;
  541. }
  542. idx++;
  543. }
  544. quickFinished = true;
  545. }
  546. }
  547. if (!quickFinished) {
  548. if (dimSize === 1) {
  549. for (var i = 0; i < originalCount; i++) {
  550. var rawIndex = newStore.getRawIndex(i);
  551. var val = storeArr[dims[0]][rawIndex];
  552. // Do not filter NaN, see comment above.
  553. if (val >= min && val <= max || isNaN(val)) {
  554. newIndices[offset++] = rawIndex;
  555. }
  556. }
  557. } else {
  558. for (var i = 0; i < originalCount; i++) {
  559. var keep = true;
  560. var rawIndex = newStore.getRawIndex(i);
  561. for (var k = 0; k < dimSize; k++) {
  562. var dimk = dims[k];
  563. var val = storeArr[dimk][rawIndex];
  564. // Do not filter NaN, see comment above.
  565. if (val < range[dimk][0] || val > range[dimk][1]) {
  566. keep = false;
  567. }
  568. }
  569. if (keep) {
  570. newIndices[offset++] = newStore.getRawIndex(i);
  571. }
  572. }
  573. }
  574. }
  575. // Set indices after filtered.
  576. if (offset < originalCount) {
  577. newStore._indices = newIndices;
  578. }
  579. newStore._count = offset;
  580. // Reset data extent
  581. newStore._extent = [];
  582. newStore._updateGetRawIdx();
  583. return newStore;
  584. };
  585. // /**
  586. // * Data mapping to a plain array
  587. // */
  588. // mapArray(dims: DimensionIndex[], cb: MapArrayCb): any[] {
  589. // const result: any[] = [];
  590. // this.each(dims, function () {
  591. // result.push(cb && (cb as MapArrayCb).apply(null, arguments));
  592. // });
  593. // return result;
  594. // }
  595. /**
  596. * Data mapping to a new List with given dimensions
  597. */
  598. DataStore.prototype.map = function (dims, cb) {
  599. // TODO only clone picked chunks.
  600. var target = this.clone(dims);
  601. this._updateDims(target, dims, cb);
  602. return target;
  603. };
  604. /**
  605. * @caution Danger!! Only used in dataStack.
  606. */
  607. DataStore.prototype.modify = function (dims, cb) {
  608. this._updateDims(this, dims, cb);
  609. };
  610. DataStore.prototype._updateDims = function (target, dims, cb) {
  611. var targetChunks = target._chunks;
  612. var tmpRetValue = [];
  613. var dimSize = dims.length;
  614. var dataCount = target.count();
  615. var values = [];
  616. var rawExtent = target._rawExtent;
  617. for (var i = 0; i < dims.length; i++) {
  618. rawExtent[dims[i]] = getInitialExtent();
  619. }
  620. for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {
  621. var rawIndex = target.getRawIndex(dataIndex);
  622. for (var k = 0; k < dimSize; k++) {
  623. values[k] = targetChunks[dims[k]][rawIndex];
  624. }
  625. values[dimSize] = dataIndex;
  626. var retValue = cb && cb.apply(null, values);
  627. if (retValue != null) {
  628. // a number or string (in oridinal dimension)?
  629. if (typeof retValue !== 'object') {
  630. tmpRetValue[0] = retValue;
  631. retValue = tmpRetValue;
  632. }
  633. for (var i = 0; i < retValue.length; i++) {
  634. var dim = dims[i];
  635. var val = retValue[i];
  636. var rawExtentOnDim = rawExtent[dim];
  637. var dimStore = targetChunks[dim];
  638. if (dimStore) {
  639. dimStore[rawIndex] = val;
  640. }
  641. if (val < rawExtentOnDim[0]) {
  642. rawExtentOnDim[0] = val;
  643. }
  644. if (val > rawExtentOnDim[1]) {
  645. rawExtentOnDim[1] = val;
  646. }
  647. }
  648. }
  649. }
  650. };
  651. /**
  652. * Large data down sampling using largest-triangle-three-buckets
  653. * @param {string} valueDimension
  654. * @param {number} targetCount
  655. */
  656. DataStore.prototype.lttbDownSample = function (valueDimension, rate) {
  657. var target = this.clone([valueDimension], true);
  658. var targetStorage = target._chunks;
  659. var dimStore = targetStorage[valueDimension];
  660. var len = this.count();
  661. var sampledIndex = 0;
  662. var frameSize = Math.floor(1 / rate);
  663. var currentRawIndex = this.getRawIndex(0);
  664. var maxArea;
  665. var area;
  666. var nextRawIndex;
  667. var newIndices = new (getIndicesCtor(this._rawCount))(Math.min((Math.ceil(len / frameSize) + 2) * 2, len));
  668. // First frame use the first data.
  669. newIndices[sampledIndex++] = currentRawIndex;
  670. for (var i = 1; i < len - 1; i += frameSize) {
  671. var nextFrameStart = Math.min(i + frameSize, len - 1);
  672. var nextFrameEnd = Math.min(i + frameSize * 2, len);
  673. var avgX = (nextFrameEnd + nextFrameStart) / 2;
  674. var avgY = 0;
  675. for (var idx = nextFrameStart; idx < nextFrameEnd; idx++) {
  676. var rawIndex = this.getRawIndex(idx);
  677. var y = dimStore[rawIndex];
  678. if (isNaN(y)) {
  679. continue;
  680. }
  681. avgY += y;
  682. }
  683. avgY /= nextFrameEnd - nextFrameStart;
  684. var frameStart = i;
  685. var frameEnd = Math.min(i + frameSize, len);
  686. var pointAX = i - 1;
  687. var pointAY = dimStore[currentRawIndex];
  688. maxArea = -1;
  689. nextRawIndex = frameStart;
  690. var firstNaNIndex = -1;
  691. var countNaN = 0;
  692. // Find a point from current frame that construct a triangle with largest area with previous selected point
  693. // And the average of next frame.
  694. for (var idx = frameStart; idx < frameEnd; idx++) {
  695. var rawIndex = this.getRawIndex(idx);
  696. var y = dimStore[rawIndex];
  697. if (isNaN(y)) {
  698. countNaN++;
  699. if (firstNaNIndex < 0) {
  700. firstNaNIndex = rawIndex;
  701. }
  702. continue;
  703. }
  704. // Calculate triangle area over three buckets
  705. area = Math.abs((pointAX - avgX) * (y - pointAY) - (pointAX - idx) * (avgY - pointAY));
  706. if (area > maxArea) {
  707. maxArea = area;
  708. nextRawIndex = rawIndex; // Next a is this b
  709. }
  710. }
  711. if (countNaN > 0 && countNaN < frameEnd - frameStart) {
  712. // Append first NaN point in every bucket.
  713. // It is necessary to ensure the correct order of indices.
  714. newIndices[sampledIndex++] = Math.min(firstNaNIndex, nextRawIndex);
  715. nextRawIndex = Math.max(firstNaNIndex, nextRawIndex);
  716. }
  717. newIndices[sampledIndex++] = nextRawIndex;
  718. currentRawIndex = nextRawIndex; // This a is the next a (chosen b)
  719. }
  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 using min-max
  729. * @param {string} valueDimension
  730. * @param {number} rate
  731. */
  732. DataStore.prototype.minmaxDownSample = function (valueDimension, rate) {
  733. var target = this.clone([valueDimension], true);
  734. var targetStorage = target._chunks;
  735. var frameSize = Math.floor(1 / rate);
  736. var dimStore = targetStorage[valueDimension];
  737. var len = this.count();
  738. // Each frame results in 2 data points, one for min and one for max
  739. var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize) * 2);
  740. var offset = 0;
  741. for (var i = 0; i < len; i += frameSize) {
  742. var minIndex = i;
  743. var minValue = dimStore[this.getRawIndex(minIndex)];
  744. var maxIndex = i;
  745. var maxValue = dimStore[this.getRawIndex(maxIndex)];
  746. var thisFrameSize = frameSize;
  747. // Handle final smaller frame
  748. if (i + frameSize > len) {
  749. thisFrameSize = len - i;
  750. }
  751. // Determine min and max within the current frame
  752. for (var k = 0; k < thisFrameSize; k++) {
  753. var rawIndex = this.getRawIndex(i + k);
  754. var value = dimStore[rawIndex];
  755. if (value < minValue) {
  756. minValue = value;
  757. minIndex = i + k;
  758. }
  759. if (value > maxValue) {
  760. maxValue = value;
  761. maxIndex = i + k;
  762. }
  763. }
  764. var rawMinIndex = this.getRawIndex(minIndex);
  765. var rawMaxIndex = this.getRawIndex(maxIndex);
  766. // Set the order of the min and max values, based on their ordering in the frame
  767. if (minIndex < maxIndex) {
  768. newIndices[offset++] = rawMinIndex;
  769. newIndices[offset++] = rawMaxIndex;
  770. } else {
  771. newIndices[offset++] = rawMaxIndex;
  772. newIndices[offset++] = rawMinIndex;
  773. }
  774. }
  775. target._count = offset;
  776. target._indices = newIndices;
  777. target._updateGetRawIdx();
  778. return target;
  779. };
  780. /**
  781. * Large data down sampling on given dimension
  782. * @param sampleIndex Sample index for name and id
  783. */
  784. DataStore.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) {
  785. var target = this.clone([dimension], true);
  786. var targetStorage = target._chunks;
  787. var frameValues = [];
  788. var frameSize = Math.floor(1 / rate);
  789. var dimStore = targetStorage[dimension];
  790. var len = this.count();
  791. var rawExtentOnDim = target._rawExtent[dimension] = getInitialExtent();
  792. var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize));
  793. var offset = 0;
  794. for (var i = 0; i < len; i += frameSize) {
  795. // Last frame
  796. if (frameSize > len - i) {
  797. frameSize = len - i;
  798. frameValues.length = frameSize;
  799. }
  800. for (var k = 0; k < frameSize; k++) {
  801. var dataIdx = this.getRawIndex(i + k);
  802. frameValues[k] = dimStore[dataIdx];
  803. }
  804. var value = sampleValue(frameValues);
  805. var sampleFrameIdx = this.getRawIndex(Math.min(i + sampleIndex(frameValues, value) || 0, len - 1));
  806. // Only write value on the filtered data
  807. dimStore[sampleFrameIdx] = value;
  808. if (value < rawExtentOnDim[0]) {
  809. rawExtentOnDim[0] = value;
  810. }
  811. if (value > rawExtentOnDim[1]) {
  812. rawExtentOnDim[1] = value;
  813. }
  814. newIndices[offset++] = sampleFrameIdx;
  815. }
  816. target._count = offset;
  817. target._indices = newIndices;
  818. target._updateGetRawIdx();
  819. return target;
  820. };
  821. /**
  822. * Data iteration
  823. * @param ctx default this
  824. * @example
  825. * list.each('x', function (x, idx) {});
  826. * list.each(['x', 'y'], function (x, y, idx) {});
  827. * list.each(function (idx) {})
  828. */
  829. DataStore.prototype.each = function (dims, cb) {
  830. if (!this._count) {
  831. return;
  832. }
  833. var dimSize = dims.length;
  834. var chunks = this._chunks;
  835. for (var i = 0, len = this.count(); i < len; i++) {
  836. var rawIdx = this.getRawIndex(i);
  837. // Simple optimization
  838. switch (dimSize) {
  839. case 0:
  840. cb(i);
  841. break;
  842. case 1:
  843. cb(chunks[dims[0]][rawIdx], i);
  844. break;
  845. case 2:
  846. cb(chunks[dims[0]][rawIdx], chunks[dims[1]][rawIdx], i);
  847. break;
  848. default:
  849. var k = 0;
  850. var value = [];
  851. for (; k < dimSize; k++) {
  852. value[k] = chunks[dims[k]][rawIdx];
  853. }
  854. // Index
  855. value[k] = i;
  856. cb.apply(null, value);
  857. }
  858. }
  859. };
  860. /**
  861. * Get extent of data in one dimension
  862. */
  863. DataStore.prototype.getDataExtent = function (dim) {
  864. // Make sure use concrete dim as cache name.
  865. var dimData = this._chunks[dim];
  866. var initialExtent = getInitialExtent();
  867. if (!dimData) {
  868. return initialExtent;
  869. }
  870. // Make more strict checkings to ensure hitting cache.
  871. var currEnd = this.count();
  872. // Consider the most cases when using data zoom, `getDataExtent`
  873. // happened before filtering. We cache raw extent, which is not
  874. // necessary to be cleared and recalculated when restore data.
  875. var useRaw = !this._indices;
  876. var dimExtent;
  877. if (useRaw) {
  878. return this._rawExtent[dim].slice();
  879. }
  880. dimExtent = this._extent[dim];
  881. if (dimExtent) {
  882. return dimExtent.slice();
  883. }
  884. dimExtent = initialExtent;
  885. var min = dimExtent[0];
  886. var max = dimExtent[1];
  887. for (var i = 0; i < currEnd; i++) {
  888. var rawIdx = this.getRawIndex(i);
  889. var value = dimData[rawIdx];
  890. value < min && (min = value);
  891. value > max && (max = value);
  892. }
  893. dimExtent = [min, max];
  894. this._extent[dim] = dimExtent;
  895. return dimExtent;
  896. };
  897. /**
  898. * Get raw data item
  899. */
  900. DataStore.prototype.getRawDataItem = function (idx) {
  901. var rawIdx = this.getRawIndex(idx);
  902. if (!this._provider.persistent) {
  903. var val = [];
  904. var chunks = this._chunks;
  905. for (var i = 0; i < chunks.length; i++) {
  906. val.push(chunks[i][rawIdx]);
  907. }
  908. return val;
  909. } else {
  910. return this._provider.getItem(rawIdx);
  911. }
  912. };
  913. /**
  914. * Clone shallow.
  915. *
  916. * @param clonedDims Determine which dims to clone. Will share the data if not specified.
  917. */
  918. DataStore.prototype.clone = function (clonedDims, ignoreIndices) {
  919. var target = new DataStore();
  920. var chunks = this._chunks;
  921. var clonedDimsMap = clonedDims && reduce(clonedDims, function (obj, dimIdx) {
  922. obj[dimIdx] = true;
  923. return obj;
  924. }, {});
  925. if (clonedDimsMap) {
  926. for (var i = 0; i < chunks.length; i++) {
  927. // Not clone if dim is not picked.
  928. target._chunks[i] = !clonedDimsMap[i] ? chunks[i] : cloneChunk(chunks[i]);
  929. }
  930. } else {
  931. target._chunks = chunks;
  932. }
  933. this._copyCommonProps(target);
  934. if (!ignoreIndices) {
  935. target._indices = this._cloneIndices();
  936. }
  937. target._updateGetRawIdx();
  938. return target;
  939. };
  940. DataStore.prototype._copyCommonProps = function (target) {
  941. target._count = this._count;
  942. target._rawCount = this._rawCount;
  943. target._provider = this._provider;
  944. target._dimensions = this._dimensions;
  945. target._extent = clone(this._extent);
  946. target._rawExtent = clone(this._rawExtent);
  947. };
  948. DataStore.prototype._cloneIndices = function () {
  949. if (this._indices) {
  950. var Ctor = this._indices.constructor;
  951. var indices = void 0;
  952. if (Ctor === Array) {
  953. var thisCount = this._indices.length;
  954. indices = new Ctor(thisCount);
  955. for (var i = 0; i < thisCount; i++) {
  956. indices[i] = this._indices[i];
  957. }
  958. } else {
  959. indices = new Ctor(this._indices);
  960. }
  961. return indices;
  962. }
  963. return null;
  964. };
  965. DataStore.prototype._getRawIdxIdentity = function (idx) {
  966. return idx;
  967. };
  968. DataStore.prototype._getRawIdx = function (idx) {
  969. if (idx < this._count && idx >= 0) {
  970. return this._indices[idx];
  971. }
  972. return -1;
  973. };
  974. DataStore.prototype._updateGetRawIdx = function () {
  975. this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity;
  976. };
  977. DataStore.internalField = function () {
  978. function getDimValueSimply(dataItem, property, dataIndex, dimIndex) {
  979. return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]);
  980. }
  981. defaultDimValueGetters = {
  982. arrayRows: getDimValueSimply,
  983. objectRows: function (dataItem, property, dataIndex, dimIndex) {
  984. return parseDataValue(dataItem[property], this._dimensions[dimIndex]);
  985. },
  986. keyedColumns: getDimValueSimply,
  987. original: function (dataItem, property, dataIndex, dimIndex) {
  988. // Performance sensitive, do not use modelUtil.getDataItemValue.
  989. // If dataItem is an plain object with no value field, the let `value`
  990. // will be assigned with the object, but it will be tread correctly
  991. // in the `convertValue`.
  992. var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
  993. return parseDataValue(value instanceof Array ? value[dimIndex]
  994. // If value is a single number or something else not array.
  995. : value, this._dimensions[dimIndex]);
  996. },
  997. typedArray: function (dataItem, property, dataIndex, dimIndex) {
  998. return dataItem[dimIndex];
  999. }
  1000. };
  1001. }();
  1002. return DataStore;
  1003. }();
  1004. export default DataStore;