DataStore.js 33 KB

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