List.js 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085
  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. var _config = require("../config");
  20. var __DEV__ = _config.__DEV__;
  21. var zrUtil = require("zrender/lib/core/util");
  22. var Model = require("../model/Model");
  23. var DataDiffer = require("./DataDiffer");
  24. var Source = require("./Source");
  25. var _dataProvider = require("./helper/dataProvider");
  26. var defaultDimValueGetters = _dataProvider.defaultDimValueGetters;
  27. var DefaultDataProvider = _dataProvider.DefaultDataProvider;
  28. var _dimensionHelper = require("./helper/dimensionHelper");
  29. var summarizeDimensions = _dimensionHelper.summarizeDimensions;
  30. var DataDimensionInfo = require("./DataDimensionInfo");
  31. /*
  32. * Licensed to the Apache Software Foundation (ASF) under one
  33. * or more contributor license agreements. See the NOTICE file
  34. * distributed with this work for additional information
  35. * regarding copyright ownership. The ASF licenses this file
  36. * to you under the Apache License, Version 2.0 (the
  37. * "License"); you may not use this file except in compliance
  38. * with the License. You may obtain a copy of the License at
  39. *
  40. * http://www.apache.org/licenses/LICENSE-2.0
  41. *
  42. * Unless required by applicable law or agreed to in writing,
  43. * software distributed under the License is distributed on an
  44. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  45. * KIND, either express or implied. See the License for the
  46. * specific language governing permissions and limitations
  47. * under the License.
  48. */
  49. /* global Float64Array, Int32Array, Uint32Array, Uint16Array */
  50. /**
  51. * List for data storage
  52. * @module echarts/data/List
  53. */
  54. var isObject = zrUtil.isObject;
  55. var UNDEFINED = 'undefined';
  56. var INDEX_NOT_FOUND = -1; // Use prefix to avoid index to be the same as otherIdList[idx],
  57. // which will cause weird udpate animation.
  58. var ID_PREFIX = 'e\0\0';
  59. var dataCtors = {
  60. 'float': typeof Float64Array === UNDEFINED ? Array : Float64Array,
  61. 'int': typeof Int32Array === UNDEFINED ? Array : Int32Array,
  62. // Ordinal data type can be string or int
  63. 'ordinal': Array,
  64. 'number': Array,
  65. 'time': Array
  66. }; // Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is
  67. // different from the Ctor of typed array.
  68. var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;
  69. var CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array;
  70. var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;
  71. function getIndicesCtor(list) {
  72. // The possible max value in this._indicies is always this._rawCount despite of filtering.
  73. return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
  74. }
  75. function cloneChunk(originalChunk) {
  76. var Ctor = originalChunk.constructor; // Only shallow clone is enough when Array.
  77. return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);
  78. }
  79. var TRANSFERABLE_PROPERTIES = ['hasItemOption', '_nameList', '_idList', '_invertedIndicesMap', '_rawData', '_chunkSize', '_chunkCount', '_dimValueGetter', '_count', '_rawCount', '_nameDimIdx', '_idDimIdx'];
  80. var CLONE_PROPERTIES = ['_extent', '_approximateExtent', '_rawExtent'];
  81. function transferProperties(target, source) {
  82. zrUtil.each(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) {
  83. if (source.hasOwnProperty(propName)) {
  84. target[propName] = source[propName];
  85. }
  86. });
  87. target.__wrappedMethods = source.__wrappedMethods;
  88. zrUtil.each(CLONE_PROPERTIES, function (propName) {
  89. target[propName] = zrUtil.clone(source[propName]);
  90. });
  91. target._calculationInfo = zrUtil.extend(source._calculationInfo);
  92. }
  93. /**
  94. * @constructor
  95. * @alias module:echarts/data/List
  96. *
  97. * @param {Array.<string|Object|module:data/DataDimensionInfo>} dimensions
  98. * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
  99. * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
  100. * @param {module:echarts/model/Model} hostModel
  101. */
  102. var List = function (dimensions, hostModel) {
  103. dimensions = dimensions || ['x', 'y'];
  104. var dimensionInfos = {};
  105. var dimensionNames = [];
  106. var invertedIndicesMap = {};
  107. for (var i = 0; i < dimensions.length; i++) {
  108. // Use the original dimensions[i], where other flag props may exists.
  109. var dimensionInfo = dimensions[i];
  110. if (zrUtil.isString(dimensionInfo)) {
  111. dimensionInfo = new DataDimensionInfo({
  112. name: dimensionInfo
  113. });
  114. } else if (!(dimensionInfo instanceof DataDimensionInfo)) {
  115. dimensionInfo = new DataDimensionInfo(dimensionInfo);
  116. }
  117. var dimensionName = dimensionInfo.name;
  118. dimensionInfo.type = dimensionInfo.type || 'float';
  119. if (!dimensionInfo.coordDim) {
  120. dimensionInfo.coordDim = dimensionName;
  121. dimensionInfo.coordDimIndex = 0;
  122. }
  123. dimensionInfo.otherDims = dimensionInfo.otherDims || {};
  124. dimensionNames.push(dimensionName);
  125. dimensionInfos[dimensionName] = dimensionInfo;
  126. dimensionInfo.index = i;
  127. if (dimensionInfo.createInvertedIndices) {
  128. invertedIndicesMap[dimensionName] = [];
  129. }
  130. }
  131. /**
  132. * @readOnly
  133. * @type {Array.<string>}
  134. */
  135. this.dimensions = dimensionNames;
  136. /**
  137. * Infomation of each data dimension, like data type.
  138. * @type {Object}
  139. */
  140. this._dimensionInfos = dimensionInfos;
  141. /**
  142. * @type {module:echarts/model/Model}
  143. */
  144. this.hostModel = hostModel;
  145. /**
  146. * @type {module:echarts/model/Model}
  147. */
  148. this.dataType;
  149. /**
  150. * Indices stores the indices of data subset after filtered.
  151. * This data subset will be used in chart.
  152. * @type {Array.<number>}
  153. * @readOnly
  154. */
  155. this._indices = null;
  156. this._count = 0;
  157. this._rawCount = 0;
  158. /**
  159. * Data storage
  160. * @type {Object.<key, Array.<TypedArray|Array>>}
  161. * @private
  162. */
  163. this._storage = {};
  164. /**
  165. * @type {Array.<string>}
  166. */
  167. this._nameList = [];
  168. /**
  169. * @type {Array.<string>}
  170. */
  171. this._idList = [];
  172. /**
  173. * Models of data option is stored sparse for optimizing memory cost
  174. * @type {Array.<module:echarts/model/Model>}
  175. * @private
  176. */
  177. this._optionModels = [];
  178. /**
  179. * Global visual properties after visual coding
  180. * @type {Object}
  181. * @private
  182. */
  183. this._visual = {};
  184. /**
  185. * Globel layout properties.
  186. * @type {Object}
  187. * @private
  188. */
  189. this._layout = {};
  190. /**
  191. * Item visual properties after visual coding
  192. * @type {Array.<Object>}
  193. * @private
  194. */
  195. this._itemVisuals = [];
  196. /**
  197. * Key: visual type, Value: boolean
  198. * @type {Object}
  199. * @readOnly
  200. */
  201. this.hasItemVisual = {};
  202. /**
  203. * Item layout properties after layout
  204. * @type {Array.<Object>}
  205. * @private
  206. */
  207. this._itemLayouts = [];
  208. /**
  209. * Graphic elemnents
  210. * @type {Array.<module:zrender/Element>}
  211. * @private
  212. */
  213. this._graphicEls = [];
  214. /**
  215. * Max size of each chunk.
  216. * @type {number}
  217. * @private
  218. */
  219. this._chunkSize = 1e5;
  220. /**
  221. * @type {number}
  222. * @private
  223. */
  224. this._chunkCount = 0;
  225. /**
  226. * @type {Array.<Array|Object>}
  227. * @private
  228. */
  229. this._rawData;
  230. /**
  231. * Raw extent will not be cloned, but only transfered.
  232. * It will not be calculated util needed.
  233. * key: dim,
  234. * value: {end: number, extent: Array.<number>}
  235. * @type {Object}
  236. * @private
  237. */
  238. this._rawExtent = {};
  239. /**
  240. * @type {Object}
  241. * @private
  242. */
  243. this._extent = {};
  244. /**
  245. * key: dim
  246. * value: extent
  247. * @type {Object}
  248. * @private
  249. */
  250. this._approximateExtent = {};
  251. /**
  252. * Cache summary info for fast visit. See "dimensionHelper".
  253. * @type {Object}
  254. * @private
  255. */
  256. this._dimensionsSummary = summarizeDimensions(this);
  257. /**
  258. * @type {Object.<Array|TypedArray>}
  259. * @private
  260. */
  261. this._invertedIndicesMap = invertedIndicesMap;
  262. /**
  263. * @type {Object}
  264. * @private
  265. */
  266. this._calculationInfo = {};
  267. /**
  268. * User output info of this data.
  269. * DO NOT use it in other places!
  270. *
  271. * When preparing user params for user callbacks, we have
  272. * to clone these inner data structures to prevent users
  273. * from modifying them to effect built-in logic. And for
  274. * performance consideration we make this `userOutput` to
  275. * avoid clone them too many times.
  276. *
  277. * @type {Object}
  278. * @readOnly
  279. */
  280. this.userOutput = this._dimensionsSummary.userOutput;
  281. };
  282. var listProto = List.prototype;
  283. listProto.type = 'list';
  284. /**
  285. * If each data item has it's own option
  286. * @type {boolean}
  287. */
  288. listProto.hasItemOption = true;
  289. /**
  290. * The meanings of the input parameter `dim`:
  291. *
  292. * + If dim is a number (e.g., `1`), it means the index of the dimension.
  293. * For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'.
  294. * + If dim is a number-like string (e.g., `"1"`):
  295. * + If there is the same concrete dim name defined in `this.dimensions`, it means that concrete name.
  296. * + If not, it will be converted to a number, which means the index of the dimension.
  297. * (why? because of the backward compatbility. We have been tolerating number-like string in
  298. * dimension setting, although now it seems that it is not a good idea.)
  299. * For example, `visualMap[i].dimension: "1"` is the same meaning as `visualMap[i].dimension: 1`,
  300. * if no dimension name is defined as `"1"`.
  301. * + If dim is a not-number-like string, it means the concrete dim name.
  302. * For example, it can be be default name `"x"`, `"y"`, `"z"`, `"lng"`, `"lat"`, `"angle"`, `"radius"`,
  303. * or customized in `dimensions` property of option like `"age"`.
  304. *
  305. * Get dimension name
  306. * @param {string|number} dim See above.
  307. * @return {string} Concrete dim name.
  308. */
  309. listProto.getDimension = function (dim) {
  310. if (typeof dim === 'number' // If being a number-like string but not being defined a dimension name.
  311. || !isNaN(dim) && !this._dimensionInfos.hasOwnProperty(dim)) {
  312. dim = this.dimensions[dim];
  313. }
  314. return dim;
  315. };
  316. /**
  317. * Get type and calculation info of particular dimension
  318. * @param {string|number} dim
  319. * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
  320. * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
  321. */
  322. listProto.getDimensionInfo = function (dim) {
  323. // Do not clone, because there may be categories in dimInfo.
  324. return this._dimensionInfos[this.getDimension(dim)];
  325. };
  326. /**
  327. * @return {Array.<string>} concrete dimension name list on coord.
  328. */
  329. listProto.getDimensionsOnCoord = function () {
  330. return this._dimensionsSummary.dataDimsOnCoord.slice();
  331. };
  332. /**
  333. * @param {string} coordDim
  334. * @param {number} [idx] A coordDim may map to more than one data dim.
  335. * If idx is `true`, return a array of all mapped dims.
  336. * If idx is not specified, return the first dim not extra.
  337. * @return {string|Array.<string>} concrete data dim.
  338. * If idx is number, and not found, return null/undefined.
  339. * If idx is `true`, and not found, return empty array (always return array).
  340. */
  341. listProto.mapDimension = function (coordDim, idx) {
  342. var dimensionsSummary = this._dimensionsSummary;
  343. if (idx == null) {
  344. return dimensionsSummary.encodeFirstDimNotExtra[coordDim];
  345. }
  346. var dims = dimensionsSummary.encode[coordDim];
  347. return idx === true // always return array if idx is `true`
  348. ? (dims || []).slice() : dims && dims[idx];
  349. };
  350. /**
  351. * Initialize from data
  352. * @param {Array.<Object|number|Array>} data source or data or data provider.
  353. * @param {Array.<string>} [nameLIst] The name of a datum is used on data diff and
  354. * default label/tooltip.
  355. * A name can be specified in encode.itemName,
  356. * or dataItem.name (only for series option data),
  357. * or provided in nameList from outside.
  358. * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
  359. */
  360. listProto.initData = function (data, nameList, dimValueGetter) {
  361. var notProvider = Source.isInstance(data) || zrUtil.isArrayLike(data);
  362. if (notProvider) {
  363. data = new DefaultDataProvider(data, this.dimensions.length);
  364. }
  365. this._rawData = data; // Clear
  366. this._storage = {};
  367. this._indices = null;
  368. this._nameList = nameList || [];
  369. this._idList = [];
  370. this._nameRepeatCount = {};
  371. if (!dimValueGetter) {
  372. this.hasItemOption = false;
  373. }
  374. /**
  375. * @readOnly
  376. */
  377. this.defaultDimValueGetter = defaultDimValueGetters[this._rawData.getSource().sourceFormat]; // Default dim value getter
  378. this._dimValueGetter = dimValueGetter = dimValueGetter || this.defaultDimValueGetter;
  379. this._dimValueGetterArrayRows = defaultDimValueGetters.arrayRows; // Reset raw extent.
  380. this._rawExtent = {};
  381. this._initDataFromProvider(0, data.count()); // If data has no item option.
  382. if (data.pure) {
  383. this.hasItemOption = false;
  384. }
  385. };
  386. listProto.getProvider = function () {
  387. return this._rawData;
  388. };
  389. /**
  390. * Caution: Can be only called on raw data (before `this._indices` created).
  391. */
  392. listProto.appendData = function (data) {
  393. var rawData = this._rawData;
  394. var start = this.count();
  395. rawData.appendData(data);
  396. var end = rawData.count();
  397. if (!rawData.persistent) {
  398. end += start;
  399. }
  400. this._initDataFromProvider(start, end);
  401. };
  402. /**
  403. * Caution: Can be only called on raw data (before `this._indices` created).
  404. * This method does not modify `rawData` (`dataProvider`), but only
  405. * add values to storage.
  406. *
  407. * The final count will be increased by `Math.max(values.length, names.length)`.
  408. *
  409. * @param {Array.<Array.<*>>} values That is the SourceType: 'arrayRows', like
  410. * [
  411. * [12, 33, 44],
  412. * [NaN, 43, 1],
  413. * ['-', 'asdf', 0]
  414. * ]
  415. * Each item is exaclty cooresponding to a dimension.
  416. * @param {Array.<string>} [names]
  417. */
  418. listProto.appendValues = function (values, names) {
  419. var chunkSize = this._chunkSize;
  420. var storage = this._storage;
  421. var dimensions = this.dimensions;
  422. var dimLen = dimensions.length;
  423. var rawExtent = this._rawExtent;
  424. var start = this.count();
  425. var end = start + Math.max(values.length, names ? names.length : 0);
  426. var originalChunkCount = this._chunkCount;
  427. for (var i = 0; i < dimLen; i++) {
  428. var dim = dimensions[i];
  429. if (!rawExtent[dim]) {
  430. rawExtent[dim] = getInitialExtent();
  431. }
  432. if (!storage[dim]) {
  433. storage[dim] = [];
  434. }
  435. prepareChunks(storage, this._dimensionInfos[dim], chunkSize, originalChunkCount, end);
  436. this._chunkCount = storage[dim].length;
  437. }
  438. var emptyDataItem = new Array(dimLen);
  439. for (var idx = start; idx < end; idx++) {
  440. var sourceIdx = idx - start;
  441. var chunkIndex = Math.floor(idx / chunkSize);
  442. var chunkOffset = idx % chunkSize; // Store the data by dimensions
  443. for (var k = 0; k < dimLen; k++) {
  444. var dim = dimensions[k];
  445. var val = this._dimValueGetterArrayRows(values[sourceIdx] || emptyDataItem, dim, sourceIdx, k);
  446. storage[dim][chunkIndex][chunkOffset] = val;
  447. var dimRawExtent = rawExtent[dim];
  448. val < dimRawExtent[0] && (dimRawExtent[0] = val);
  449. val > dimRawExtent[1] && (dimRawExtent[1] = val);
  450. }
  451. if (names) {
  452. this._nameList[idx] = names[sourceIdx];
  453. }
  454. }
  455. this._rawCount = this._count = end; // Reset data extent
  456. this._extent = {};
  457. prepareInvertedIndex(this);
  458. };
  459. listProto._initDataFromProvider = function (start, end) {
  460. // Optimize.
  461. if (start >= end) {
  462. return;
  463. }
  464. var chunkSize = this._chunkSize;
  465. var rawData = this._rawData;
  466. var storage = this._storage;
  467. var dimensions = this.dimensions;
  468. var dimLen = dimensions.length;
  469. var dimensionInfoMap = this._dimensionInfos;
  470. var nameList = this._nameList;
  471. var idList = this._idList;
  472. var rawExtent = this._rawExtent;
  473. var nameRepeatCount = this._nameRepeatCount = {};
  474. var nameDimIdx;
  475. var originalChunkCount = this._chunkCount;
  476. for (var i = 0; i < dimLen; i++) {
  477. var dim = dimensions[i];
  478. if (!rawExtent[dim]) {
  479. rawExtent[dim] = getInitialExtent();
  480. }
  481. var dimInfo = dimensionInfoMap[dim];
  482. if (dimInfo.otherDims.itemName === 0) {
  483. nameDimIdx = this._nameDimIdx = i;
  484. }
  485. if (dimInfo.otherDims.itemId === 0) {
  486. this._idDimIdx = i;
  487. }
  488. if (!storage[dim]) {
  489. storage[dim] = [];
  490. }
  491. prepareChunks(storage, dimInfo, chunkSize, originalChunkCount, end);
  492. this._chunkCount = storage[dim].length;
  493. }
  494. var dataItem = new Array(dimLen);
  495. for (var idx = start; idx < end; idx++) {
  496. // NOTICE: Try not to write things into dataItem
  497. dataItem = rawData.getItem(idx, dataItem); // Each data item is value
  498. // [1, 2]
  499. // 2
  500. // Bar chart, line chart which uses category axis
  501. // only gives the 'y' value. 'x' value is the indices of category
  502. // Use a tempValue to normalize the value to be a (x, y) value
  503. var chunkIndex = Math.floor(idx / chunkSize);
  504. var chunkOffset = idx % chunkSize; // Store the data by dimensions
  505. for (var k = 0; k < dimLen; k++) {
  506. var dim = dimensions[k];
  507. var dimStorage = storage[dim][chunkIndex]; // PENDING NULL is empty or zero
  508. var val = this._dimValueGetter(dataItem, dim, idx, k);
  509. dimStorage[chunkOffset] = val;
  510. var dimRawExtent = rawExtent[dim];
  511. val < dimRawExtent[0] && (dimRawExtent[0] = val);
  512. val > dimRawExtent[1] && (dimRawExtent[1] = val);
  513. } // ??? FIXME not check by pure but sourceFormat?
  514. // TODO refactor these logic.
  515. if (!rawData.pure) {
  516. var name = nameList[idx];
  517. if (dataItem && name == null) {
  518. // If dataItem is {name: ...}, it has highest priority.
  519. // That is appropriate for many common cases.
  520. if (dataItem.name != null) {
  521. // There is no other place to persistent dataItem.name,
  522. // so save it to nameList.
  523. nameList[idx] = name = dataItem.name;
  524. } else if (nameDimIdx != null) {
  525. var nameDim = dimensions[nameDimIdx];
  526. var nameDimChunk = storage[nameDim][chunkIndex];
  527. if (nameDimChunk) {
  528. name = nameDimChunk[chunkOffset];
  529. var ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
  530. if (ordinalMeta && ordinalMeta.categories.length) {
  531. name = ordinalMeta.categories[name];
  532. }
  533. }
  534. }
  535. } // Try using the id in option
  536. // id or name is used on dynamical data, mapping old and new items.
  537. var id = dataItem == null ? null : dataItem.id;
  538. if (id == null && name != null) {
  539. // Use name as id and add counter to avoid same name
  540. nameRepeatCount[name] = nameRepeatCount[name] || 0;
  541. id = name;
  542. if (nameRepeatCount[name] > 0) {
  543. id += '__ec__' + nameRepeatCount[name];
  544. }
  545. nameRepeatCount[name]++;
  546. }
  547. id != null && (idList[idx] = id);
  548. }
  549. }
  550. if (!rawData.persistent && rawData.clean) {
  551. // Clean unused data if data source is typed array.
  552. rawData.clean();
  553. }
  554. this._rawCount = this._count = end; // Reset data extent
  555. this._extent = {};
  556. prepareInvertedIndex(this);
  557. };
  558. function prepareChunks(storage, dimInfo, chunkSize, chunkCount, end) {
  559. var DataCtor = dataCtors[dimInfo.type];
  560. var lastChunkIndex = chunkCount - 1;
  561. var dim = dimInfo.name;
  562. var resizeChunkArray = storage[dim][lastChunkIndex];
  563. if (resizeChunkArray && resizeChunkArray.length < chunkSize) {
  564. var newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize)); // The cost of the copy is probably inconsiderable
  565. // within the initial chunkSize.
  566. for (var j = 0; j < resizeChunkArray.length; j++) {
  567. newStore[j] = resizeChunkArray[j];
  568. }
  569. storage[dim][lastChunkIndex] = newStore;
  570. } // Create new chunks.
  571. for (var k = chunkCount * chunkSize; k < end; k += chunkSize) {
  572. storage[dim].push(new DataCtor(Math.min(end - k, chunkSize)));
  573. }
  574. }
  575. function prepareInvertedIndex(list) {
  576. var invertedIndicesMap = list._invertedIndicesMap;
  577. zrUtil.each(invertedIndicesMap, function (invertedIndices, dim) {
  578. var dimInfo = list._dimensionInfos[dim]; // Currently, only dimensions that has ordinalMeta can create inverted indices.
  579. var ordinalMeta = dimInfo.ordinalMeta;
  580. if (ordinalMeta) {
  581. invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array(ordinalMeta.categories.length); // The default value of TypedArray is 0. To avoid miss
  582. // mapping to 0, we should set it as INDEX_NOT_FOUND.
  583. for (var i = 0; i < invertedIndices.length; i++) {
  584. invertedIndices[i] = INDEX_NOT_FOUND;
  585. }
  586. for (var i = 0; i < list._count; i++) {
  587. // Only support the case that all values are distinct.
  588. invertedIndices[list.get(dim, i)] = i;
  589. }
  590. }
  591. });
  592. }
  593. function getRawValueFromStore(list, dimIndex, rawIndex) {
  594. var val;
  595. if (dimIndex != null) {
  596. var chunkSize = list._chunkSize;
  597. var chunkIndex = Math.floor(rawIndex / chunkSize);
  598. var chunkOffset = rawIndex % chunkSize;
  599. var dim = list.dimensions[dimIndex];
  600. var chunk = list._storage[dim][chunkIndex];
  601. if (chunk) {
  602. val = chunk[chunkOffset];
  603. var ordinalMeta = list._dimensionInfos[dim].ordinalMeta;
  604. if (ordinalMeta && ordinalMeta.categories.length) {
  605. val = ordinalMeta.categories[val];
  606. }
  607. }
  608. }
  609. return val;
  610. }
  611. /**
  612. * @return {number}
  613. */
  614. listProto.count = function () {
  615. return this._count;
  616. };
  617. listProto.getIndices = function () {
  618. var newIndices;
  619. var indices = this._indices;
  620. if (indices) {
  621. var Ctor = indices.constructor;
  622. var thisCount = this._count; // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.
  623. if (Ctor === Array) {
  624. newIndices = new Ctor(thisCount);
  625. for (var i = 0; i < thisCount; i++) {
  626. newIndices[i] = indices[i];
  627. }
  628. } else {
  629. newIndices = new Ctor(indices.buffer, 0, thisCount);
  630. }
  631. } else {
  632. var Ctor = getIndicesCtor(this);
  633. var newIndices = new Ctor(this.count());
  634. for (var i = 0; i < newIndices.length; i++) {
  635. newIndices[i] = i;
  636. }
  637. }
  638. return newIndices;
  639. };
  640. /**
  641. * Get value. Return NaN if idx is out of range.
  642. * @param {string} dim Dim must be concrete name.
  643. * @param {number} idx
  644. * @param {boolean} stack
  645. * @return {number}
  646. */
  647. listProto.get = function (dim, idx
  648. /*, stack */
  649. ) {
  650. if (!(idx >= 0 && idx < this._count)) {
  651. return NaN;
  652. }
  653. var storage = this._storage;
  654. if (!storage[dim]) {
  655. // TODO Warn ?
  656. return NaN;
  657. }
  658. idx = this.getRawIndex(idx);
  659. var chunkIndex = Math.floor(idx / this._chunkSize);
  660. var chunkOffset = idx % this._chunkSize;
  661. var chunkStore = storage[dim][chunkIndex];
  662. var value = chunkStore[chunkOffset]; // FIXME ordinal data type is not stackable
  663. // if (stack) {
  664. // var dimensionInfo = this._dimensionInfos[dim];
  665. // if (dimensionInfo && dimensionInfo.stackable) {
  666. // var stackedOn = this.stackedOn;
  667. // while (stackedOn) {
  668. // // Get no stacked data of stacked on
  669. // var stackedValue = stackedOn.get(dim, idx);
  670. // // Considering positive stack, negative stack and empty data
  671. // if ((value >= 0 && stackedValue > 0) // Positive stack
  672. // || (value <= 0 && stackedValue < 0) // Negative stack
  673. // ) {
  674. // value += stackedValue;
  675. // }
  676. // stackedOn = stackedOn.stackedOn;
  677. // }
  678. // }
  679. // }
  680. return value;
  681. };
  682. /**
  683. * @param {string} dim concrete dim
  684. * @param {number} rawIndex
  685. * @return {number|string}
  686. */
  687. listProto.getByRawIndex = function (dim, rawIdx) {
  688. if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
  689. return NaN;
  690. }
  691. var dimStore = this._storage[dim];
  692. if (!dimStore) {
  693. // TODO Warn ?
  694. return NaN;
  695. }
  696. var chunkIndex = Math.floor(rawIdx / this._chunkSize);
  697. var chunkOffset = rawIdx % this._chunkSize;
  698. var chunkStore = dimStore[chunkIndex];
  699. return chunkStore[chunkOffset];
  700. };
  701. /**
  702. * FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange).
  703. * Hack a much simpler _getFast
  704. * @private
  705. */
  706. listProto._getFast = function (dim, rawIdx) {
  707. var chunkIndex = Math.floor(rawIdx / this._chunkSize);
  708. var chunkOffset = rawIdx % this._chunkSize;
  709. var chunkStore = this._storage[dim][chunkIndex];
  710. return chunkStore[chunkOffset];
  711. };
  712. /**
  713. * Get value for multi dimensions.
  714. * @param {Array.<string>} [dimensions] If ignored, using all dimensions.
  715. * @param {number} idx
  716. * @return {number}
  717. */
  718. listProto.getValues = function (dimensions, idx
  719. /*, stack */
  720. ) {
  721. var values = [];
  722. if (!zrUtil.isArray(dimensions)) {
  723. // stack = idx;
  724. idx = dimensions;
  725. dimensions = this.dimensions;
  726. }
  727. for (var i = 0, len = dimensions.length; i < len; i++) {
  728. values.push(this.get(dimensions[i], idx
  729. /*, stack */
  730. ));
  731. }
  732. return values;
  733. };
  734. /**
  735. * If value is NaN. Inlcuding '-'
  736. * Only check the coord dimensions.
  737. * @param {string} dim
  738. * @param {number} idx
  739. * @return {number}
  740. */
  741. listProto.hasValue = function (idx) {
  742. var dataDimsOnCoord = this._dimensionsSummary.dataDimsOnCoord;
  743. for (var i = 0, len = dataDimsOnCoord.length; i < len; i++) {
  744. // Ordinal type originally can be string or number.
  745. // But when an ordinal type is used on coord, it can
  746. // not be string but only number. So we can also use isNaN.
  747. if (isNaN(this.get(dataDimsOnCoord[i], idx))) {
  748. return false;
  749. }
  750. }
  751. return true;
  752. };
  753. /**
  754. * Get extent of data in one dimension
  755. * @param {string} dim
  756. * @param {boolean} stack
  757. */
  758. listProto.getDataExtent = function (dim
  759. /*, stack */
  760. ) {
  761. // Make sure use concrete dim as cache name.
  762. dim = this.getDimension(dim);
  763. var dimData = this._storage[dim];
  764. var initialExtent = getInitialExtent(); // stack = !!((stack || false) && this.getCalculationInfo(dim));
  765. if (!dimData) {
  766. return initialExtent;
  767. } // Make more strict checkings to ensure hitting cache.
  768. var currEnd = this.count(); // var cacheName = [dim, !!stack].join('_');
  769. // var cacheName = dim;
  770. // Consider the most cases when using data zoom, `getDataExtent`
  771. // happened before filtering. We cache raw extent, which is not
  772. // necessary to be cleared and recalculated when restore data.
  773. var useRaw = !this._indices; // && !stack;
  774. var dimExtent;
  775. if (useRaw) {
  776. return this._rawExtent[dim].slice();
  777. }
  778. dimExtent = this._extent[dim];
  779. if (dimExtent) {
  780. return dimExtent.slice();
  781. }
  782. dimExtent = initialExtent;
  783. var min = dimExtent[0];
  784. var max = dimExtent[1];
  785. for (var i = 0; i < currEnd; i++) {
  786. // var value = stack ? this.get(dim, i, true) : this._getFast(dim, this.getRawIndex(i));
  787. var value = this._getFast(dim, this.getRawIndex(i));
  788. value < min && (min = value);
  789. value > max && (max = value);
  790. }
  791. dimExtent = [min, max];
  792. this._extent[dim] = dimExtent;
  793. return dimExtent;
  794. };
  795. /**
  796. * Optimize for the scenario that data is filtered by a given extent.
  797. * Consider that if data amount is more than hundreds of thousand,
  798. * extent calculation will cost more than 10ms and the cache will
  799. * be erased because of the filtering.
  800. */
  801. listProto.getApproximateExtent = function (dim
  802. /*, stack */
  803. ) {
  804. dim = this.getDimension(dim);
  805. return this._approximateExtent[dim] || this.getDataExtent(dim
  806. /*, stack */
  807. );
  808. };
  809. listProto.setApproximateExtent = function (extent, dim
  810. /*, stack */
  811. ) {
  812. dim = this.getDimension(dim);
  813. this._approximateExtent[dim] = extent.slice();
  814. };
  815. /**
  816. * @param {string} key
  817. * @return {*}
  818. */
  819. listProto.getCalculationInfo = function (key) {
  820. return this._calculationInfo[key];
  821. };
  822. /**
  823. * @param {string|Object} key or k-v object
  824. * @param {*} [value]
  825. */
  826. listProto.setCalculationInfo = function (key, value) {
  827. isObject(key) ? zrUtil.extend(this._calculationInfo, key) : this._calculationInfo[key] = value;
  828. };
  829. /**
  830. * Get sum of data in one dimension
  831. * @param {string} dim
  832. */
  833. listProto.getSum = function (dim
  834. /*, stack */
  835. ) {
  836. var dimData = this._storage[dim];
  837. var sum = 0;
  838. if (dimData) {
  839. for (var i = 0, len = this.count(); i < len; i++) {
  840. var value = this.get(dim, i
  841. /*, stack */
  842. );
  843. if (!isNaN(value)) {
  844. sum += value;
  845. }
  846. }
  847. }
  848. return sum;
  849. };
  850. /**
  851. * Get median of data in one dimension
  852. * @param {string} dim
  853. */
  854. listProto.getMedian = function (dim
  855. /*, stack */
  856. ) {
  857. var dimDataArray = []; // map all data of one dimension
  858. this.each(dim, function (val, idx) {
  859. if (!isNaN(val)) {
  860. dimDataArray.push(val);
  861. }
  862. }); // TODO
  863. // Use quick select?
  864. // immutability & sort
  865. var sortedDimDataArray = [].concat(dimDataArray).sort(function (a, b) {
  866. return a - b;
  867. });
  868. var len = this.count(); // calculate median
  869. return len === 0 ? 0 : len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;
  870. }; // /**
  871. // * Retreive the index with given value
  872. // * @param {string} dim Concrete dimension.
  873. // * @param {number} value
  874. // * @return {number}
  875. // */
  876. // Currently incorrect: should return dataIndex but not rawIndex.
  877. // Do not fix it until this method is to be used somewhere.
  878. // FIXME Precision of float value
  879. // listProto.indexOf = function (dim, value) {
  880. // var storage = this._storage;
  881. // var dimData = storage[dim];
  882. // var chunkSize = this._chunkSize;
  883. // if (dimData) {
  884. // for (var i = 0, len = this.count(); i < len; i++) {
  885. // var chunkIndex = Math.floor(i / chunkSize);
  886. // var chunkOffset = i % chunkSize;
  887. // if (dimData[chunkIndex][chunkOffset] === value) {
  888. // return i;
  889. // }
  890. // }
  891. // }
  892. // return -1;
  893. // };
  894. /**
  895. * Only support the dimension which inverted index created.
  896. * Do not support other cases until required.
  897. * @param {string} concrete dim
  898. * @param {number|string} value
  899. * @return {number} rawIndex
  900. */
  901. listProto.rawIndexOf = function (dim, value) {
  902. var invertedIndices = dim && this._invertedIndicesMap[dim];
  903. var rawIndex = invertedIndices[value];
  904. if (rawIndex == null || isNaN(rawIndex)) {
  905. return INDEX_NOT_FOUND;
  906. }
  907. return rawIndex;
  908. };
  909. /**
  910. * Retreive the index with given name
  911. * @param {number} idx
  912. * @param {number} name
  913. * @return {number}
  914. */
  915. listProto.indexOfName = function (name) {
  916. for (var i = 0, len = this.count(); i < len; i++) {
  917. if (this.getName(i) === name) {
  918. return i;
  919. }
  920. }
  921. return -1;
  922. };
  923. /**
  924. * Retreive the index with given raw data index
  925. * @param {number} idx
  926. * @param {number} name
  927. * @return {number}
  928. */
  929. listProto.indexOfRawIndex = function (rawIndex) {
  930. if (rawIndex >= this._rawCount || rawIndex < 0) {
  931. return -1;
  932. }
  933. if (!this._indices) {
  934. return rawIndex;
  935. } // Indices are ascending
  936. var indices = this._indices; // If rawIndex === dataIndex
  937. var rawDataIndex = indices[rawIndex];
  938. if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
  939. return rawIndex;
  940. }
  941. var left = 0;
  942. var right = this._count - 1;
  943. while (left <= right) {
  944. var mid = (left + right) / 2 | 0;
  945. if (indices[mid] < rawIndex) {
  946. left = mid + 1;
  947. } else if (indices[mid] > rawIndex) {
  948. right = mid - 1;
  949. } else {
  950. return mid;
  951. }
  952. }
  953. return -1;
  954. };
  955. /**
  956. * Retreive the index of nearest value
  957. * @param {string} dim
  958. * @param {number} value
  959. * @param {number} [maxDistance=Infinity]
  960. * @return {Array.<number>} If and only if multiple indices has
  961. * the same value, they are put to the result.
  962. */
  963. listProto.indicesOfNearest = function (dim, value, maxDistance) {
  964. var storage = this._storage;
  965. var dimData = storage[dim];
  966. var nearestIndices = [];
  967. if (!dimData) {
  968. return nearestIndices;
  969. }
  970. if (maxDistance == null) {
  971. maxDistance = Infinity;
  972. }
  973. var minDist = Infinity;
  974. var minDiff = -1;
  975. var nearestIndicesLen = 0; // Check the test case of `test/ut/spec/data/List.js`.
  976. for (var i = 0, len = this.count(); i < len; i++) {
  977. var diff = value - this.get(dim, i);
  978. var dist = Math.abs(diff);
  979. if (dist <= maxDistance) {
  980. // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,
  981. // we'd better not push both of them to `nearestIndices`, otherwise it is easy to
  982. // get more than one item in `nearestIndices` (more specifically, in `tooltip`).
  983. // So we chose the one that `diff >= 0` in this csae.
  984. // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them
  985. // should be push to `nearestIndices`.
  986. if (dist < minDist || dist === minDist && diff >= 0 && minDiff < 0) {
  987. minDist = dist;
  988. minDiff = diff;
  989. nearestIndicesLen = 0;
  990. }
  991. if (diff === minDiff) {
  992. nearestIndices[nearestIndicesLen++] = i;
  993. }
  994. }
  995. }
  996. nearestIndices.length = nearestIndicesLen;
  997. return nearestIndices;
  998. };
  999. /**
  1000. * Get raw data index
  1001. * @param {number} idx
  1002. * @return {number}
  1003. */
  1004. listProto.getRawIndex = getRawIndexWithoutIndices;
  1005. function getRawIndexWithoutIndices(idx) {
  1006. return idx;
  1007. }
  1008. function getRawIndexWithIndices(idx) {
  1009. if (idx < this._count && idx >= 0) {
  1010. return this._indices[idx];
  1011. }
  1012. return -1;
  1013. }
  1014. /**
  1015. * Get raw data item
  1016. * @param {number} idx
  1017. * @return {number}
  1018. */
  1019. listProto.getRawDataItem = function (idx) {
  1020. if (!this._rawData.persistent) {
  1021. var val = [];
  1022. for (var i = 0; i < this.dimensions.length; i++) {
  1023. var dim = this.dimensions[i];
  1024. val.push(this.get(dim, idx));
  1025. }
  1026. return val;
  1027. } else {
  1028. return this._rawData.getItem(this.getRawIndex(idx));
  1029. }
  1030. };
  1031. /**
  1032. * @param {number} idx
  1033. * @param {boolean} [notDefaultIdx=false]
  1034. * @return {string}
  1035. */
  1036. listProto.getName = function (idx) {
  1037. var rawIndex = this.getRawIndex(idx);
  1038. return this._nameList[rawIndex] || getRawValueFromStore(this, this._nameDimIdx, rawIndex) || '';
  1039. };
  1040. /**
  1041. * @param {number} idx
  1042. * @param {boolean} [notDefaultIdx=false]
  1043. * @return {string}
  1044. */
  1045. listProto.getId = function (idx) {
  1046. return getId(this, this.getRawIndex(idx));
  1047. };
  1048. function getId(list, rawIndex) {
  1049. var id = list._idList[rawIndex];
  1050. if (id == null) {
  1051. id = getRawValueFromStore(list, list._idDimIdx, rawIndex);
  1052. }
  1053. if (id == null) {
  1054. // FIXME Check the usage in graph, should not use prefix.
  1055. id = ID_PREFIX + rawIndex;
  1056. }
  1057. return id;
  1058. }
  1059. function normalizeDimensions(dimensions) {
  1060. if (!zrUtil.isArray(dimensions)) {
  1061. dimensions = [dimensions];
  1062. }
  1063. return dimensions;
  1064. }
  1065. function validateDimensions(list, dims) {
  1066. for (var i = 0; i < dims.length; i++) {
  1067. // stroage may be empty when no data, so use
  1068. // dimensionInfos to check.
  1069. if (!list._dimensionInfos[dims[i]]) {
  1070. console.error('Unkown dimension ' + dims[i]);
  1071. }
  1072. }
  1073. }
  1074. /**
  1075. * Data iteration
  1076. * @param {string|Array.<string>}
  1077. * @param {Function} cb
  1078. * @param {*} [context=this]
  1079. *
  1080. * @example
  1081. * list.each('x', function (x, idx) {});
  1082. * list.each(['x', 'y'], function (x, y, idx) {});
  1083. * list.each(function (idx) {})
  1084. */
  1085. listProto.each = function (dims, cb, context, contextCompat) {
  1086. 'use strict';
  1087. if (!this._count) {
  1088. return;
  1089. }
  1090. if (typeof dims === 'function') {
  1091. contextCompat = context;
  1092. context = cb;
  1093. cb = dims;
  1094. dims = [];
  1095. } // contextCompat just for compat echarts3
  1096. context = context || contextCompat || this;
  1097. dims = zrUtil.map(normalizeDimensions(dims), this.getDimension, this);
  1098. var dimSize = dims.length;
  1099. for (var i = 0; i < this.count(); i++) {
  1100. // Simple optimization
  1101. switch (dimSize) {
  1102. case 0:
  1103. cb.call(context, i);
  1104. break;
  1105. case 1:
  1106. cb.call(context, this.get(dims[0], i), i);
  1107. break;
  1108. case 2:
  1109. cb.call(context, this.get(dims[0], i), this.get(dims[1], i), i);
  1110. break;
  1111. default:
  1112. var k = 0;
  1113. var value = [];
  1114. for (; k < dimSize; k++) {
  1115. value[k] = this.get(dims[k], i);
  1116. } // Index
  1117. value[k] = i;
  1118. cb.apply(context, value);
  1119. }
  1120. }
  1121. };
  1122. /**
  1123. * Data filter
  1124. * @param {string|Array.<string>}
  1125. * @param {Function} cb
  1126. * @param {*} [context=this]
  1127. */
  1128. listProto.filterSelf = function (dimensions, cb, context, contextCompat) {
  1129. 'use strict';
  1130. if (!this._count) {
  1131. return;
  1132. }
  1133. if (typeof dimensions === 'function') {
  1134. contextCompat = context;
  1135. context = cb;
  1136. cb = dimensions;
  1137. dimensions = [];
  1138. } // contextCompat just for compat echarts3
  1139. context = context || contextCompat || this;
  1140. dimensions = zrUtil.map(normalizeDimensions(dimensions), this.getDimension, this);
  1141. var count = this.count();
  1142. var Ctor = getIndicesCtor(this);
  1143. var newIndices = new Ctor(count);
  1144. var value = [];
  1145. var dimSize = dimensions.length;
  1146. var offset = 0;
  1147. var dim0 = dimensions[0];
  1148. for (var i = 0; i < count; i++) {
  1149. var keep;
  1150. var rawIdx = this.getRawIndex(i); // Simple optimization
  1151. if (dimSize === 0) {
  1152. keep = cb.call(context, i);
  1153. } else if (dimSize === 1) {
  1154. var val = this._getFast(dim0, rawIdx);
  1155. keep = cb.call(context, val, i);
  1156. } else {
  1157. for (var k = 0; k < dimSize; k++) {
  1158. value[k] = this._getFast(dim0, rawIdx);
  1159. }
  1160. value[k] = i;
  1161. keep = cb.apply(context, value);
  1162. }
  1163. if (keep) {
  1164. newIndices[offset++] = rawIdx;
  1165. }
  1166. } // Set indices after filtered.
  1167. if (offset < count) {
  1168. this._indices = newIndices;
  1169. }
  1170. this._count = offset; // Reset data extent
  1171. this._extent = {};
  1172. this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  1173. return this;
  1174. };
  1175. /**
  1176. * Select data in range. (For optimization of filter)
  1177. * (Manually inline code, support 5 million data filtering in data zoom.)
  1178. */
  1179. listProto.selectRange = function (range) {
  1180. 'use strict';
  1181. if (!this._count) {
  1182. return;
  1183. }
  1184. var dimensions = [];
  1185. for (var dim in range) {
  1186. if (range.hasOwnProperty(dim)) {
  1187. dimensions.push(dim);
  1188. }
  1189. }
  1190. var dimSize = dimensions.length;
  1191. if (!dimSize) {
  1192. return;
  1193. }
  1194. var originalCount = this.count();
  1195. var Ctor = getIndicesCtor(this);
  1196. var newIndices = new Ctor(originalCount);
  1197. var offset = 0;
  1198. var dim0 = dimensions[0];
  1199. var min = range[dim0][0];
  1200. var max = range[dim0][1];
  1201. var quickFinished = false;
  1202. if (!this._indices) {
  1203. // Extreme optimization for common case. About 2x faster in chrome.
  1204. var idx = 0;
  1205. if (dimSize === 1) {
  1206. var dimStorage = this._storage[dimensions[0]];
  1207. for (var k = 0; k < this._chunkCount; k++) {
  1208. var chunkStorage = dimStorage[k];
  1209. var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
  1210. for (var i = 0; i < len; i++) {
  1211. var val = chunkStorage[i]; // NaN will not be filtered. Consider the case, in line chart, empty
  1212. // value indicates the line should be broken. But for the case like
  1213. // scatter plot, a data item with empty value will not be rendered,
  1214. // but the axis extent may be effected if some other dim of the data
  1215. // item has value. Fortunately it is not a significant negative effect.
  1216. if (val >= min && val <= max || isNaN(val)) {
  1217. newIndices[offset++] = idx;
  1218. }
  1219. idx++;
  1220. }
  1221. }
  1222. quickFinished = true;
  1223. } else if (dimSize === 2) {
  1224. var dimStorage = this._storage[dim0];
  1225. var dimStorage2 = this._storage[dimensions[1]];
  1226. var min2 = range[dimensions[1]][0];
  1227. var max2 = range[dimensions[1]][1];
  1228. for (var k = 0; k < this._chunkCount; k++) {
  1229. var chunkStorage = dimStorage[k];
  1230. var chunkStorage2 = dimStorage2[k];
  1231. var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
  1232. for (var i = 0; i < len; i++) {
  1233. var val = chunkStorage[i];
  1234. var val2 = chunkStorage2[i]; // Do not filter NaN, see comment above.
  1235. if ((val >= min && val <= max || isNaN(val)) && (val2 >= min2 && val2 <= max2 || isNaN(val2))) {
  1236. newIndices[offset++] = idx;
  1237. }
  1238. idx++;
  1239. }
  1240. }
  1241. quickFinished = true;
  1242. }
  1243. }
  1244. if (!quickFinished) {
  1245. if (dimSize === 1) {
  1246. for (var i = 0; i < originalCount; i++) {
  1247. var rawIndex = this.getRawIndex(i);
  1248. var val = this._getFast(dim0, rawIndex); // Do not filter NaN, see comment above.
  1249. if (val >= min && val <= max || isNaN(val)) {
  1250. newIndices[offset++] = rawIndex;
  1251. }
  1252. }
  1253. } else {
  1254. for (var i = 0; i < originalCount; i++) {
  1255. var keep = true;
  1256. var rawIndex = this.getRawIndex(i);
  1257. for (var k = 0; k < dimSize; k++) {
  1258. var dimk = dimensions[k];
  1259. var val = this._getFast(dim, rawIndex); // Do not filter NaN, see comment above.
  1260. if (val < range[dimk][0] || val > range[dimk][1]) {
  1261. keep = false;
  1262. }
  1263. }
  1264. if (keep) {
  1265. newIndices[offset++] = this.getRawIndex(i);
  1266. }
  1267. }
  1268. }
  1269. } // Set indices after filtered.
  1270. if (offset < originalCount) {
  1271. this._indices = newIndices;
  1272. }
  1273. this._count = offset; // Reset data extent
  1274. this._extent = {};
  1275. this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  1276. return this;
  1277. };
  1278. /**
  1279. * Data mapping to a plain array
  1280. * @param {string|Array.<string>} [dimensions]
  1281. * @param {Function} cb
  1282. * @param {*} [context=this]
  1283. * @return {Array}
  1284. */
  1285. listProto.mapArray = function (dimensions, cb, context, contextCompat) {
  1286. 'use strict';
  1287. if (typeof dimensions === 'function') {
  1288. contextCompat = context;
  1289. context = cb;
  1290. cb = dimensions;
  1291. dimensions = [];
  1292. } // contextCompat just for compat echarts3
  1293. context = context || contextCompat || this;
  1294. var result = [];
  1295. this.each(dimensions, function () {
  1296. result.push(cb && cb.apply(this, arguments));
  1297. }, context);
  1298. return result;
  1299. }; // Data in excludeDimensions is copied, otherwise transfered.
  1300. function cloneListForMapAndSample(original, excludeDimensions) {
  1301. var allDimensions = original.dimensions;
  1302. var list = new List(zrUtil.map(allDimensions, original.getDimensionInfo, original), original.hostModel); // FIXME If needs stackedOn, value may already been stacked
  1303. transferProperties(list, original);
  1304. var storage = list._storage = {};
  1305. var originalStorage = original._storage; // Init storage
  1306. for (var i = 0; i < allDimensions.length; i++) {
  1307. var dim = allDimensions[i];
  1308. if (originalStorage[dim]) {
  1309. // Notice that we do not reset invertedIndicesMap here, becuase
  1310. // there is no scenario of mapping or sampling ordinal dimension.
  1311. if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
  1312. storage[dim] = cloneDimStore(originalStorage[dim]);
  1313. list._rawExtent[dim] = getInitialExtent();
  1314. list._extent[dim] = null;
  1315. } else {
  1316. // Direct reference for other dimensions
  1317. storage[dim] = originalStorage[dim];
  1318. }
  1319. }
  1320. }
  1321. return list;
  1322. }
  1323. function cloneDimStore(originalDimStore) {
  1324. var newDimStore = new Array(originalDimStore.length);
  1325. for (var j = 0; j < originalDimStore.length; j++) {
  1326. newDimStore[j] = cloneChunk(originalDimStore[j]);
  1327. }
  1328. return newDimStore;
  1329. }
  1330. function getInitialExtent() {
  1331. return [Infinity, -Infinity];
  1332. }
  1333. /**
  1334. * Data mapping to a new List with given dimensions
  1335. * @param {string|Array.<string>} dimensions
  1336. * @param {Function} cb
  1337. * @param {*} [context=this]
  1338. * @return {Array}
  1339. */
  1340. listProto.map = function (dimensions, cb, context, contextCompat) {
  1341. 'use strict'; // contextCompat just for compat echarts3
  1342. context = context || contextCompat || this;
  1343. dimensions = zrUtil.map(normalizeDimensions(dimensions), this.getDimension, this);
  1344. var list = cloneListForMapAndSample(this, dimensions); // Following properties are all immutable.
  1345. // So we can reference to the same value
  1346. list._indices = this._indices;
  1347. list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  1348. var storage = list._storage;
  1349. var tmpRetValue = [];
  1350. var chunkSize = this._chunkSize;
  1351. var dimSize = dimensions.length;
  1352. var dataCount = this.count();
  1353. var values = [];
  1354. var rawExtent = list._rawExtent;
  1355. for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {
  1356. for (var dimIndex = 0; dimIndex < dimSize; dimIndex++) {
  1357. values[dimIndex] = this.get(dimensions[dimIndex], dataIndex
  1358. /*, stack */
  1359. );
  1360. }
  1361. values[dimSize] = dataIndex;
  1362. var retValue = cb && cb.apply(context, values);
  1363. if (retValue != null) {
  1364. // a number or string (in oridinal dimension)?
  1365. if (typeof retValue !== 'object') {
  1366. tmpRetValue[0] = retValue;
  1367. retValue = tmpRetValue;
  1368. }
  1369. var rawIndex = this.getRawIndex(dataIndex);
  1370. var chunkIndex = Math.floor(rawIndex / chunkSize);
  1371. var chunkOffset = rawIndex % chunkSize;
  1372. for (var i = 0; i < retValue.length; i++) {
  1373. var dim = dimensions[i];
  1374. var val = retValue[i];
  1375. var rawExtentOnDim = rawExtent[dim];
  1376. var dimStore = storage[dim];
  1377. if (dimStore) {
  1378. dimStore[chunkIndex][chunkOffset] = val;
  1379. }
  1380. if (val < rawExtentOnDim[0]) {
  1381. rawExtentOnDim[0] = val;
  1382. }
  1383. if (val > rawExtentOnDim[1]) {
  1384. rawExtentOnDim[1] = val;
  1385. }
  1386. }
  1387. }
  1388. }
  1389. return list;
  1390. };
  1391. /**
  1392. * Large data down sampling on given dimension
  1393. * @param {string} dimension
  1394. * @param {number} rate
  1395. * @param {Function} sampleValue
  1396. * @param {Function} sampleIndex Sample index for name and id
  1397. */
  1398. listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
  1399. var list = cloneListForMapAndSample(this, [dimension]);
  1400. var targetStorage = list._storage;
  1401. var frameValues = [];
  1402. var frameSize = Math.floor(1 / rate);
  1403. var dimStore = targetStorage[dimension];
  1404. var len = this.count();
  1405. var chunkSize = this._chunkSize;
  1406. var rawExtentOnDim = list._rawExtent[dimension];
  1407. var newIndices = new (getIndicesCtor(this))(len);
  1408. var offset = 0;
  1409. for (var i = 0; i < len; i += frameSize) {
  1410. // Last frame
  1411. if (frameSize > len - i) {
  1412. frameSize = len - i;
  1413. frameValues.length = frameSize;
  1414. }
  1415. for (var k = 0; k < frameSize; k++) {
  1416. var dataIdx = this.getRawIndex(i + k);
  1417. var originalChunkIndex = Math.floor(dataIdx / chunkSize);
  1418. var originalChunkOffset = dataIdx % chunkSize;
  1419. frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset];
  1420. }
  1421. var value = sampleValue(frameValues);
  1422. var sampleFrameIdx = this.getRawIndex(Math.min(i + sampleIndex(frameValues, value) || 0, len - 1));
  1423. var sampleChunkIndex = Math.floor(sampleFrameIdx / chunkSize);
  1424. var sampleChunkOffset = sampleFrameIdx % chunkSize; // Only write value on the filtered data
  1425. dimStore[sampleChunkIndex][sampleChunkOffset] = value;
  1426. if (value < rawExtentOnDim[0]) {
  1427. rawExtentOnDim[0] = value;
  1428. }
  1429. if (value > rawExtentOnDim[1]) {
  1430. rawExtentOnDim[1] = value;
  1431. }
  1432. newIndices[offset++] = sampleFrameIdx;
  1433. }
  1434. list._count = offset;
  1435. list._indices = newIndices;
  1436. list.getRawIndex = getRawIndexWithIndices;
  1437. return list;
  1438. };
  1439. /**
  1440. * Get model of one data item.
  1441. *
  1442. * @param {number} idx
  1443. */
  1444. // FIXME Model proxy ?
  1445. listProto.getItemModel = function (idx) {
  1446. var hostModel = this.hostModel;
  1447. return new Model(this.getRawDataItem(idx), hostModel, hostModel && hostModel.ecModel);
  1448. };
  1449. /**
  1450. * Create a data differ
  1451. * @param {module:echarts/data/List} otherList
  1452. * @return {module:echarts/data/DataDiffer}
  1453. */
  1454. listProto.diff = function (otherList) {
  1455. var thisList = this;
  1456. return new DataDiffer(otherList ? otherList.getIndices() : [], this.getIndices(), function (idx) {
  1457. return getId(otherList, idx);
  1458. }, function (idx) {
  1459. return getId(thisList, idx);
  1460. });
  1461. };
  1462. /**
  1463. * Get visual property.
  1464. * @param {string} key
  1465. */
  1466. listProto.getVisual = function (key) {
  1467. var visual = this._visual;
  1468. return visual && visual[key];
  1469. };
  1470. /**
  1471. * Set visual property
  1472. * @param {string|Object} key
  1473. * @param {*} [value]
  1474. *
  1475. * @example
  1476. * setVisual('color', color);
  1477. * setVisual({
  1478. * 'color': color
  1479. * });
  1480. */
  1481. listProto.setVisual = function (key, val) {
  1482. if (isObject(key)) {
  1483. for (var name in key) {
  1484. if (key.hasOwnProperty(name)) {
  1485. this.setVisual(name, key[name]);
  1486. }
  1487. }
  1488. return;
  1489. }
  1490. this._visual = this._visual || {};
  1491. this._visual[key] = val;
  1492. };
  1493. /**
  1494. * Set layout property.
  1495. * @param {string|Object} key
  1496. * @param {*} [val]
  1497. */
  1498. listProto.setLayout = function (key, val) {
  1499. if (isObject(key)) {
  1500. for (var name in key) {
  1501. if (key.hasOwnProperty(name)) {
  1502. this.setLayout(name, key[name]);
  1503. }
  1504. }
  1505. return;
  1506. }
  1507. this._layout[key] = val;
  1508. };
  1509. /**
  1510. * Get layout property.
  1511. * @param {string} key.
  1512. * @return {*}
  1513. */
  1514. listProto.getLayout = function (key) {
  1515. return this._layout[key];
  1516. };
  1517. /**
  1518. * Get layout of single data item
  1519. * @param {number} idx
  1520. */
  1521. listProto.getItemLayout = function (idx) {
  1522. return this._itemLayouts[idx];
  1523. };
  1524. /**
  1525. * Set layout of single data item
  1526. * @param {number} idx
  1527. * @param {Object} layout
  1528. * @param {boolean=} [merge=false]
  1529. */
  1530. listProto.setItemLayout = function (idx, layout, merge) {
  1531. this._itemLayouts[idx] = merge ? zrUtil.extend(this._itemLayouts[idx] || {}, layout) : layout;
  1532. };
  1533. /**
  1534. * Clear all layout of single data item
  1535. */
  1536. listProto.clearItemLayouts = function () {
  1537. this._itemLayouts.length = 0;
  1538. };
  1539. /**
  1540. * Get visual property of single data item
  1541. * @param {number} idx
  1542. * @param {string} key
  1543. * @param {boolean} [ignoreParent=false]
  1544. */
  1545. listProto.getItemVisual = function (idx, key, ignoreParent) {
  1546. var itemVisual = this._itemVisuals[idx];
  1547. var val = itemVisual && itemVisual[key];
  1548. if (val == null && !ignoreParent) {
  1549. // Use global visual property
  1550. return this.getVisual(key);
  1551. }
  1552. return val;
  1553. };
  1554. /**
  1555. * Set visual property of single data item
  1556. *
  1557. * @param {number} idx
  1558. * @param {string|Object} key
  1559. * @param {*} [value]
  1560. *
  1561. * @example
  1562. * setItemVisual(0, 'color', color);
  1563. * setItemVisual(0, {
  1564. * 'color': color
  1565. * });
  1566. */
  1567. listProto.setItemVisual = function (idx, key, value) {
  1568. var itemVisual = this._itemVisuals[idx] || {};
  1569. var hasItemVisual = this.hasItemVisual;
  1570. this._itemVisuals[idx] = itemVisual;
  1571. if (isObject(key)) {
  1572. for (var name in key) {
  1573. if (key.hasOwnProperty(name)) {
  1574. itemVisual[name] = key[name];
  1575. hasItemVisual[name] = true;
  1576. }
  1577. }
  1578. return;
  1579. }
  1580. itemVisual[key] = value;
  1581. hasItemVisual[key] = true;
  1582. };
  1583. /**
  1584. * Clear itemVisuals and list visual.
  1585. */
  1586. listProto.clearAllVisual = function () {
  1587. this._visual = {};
  1588. this._itemVisuals = [];
  1589. this.hasItemVisual = {};
  1590. };
  1591. var setItemDataAndSeriesIndex = function (child) {
  1592. child.seriesIndex = this.seriesIndex;
  1593. child.dataIndex = this.dataIndex;
  1594. child.dataType = this.dataType;
  1595. };
  1596. /**
  1597. * Set graphic element relative to data. It can be set as null
  1598. * @param {number} idx
  1599. * @param {module:zrender/Element} [el]
  1600. */
  1601. listProto.setItemGraphicEl = function (idx, el) {
  1602. var hostModel = this.hostModel;
  1603. if (el) {
  1604. // Add data index and series index for indexing the data by element
  1605. // Useful in tooltip
  1606. el.dataIndex = idx;
  1607. el.dataType = this.dataType;
  1608. el.seriesIndex = hostModel && hostModel.seriesIndex;
  1609. if (el.type === 'group') {
  1610. el.traverse(setItemDataAndSeriesIndex, el);
  1611. }
  1612. }
  1613. this._graphicEls[idx] = el;
  1614. };
  1615. /**
  1616. * @param {number} idx
  1617. * @return {module:zrender/Element}
  1618. */
  1619. listProto.getItemGraphicEl = function (idx) {
  1620. return this._graphicEls[idx];
  1621. };
  1622. /**
  1623. * @param {Function} cb
  1624. * @param {*} context
  1625. */
  1626. listProto.eachItemGraphicEl = function (cb, context) {
  1627. zrUtil.each(this._graphicEls, function (el, idx) {
  1628. if (el) {
  1629. cb && cb.call(context, el, idx);
  1630. }
  1631. });
  1632. };
  1633. /**
  1634. * Shallow clone a new list except visual and layout properties, and graph elements.
  1635. * New list only change the indices.
  1636. */
  1637. listProto.cloneShallow = function (list) {
  1638. if (!list) {
  1639. var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
  1640. list = new List(dimensionInfoList, this.hostModel);
  1641. } // FIXME
  1642. list._storage = this._storage;
  1643. transferProperties(list, this); // Clone will not change the data extent and indices
  1644. if (this._indices) {
  1645. var Ctor = this._indices.constructor;
  1646. list._indices = new Ctor(this._indices);
  1647. } else {
  1648. list._indices = null;
  1649. }
  1650. list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  1651. return list;
  1652. };
  1653. /**
  1654. * Wrap some method to add more feature
  1655. * @param {string} methodName
  1656. * @param {Function} injectFunction
  1657. */
  1658. listProto.wrapMethod = function (methodName, injectFunction) {
  1659. var originalMethod = this[methodName];
  1660. if (typeof originalMethod !== 'function') {
  1661. return;
  1662. }
  1663. this.__wrappedMethods = this.__wrappedMethods || [];
  1664. this.__wrappedMethods.push(methodName);
  1665. this[methodName] = function () {
  1666. var res = originalMethod.apply(this, arguments);
  1667. return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments)));
  1668. };
  1669. }; // Methods that create a new list based on this list should be listed here.
  1670. // Notice that those method should `RETURN` the new list.
  1671. listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map']; // Methods that change indices of this list should be listed here.
  1672. listProto.CHANGABLE_METHODS = ['filterSelf', 'selectRange'];
  1673. var _default = List;
  1674. module.exports = _default;