DataStore.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  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 { UNDEFINED_STR } from '../util/types.js';
  42. import { parseDataValue, parseSanitizationFilter, passesSanitizationFilter } from './helper/dataValueHelper.js';
  43. import { shouldRetrieveDataByName } from './Source.js';
  44. import { initExtentForUnion } from '../util/model.js';
  45. import { asc } from '../util/number.js';
  46. /* global Float64Array, Int32Array, Uint32Array, Uint16Array */
  47. // Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is
  48. // different from the Ctor of typed array.
  49. export var CtorUint32Array = typeof Uint32Array === UNDEFINED_STR ? Array : Uint32Array;
  50. export var CtorUint16Array = typeof Uint16Array === UNDEFINED_STR ? Array : Uint16Array;
  51. export var CtorInt32Array = typeof Int32Array === UNDEFINED_STR ? Array : Int32Array;
  52. export var CtorFloat64Array = typeof Float64Array === UNDEFINED_STR ? Array : Float64Array;
  53. /**
  54. * Multi dimensional data store
  55. */
  56. var dataCtors = {
  57. 'float': CtorFloat64Array,
  58. 'int': CtorInt32Array,
  59. // Ordinal data type can be string or int
  60. 'ordinal': Array,
  61. 'number': Array,
  62. 'time': CtorFloat64Array
  63. };
  64. var defaultDimValueGetters;
  65. function getIndicesCtor(rawCount) {
  66. // The possible max value in this._indicies is always this._rawCount despite of filtering.
  67. return rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
  68. }
  69. ;
  70. function cloneChunk(originalChunk) {
  71. var Ctor = originalChunk.constructor;
  72. // Only shallow clone is enough when Array.
  73. return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);
  74. }
  75. function prepareStore(store, dimIdx, dimType, end, append) {
  76. var DataCtor = dataCtors[dimType || 'float'];
  77. if (append) {
  78. var oldStore = store[dimIdx];
  79. var oldLen = oldStore && oldStore.length;
  80. if (!(oldLen === end)) {
  81. var newStore = new DataCtor(end);
  82. // 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 = /** @class */function () {
  98. function DataStore() {
  99. this._chunks = [];
  100. // It will not be calculated until needed.
  101. this._rawExtent = [];
  102. // structure:
  103. // `const extentOnFilterOnDimension = this._extent[dim][extentFilterKey]`
  104. this._extent = [];
  105. // Count after filtered.
  106. this._count = 0;
  107. this._rawCount = 0;
  108. this._calcDimNameToIdx = createHashMap();
  109. }
  110. /**
  111. * Initialize from data
  112. */
  113. DataStore.prototype.initData = function (provider, inputDimensions, dimValueGetter) {
  114. if (process.env.NODE_ENV !== 'production') {
  115. assert(isFunction(provider.getItem) && isFunction(provider.count), 'Invalid data provider.');
  116. }
  117. this._provider = provider;
  118. // Clear
  119. this._chunks = [];
  120. this._indices = null;
  121. this.getRawIndex = this._getRawIdxIdentity;
  122. var source = provider.getSource();
  123. var defaultGetter = this.defaultDimValueGetter = defaultDimValueGetters[source.sourceFormat];
  124. // Default dim value getter
  125. this._dimValueGetter = dimValueGetter || defaultGetter;
  126. // Reset raw extent.
  127. this._rawExtent = [];
  128. var willRetrieveDataByName = shouldRetrieveDataByName(source);
  129. this._dimensions = map(inputDimensions, function (dim) {
  130. if (process.env.NODE_ENV !== 'production') {
  131. if (willRetrieveDataByName) {
  132. assert(dim.property != null);
  133. }
  134. }
  135. return {
  136. // Only pick these two props. Not leak other properties like orderMeta.
  137. type: dim.type,
  138. property: dim.property
  139. };
  140. });
  141. this._initDataFromProvider(0, provider.count());
  142. };
  143. DataStore.prototype.getProvider = function () {
  144. return this._provider;
  145. };
  146. /**
  147. * Caution: even when a `source` instance owned by a series, the created data store
  148. * may still be shared by different sereis (the source hash does not use all `source`
  149. * props, see `sourceManager`). In this case, the `source` props that are not used in
  150. * hash (like `source.dimensionDefine`) probably only belongs to a certain series and
  151. * thus should not be fetch here.
  152. */
  153. DataStore.prototype.getSource = function () {
  154. return this._provider.getSource();
  155. };
  156. /**
  157. * @caution Only used in dataStack.
  158. */
  159. DataStore.prototype.ensureCalculationDimension = function (dimName, type) {
  160. var calcDimNameToIdx = this._calcDimNameToIdx;
  161. var dimensions = this._dimensions;
  162. var calcDimIdx = calcDimNameToIdx.get(dimName);
  163. if (calcDimIdx != null) {
  164. if (dimensions[calcDimIdx].type === type) {
  165. return calcDimIdx;
  166. }
  167. } else {
  168. calcDimIdx = dimensions.length;
  169. }
  170. dimensions[calcDimIdx] = {
  171. type: type
  172. };
  173. calcDimNameToIdx.set(dimName, calcDimIdx);
  174. this._chunks[calcDimIdx] = new dataCtors[type || 'float'](this._rawCount);
  175. this._rawExtent[calcDimIdx] = initExtentForUnion();
  176. return calcDimIdx;
  177. };
  178. DataStore.prototype.collectOrdinalMeta = function (dimIdx, ordinalMeta) {
  179. var chunk = this._chunks[dimIdx];
  180. var dim = this._dimensions[dimIdx];
  181. var rawExtents = this._rawExtent;
  182. var offset = dim.ordinalOffset || 0;
  183. var len = chunk.length;
  184. if (offset === 0) {
  185. // We need to reset the rawExtent if collect is from start.
  186. // Because this dimension may be guessed as number and calcuating a wrong extent.
  187. rawExtents[dimIdx] = initExtentForUnion();
  188. }
  189. var dimRawExtent = rawExtents[dimIdx];
  190. // Parse from previous data offset. len may be changed after appendData
  191. for (var i = offset; i < len; i++) {
  192. // See also CATEGORY_AXIS_MODEL_DATA_IS_EMPTY_ARRAY.
  193. var val = chunk[i] = ordinalMeta.parseAndCollect(chunk[i]);
  194. if (!isNaN(val)) {
  195. dimRawExtent[0] = Math.min(val, dimRawExtent[0]);
  196. dimRawExtent[1] = Math.max(val, dimRawExtent[1]);
  197. }
  198. }
  199. dim.ordinalMeta = ordinalMeta;
  200. dim.ordinalOffset = len;
  201. dim.type = 'ordinal'; // Force to be ordinal
  202. };
  203. DataStore.prototype.getOrdinalMeta = function (dimIdx) {
  204. var dimInfo = this._dimensions[dimIdx];
  205. var ordinalMeta = dimInfo.ordinalMeta;
  206. return ordinalMeta;
  207. };
  208. DataStore.prototype.getDimensionProperty = function (dimIndex) {
  209. var item = this._dimensions[dimIndex];
  210. return item && item.property;
  211. };
  212. /**
  213. * Caution: Can be only called on raw data (before `this._indices` created).
  214. */
  215. DataStore.prototype.appendData = function (data) {
  216. if (process.env.NODE_ENV !== 'production') {
  217. assert(!this._indices, 'appendData can only be called on raw data.');
  218. }
  219. var provider = this._provider;
  220. var start = this.count();
  221. provider.appendData(data);
  222. var end = provider.count();
  223. if (!provider.persistent) {
  224. end += start;
  225. }
  226. if (start < end) {
  227. this._initDataFromProvider(start, end, true);
  228. }
  229. return [start, end];
  230. };
  231. DataStore.prototype.appendValues = function (values, minFillLen) {
  232. var chunks = this._chunks;
  233. var dimensions = this._dimensions;
  234. var dimLen = dimensions.length;
  235. var rawExtent = this._rawExtent;
  236. var start = this.count();
  237. var end = start + Math.max(values.length, minFillLen || 0);
  238. for (var i = 0; i < dimLen; i++) {
  239. var dim = dimensions[i];
  240. prepareStore(chunks, i, dim.type, end, true);
  241. }
  242. var emptyDataItem = [];
  243. for (var idx = start; idx < end; idx++) {
  244. var sourceIdx = idx - start;
  245. // Store the data by dimensions
  246. for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {
  247. var dim = dimensions[dimIdx];
  248. var val = defaultDimValueGetters.arrayRows.call(this, values[sourceIdx] || emptyDataItem, dim.property, sourceIdx, dimIdx);
  249. chunks[dimIdx][idx] = val;
  250. var dimRawExtent = rawExtent[dimIdx];
  251. val < dimRawExtent[0] && (dimRawExtent[0] = val);
  252. val > dimRawExtent[1] && (dimRawExtent[1] = val);
  253. }
  254. }
  255. this._rawCount = this._count = end;
  256. return {
  257. start: start,
  258. end: end
  259. };
  260. };
  261. DataStore.prototype._initDataFromProvider = function (start, end, append) {
  262. var provider = this._provider;
  263. var chunks = this._chunks;
  264. var dimensions = this._dimensions;
  265. var dimLen = dimensions.length;
  266. var rawExtent = this._rawExtent;
  267. var dimNames = map(dimensions, function (dim) {
  268. return dim.property;
  269. });
  270. for (var i = 0; i < dimLen; i++) {
  271. var dim = dimensions[i];
  272. if (!rawExtent[i]) {
  273. rawExtent[i] = initExtentForUnion();
  274. }
  275. prepareStore(chunks, i, dim.type, end, append);
  276. }
  277. if (provider.fillStorage) {
  278. provider.fillStorage(start, end, chunks, rawExtent);
  279. } else {
  280. var dataItem = [];
  281. for (var idx = start; idx < end; idx++) {
  282. // NOTICE: Try not to write things into dataItem
  283. dataItem = provider.getItem(idx, dataItem);
  284. // Each data item is value
  285. // [1, 2]
  286. // 2
  287. // Bar chart, line chart which uses category axis
  288. // only gives the 'y' value. 'x' value is the indices of category
  289. // Use a tempValue to normalize the value to be a (x, y) value
  290. // Store the data by dimensions
  291. for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) {
  292. var dimStorage = chunks[dimIdx];
  293. // PENDING NULL is empty or zero
  294. var val = this._dimValueGetter(dataItem, dimNames[dimIdx], idx, dimIdx);
  295. dimStorage[idx] = val;
  296. var dimRawExtent = rawExtent[dimIdx];
  297. val < dimRawExtent[0] && (dimRawExtent[0] = val);
  298. val > dimRawExtent[1] && (dimRawExtent[1] = val);
  299. }
  300. }
  301. }
  302. if (!provider.persistent && provider.clean) {
  303. // Clean unused data if data source is typed array.
  304. provider.clean();
  305. }
  306. this._rawCount = this._count = end;
  307. // Reset data extent
  308. this._extent = [];
  309. };
  310. DataStore.prototype.count = function () {
  311. return this._count;
  312. };
  313. /**
  314. * Get value. Return NaN if idx is out of range.
  315. */
  316. DataStore.prototype.get = function (dim, idx) {
  317. if (!(idx >= 0 && idx < this._count)) {
  318. return NaN;
  319. }
  320. var dimStore = this._chunks[dim];
  321. return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;
  322. };
  323. DataStore.prototype.getValues = function (dimensions, idx) {
  324. var values = [];
  325. var dimArr = [];
  326. if (idx == null) {
  327. idx = dimensions;
  328. // TODO get all from store?
  329. dimensions = [];
  330. // All dimensions
  331. for (var i = 0; i < this._dimensions.length; i++) {
  332. dimArr.push(i);
  333. }
  334. } else {
  335. dimArr = dimensions;
  336. }
  337. for (var i = 0, len = dimArr.length; i < len; i++) {
  338. values.push(this.get(dimArr[i], idx));
  339. }
  340. return values;
  341. };
  342. /**
  343. * @param dim concrete dim
  344. */
  345. DataStore.prototype.getByRawIndex = function (dim, rawIdx) {
  346. if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
  347. return NaN;
  348. }
  349. var dimStore = this._chunks[dim];
  350. return dimStore ? dimStore[rawIdx] : NaN;
  351. };
  352. /**
  353. * Get sum of data in one dimension
  354. */
  355. DataStore.prototype.getSum = function (dim) {
  356. var dimData = this._chunks[dim];
  357. var sum = 0;
  358. if (dimData) {
  359. for (var i = 0, len = this.count(); i < len; i++) {
  360. var value = this.get(dim, i);
  361. if (!isNaN(value)) {
  362. sum += value;
  363. }
  364. }
  365. }
  366. return sum;
  367. };
  368. /**
  369. * Get median of data in one dimension
  370. */
  371. DataStore.prototype.getMedian = function (dim) {
  372. var dimDataArray = [];
  373. // map all data of one dimension
  374. this.each([dim], function (val) {
  375. if (!isNaN(val)) {
  376. dimDataArray.push(val);
  377. }
  378. });
  379. // TODO
  380. // Use quick select?
  381. asc(dimDataArray);
  382. var len = this.count();
  383. // calculate median
  384. return len === 0 ? 0 : len % 2 === 1 ? dimDataArray[(len - 1) / 2] : (dimDataArray[len / 2] + dimDataArray[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. * [NOTICE]: Performance-sensitive for large data.
  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]] = initExtentForUnion();
  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 ordinal 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] = initExtentForUnion();
  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(0, function (x, idx) {});
  826. * list.each([0, 1], 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. DataStore.prototype.getDataExtent = function (dim, filter) {
  861. // Make sure use concrete dim as cache name.
  862. var dimData = this._chunks[dim];
  863. var initialExtent = initExtentForUnion();
  864. if (!dimData) {
  865. return initialExtent;
  866. }
  867. // Make more strict checkings to ensure hitting cache.
  868. var currEnd = this.count();
  869. // Consider the most cases when using data zoom, `getDataExtent`
  870. // happened before filtering. We cache raw extent, which is not
  871. // necessary to be cleared and recalculated when restore data.
  872. var useRaw = !this._indices && !filter;
  873. if (useRaw) {
  874. return this._rawExtent[dim].slice();
  875. }
  876. // NOTE:
  877. // - In logarithm axis, zero should be excluded, therefore the `extent[0]` should be less or equal
  878. // than the min positive data item, which requires the special handling here.
  879. // - "Filter non-positive values for logarithm axis" can also be implemented in a data processor
  880. // but that requires more complicated code to not break all streams under the current architecture,
  881. // therefore we simply implement it here.
  882. // - Performance is sensitive for large data, therefore inline filters rather than cb is used here.
  883. var thisExtent = this._extent;
  884. var dimExtentRecord = thisExtent[dim] || (thisExtent[dim] = {});
  885. var filterParsed = parseSanitizationFilter(filter);
  886. var filterKey = filterParsed.key;
  887. var dimExtent = dimExtentRecord[filterKey];
  888. if (dimExtent) {
  889. return dimExtent.slice();
  890. }
  891. var min = initialExtent[0];
  892. var max = initialExtent[1];
  893. for (var i = 0; i < currEnd; i++) {
  894. // NOTICE: Manually inline some code for performance of large data.
  895. var rawIdx = this.getRawIndex(i);
  896. var value = dimData[rawIdx];
  897. // NOTE: in most cases, filter does not exist.
  898. if (!filter || passesSanitizationFilter(filterParsed, value)) {
  899. if (value < min) {
  900. min = value;
  901. }
  902. if (value > max) {
  903. max = value;
  904. }
  905. }
  906. }
  907. return dimExtentRecord[filterKey] = [min, max];
  908. };
  909. /**
  910. * Get raw data item
  911. */
  912. DataStore.prototype.getRawDataItem = function (idx) {
  913. var rawIdx = this.getRawIndex(idx);
  914. if (!this._provider.persistent) {
  915. var val = [];
  916. var chunks = this._chunks;
  917. for (var i = 0; i < chunks.length; i++) {
  918. val.push(chunks[i][rawIdx]);
  919. }
  920. return val;
  921. } else {
  922. return this._provider.getItem(rawIdx);
  923. }
  924. };
  925. /**
  926. * Clone shallow.
  927. *
  928. * @param clonedDims Determine which dims to clone. Will share the data if not specified.
  929. */
  930. DataStore.prototype.clone = function (clonedDims, ignoreIndices) {
  931. var target = new DataStore();
  932. var chunks = this._chunks;
  933. var clonedDimsMap = clonedDims && reduce(clonedDims, function (obj, dimIdx) {
  934. obj[dimIdx] = true;
  935. return obj;
  936. }, {});
  937. if (clonedDimsMap) {
  938. for (var i = 0; i < chunks.length; i++) {
  939. // Not clone if dim is not picked.
  940. target._chunks[i] = !clonedDimsMap[i] ? chunks[i] : cloneChunk(chunks[i]);
  941. }
  942. } else {
  943. target._chunks = chunks;
  944. }
  945. this._copyCommonProps(target);
  946. if (!ignoreIndices) {
  947. target._indices = this._cloneIndices();
  948. }
  949. target._updateGetRawIdx();
  950. return target;
  951. };
  952. DataStore.prototype._copyCommonProps = function (target) {
  953. target._count = this._count;
  954. target._rawCount = this._rawCount;
  955. target._provider = this._provider;
  956. target._dimensions = this._dimensions;
  957. target._extent = clone(this._extent);
  958. target._rawExtent = clone(this._rawExtent);
  959. };
  960. DataStore.prototype._cloneIndices = function () {
  961. if (this._indices) {
  962. var Ctor = this._indices.constructor;
  963. var indices = void 0;
  964. if (Ctor === Array) {
  965. var thisCount = this._indices.length;
  966. indices = new Ctor(thisCount);
  967. for (var i = 0; i < thisCount; i++) {
  968. indices[i] = this._indices[i];
  969. }
  970. } else {
  971. indices = new Ctor(this._indices);
  972. }
  973. return indices;
  974. }
  975. return null;
  976. };
  977. DataStore.prototype._getRawIdxIdentity = function (idx) {
  978. return idx;
  979. };
  980. DataStore.prototype._getRawIdx = function (idx) {
  981. if (idx < this._count && idx >= 0) {
  982. return this._indices[idx];
  983. }
  984. return -1;
  985. };
  986. DataStore.prototype._updateGetRawIdx = function () {
  987. this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity;
  988. };
  989. DataStore.internalField = function () {
  990. function getDimValueSimply(dataItem, property, dataIndex, dimIndex) {
  991. return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]);
  992. }
  993. defaultDimValueGetters = {
  994. arrayRows: getDimValueSimply,
  995. objectRows: function (dataItem, property, dataIndex, dimIndex) {
  996. return parseDataValue(dataItem[property], this._dimensions[dimIndex]);
  997. },
  998. keyedColumns: getDimValueSimply,
  999. original: function (dataItem, property, dataIndex, dimIndex) {
  1000. // Performance sensitive, do not use modelUtil.getDataItemValue.
  1001. // If dataItem is an plain object with no value field, the let `value`
  1002. // will be assigned with the object, but it will be tread correctly
  1003. // in the `convertValue`.
  1004. var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
  1005. return parseDataValue(value instanceof Array ? value[dimIndex]
  1006. // If value is a single number or something else not array.
  1007. : value, this._dimensions[dimIndex]);
  1008. },
  1009. typedArray: function (dataItem, property, dataIndex, dimIndex) {
  1010. return dataItem[dimIndex];
  1011. }
  1012. };
  1013. }();
  1014. return DataStore;
  1015. }();
  1016. export default DataStore;