dataProvider.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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 _util = require("zrender/lib/core/util");
  22. var isTypedArray = _util.isTypedArray;
  23. var extend = _util.extend;
  24. var assert = _util.assert;
  25. var each = _util.each;
  26. var isObject = _util.isObject;
  27. var _model = require("../../util/model");
  28. var getDataItemValue = _model.getDataItemValue;
  29. var isDataItemOption = _model.isDataItemOption;
  30. var _number = require("../../util/number");
  31. var parseDate = _number.parseDate;
  32. var Source = require("../Source");
  33. var _sourceType = require("./sourceType");
  34. var SOURCE_FORMAT_TYPED_ARRAY = _sourceType.SOURCE_FORMAT_TYPED_ARRAY;
  35. var SOURCE_FORMAT_ARRAY_ROWS = _sourceType.SOURCE_FORMAT_ARRAY_ROWS;
  36. var SOURCE_FORMAT_ORIGINAL = _sourceType.SOURCE_FORMAT_ORIGINAL;
  37. var SOURCE_FORMAT_OBJECT_ROWS = _sourceType.SOURCE_FORMAT_OBJECT_ROWS;
  38. /*
  39. * Licensed to the Apache Software Foundation (ASF) under one
  40. * or more contributor license agreements. See the NOTICE file
  41. * distributed with this work for additional information
  42. * regarding copyright ownership. The ASF licenses this file
  43. * to you under the Apache License, Version 2.0 (the
  44. * "License"); you may not use this file except in compliance
  45. * with the License. You may obtain a copy of the License at
  46. *
  47. * http://www.apache.org/licenses/LICENSE-2.0
  48. *
  49. * Unless required by applicable law or agreed to in writing,
  50. * software distributed under the License is distributed on an
  51. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  52. * KIND, either express or implied. See the License for the
  53. * specific language governing permissions and limitations
  54. * under the License.
  55. */
  56. // TODO
  57. // ??? refactor? check the outer usage of data provider.
  58. // merge with defaultDimValueGetter?
  59. /**
  60. * If normal array used, mutable chunk size is supported.
  61. * If typed array used, chunk size must be fixed.
  62. */
  63. function DefaultDataProvider(source, dimSize) {
  64. if (!Source.isInstance(source)) {
  65. source = Source.seriesDataToSource(source);
  66. }
  67. this._source = source;
  68. var data = this._data = source.data;
  69. var sourceFormat = source.sourceFormat; // Typed array. TODO IE10+?
  70. if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
  71. this._offset = 0;
  72. this._dimSize = dimSize;
  73. this._data = data;
  74. }
  75. var methods = providerMethods[sourceFormat === SOURCE_FORMAT_ARRAY_ROWS ? sourceFormat + '_' + source.seriesLayoutBy : sourceFormat];
  76. extend(this, methods);
  77. }
  78. var providerProto = DefaultDataProvider.prototype; // If data is pure without style configuration
  79. providerProto.pure = false; // If data is persistent and will not be released after use.
  80. providerProto.persistent = true; // ???! FIXME legacy data provider do not has method getSource
  81. providerProto.getSource = function () {
  82. return this._source;
  83. };
  84. var providerMethods = {
  85. 'arrayRows_column': {
  86. pure: true,
  87. count: function () {
  88. return Math.max(0, this._data.length - this._source.startIndex);
  89. },
  90. getItem: function (idx) {
  91. return this._data[idx + this._source.startIndex];
  92. },
  93. appendData: appendDataSimply
  94. },
  95. 'arrayRows_row': {
  96. pure: true,
  97. count: function () {
  98. var row = this._data[0];
  99. return row ? Math.max(0, row.length - this._source.startIndex) : 0;
  100. },
  101. getItem: function (idx) {
  102. idx += this._source.startIndex;
  103. var item = [];
  104. var data = this._data;
  105. for (var i = 0; i < data.length; i++) {
  106. var row = data[i];
  107. item.push(row ? row[idx] : null);
  108. }
  109. return item;
  110. },
  111. appendData: function () {
  112. throw new Error('Do not support appendData when set seriesLayoutBy: "row".');
  113. }
  114. },
  115. 'objectRows': {
  116. pure: true,
  117. count: countSimply,
  118. getItem: getItemSimply,
  119. appendData: appendDataSimply
  120. },
  121. 'keyedColumns': {
  122. pure: true,
  123. count: function () {
  124. var dimName = this._source.dimensionsDefine[0].name;
  125. var col = this._data[dimName];
  126. return col ? col.length : 0;
  127. },
  128. getItem: function (idx) {
  129. var item = [];
  130. var dims = this._source.dimensionsDefine;
  131. for (var i = 0; i < dims.length; i++) {
  132. var col = this._data[dims[i].name];
  133. item.push(col ? col[idx] : null);
  134. }
  135. return item;
  136. },
  137. appendData: function (newData) {
  138. var data = this._data;
  139. each(newData, function (newCol, key) {
  140. var oldCol = data[key] || (data[key] = []);
  141. for (var i = 0; i < (newCol || []).length; i++) {
  142. oldCol.push(newCol[i]);
  143. }
  144. });
  145. }
  146. },
  147. 'original': {
  148. count: countSimply,
  149. getItem: getItemSimply,
  150. appendData: appendDataSimply
  151. },
  152. 'typedArray': {
  153. persistent: false,
  154. pure: true,
  155. count: function () {
  156. return this._data ? this._data.length / this._dimSize : 0;
  157. },
  158. getItem: function (idx, out) {
  159. idx = idx - this._offset;
  160. out = out || [];
  161. var offset = this._dimSize * idx;
  162. for (var i = 0; i < this._dimSize; i++) {
  163. out[i] = this._data[offset + i];
  164. }
  165. return out;
  166. },
  167. appendData: function (newData) {
  168. this._data = newData;
  169. },
  170. // Clean self if data is already used.
  171. clean: function () {
  172. // PENDING
  173. this._offset += this.count();
  174. this._data = null;
  175. }
  176. }
  177. };
  178. function countSimply() {
  179. return this._data.length;
  180. }
  181. function getItemSimply(idx) {
  182. return this._data[idx];
  183. }
  184. function appendDataSimply(newData) {
  185. for (var i = 0; i < newData.length; i++) {
  186. this._data.push(newData[i]);
  187. }
  188. }
  189. var rawValueGetters = {
  190. arrayRows: getRawValueSimply,
  191. objectRows: function (dataItem, dataIndex, dimIndex, dimName) {
  192. return dimIndex != null ? dataItem[dimName] : dataItem;
  193. },
  194. keyedColumns: getRawValueSimply,
  195. original: function (dataItem, dataIndex, dimIndex, dimName) {
  196. // FIXME
  197. // In some case (markpoint in geo (geo-map.html)), dataItem
  198. // is {coord: [...]}
  199. var value = getDataItemValue(dataItem);
  200. return dimIndex == null || !(value instanceof Array) ? value : value[dimIndex];
  201. },
  202. typedArray: getRawValueSimply
  203. };
  204. function getRawValueSimply(dataItem, dataIndex, dimIndex, dimName) {
  205. return dimIndex != null ? dataItem[dimIndex] : dataItem;
  206. }
  207. var defaultDimValueGetters = {
  208. arrayRows: getDimValueSimply,
  209. objectRows: function (dataItem, dimName, dataIndex, dimIndex) {
  210. return converDataValue(dataItem[dimName], this._dimensionInfos[dimName]);
  211. },
  212. keyedColumns: getDimValueSimply,
  213. original: function (dataItem, dimName, dataIndex, dimIndex) {
  214. // Performance sensitive, do not use modelUtil.getDataItemValue.
  215. // If dataItem is an plain object with no value field, the var `value`
  216. // will be assigned with the object, but it will be tread correctly
  217. // in the `convertDataValue`.
  218. var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value); // If any dataItem is like { value: 10 }
  219. if (!this._rawData.pure && isDataItemOption(dataItem)) {
  220. this.hasItemOption = true;
  221. }
  222. return converDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array.
  223. : value, this._dimensionInfos[dimName]);
  224. },
  225. typedArray: function (dataItem, dimName, dataIndex, dimIndex) {
  226. return dataItem[dimIndex];
  227. }
  228. };
  229. function getDimValueSimply(dataItem, dimName, dataIndex, dimIndex) {
  230. return converDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]);
  231. }
  232. /**
  233. * This helper method convert value in data.
  234. * @param {string|number|Date} value
  235. * @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'.
  236. * If "dimInfo.ordinalParseAndSave", ordinal value can be parsed.
  237. */
  238. function converDataValue(value, dimInfo) {
  239. // Performance sensitive.
  240. var dimType = dimInfo && dimInfo.type;
  241. if (dimType === 'ordinal') {
  242. // If given value is a category string
  243. var ordinalMeta = dimInfo && dimInfo.ordinalMeta;
  244. return ordinalMeta ? ordinalMeta.parseAndCollect(value) : value;
  245. }
  246. if (dimType === 'time' // spead up when using timestamp
  247. && typeof value !== 'number' && value != null && value !== '-') {
  248. value = +parseDate(value);
  249. } // dimType defaults 'number'.
  250. // If dimType is not ordinal and value is null or undefined or NaN or '-',
  251. // parse to NaN.
  252. return value == null || value === '' ? NaN // If string (like '-'), using '+' parse to NaN
  253. // If object, also parse to NaN
  254. : +value;
  255. } // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem,
  256. // Consider persistent.
  257. // Caution: why use raw value to display on label or tooltip?
  258. // A reason is to avoid format. For example time value we do not know
  259. // how to format is expected. More over, if stack is used, calculated
  260. // value may be 0.91000000001, which have brings trouble to display.
  261. // TODO: consider how to treat null/undefined/NaN when display?
  262. /**
  263. * @param {module:echarts/data/List} data
  264. * @param {number} dataIndex
  265. * @param {string|number} [dim] dimName or dimIndex
  266. * @return {Array.<number>|string|number} can be null/undefined.
  267. */
  268. function retrieveRawValue(data, dataIndex, dim) {
  269. if (!data) {
  270. return;
  271. } // Consider data may be not persistent.
  272. var dataItem = data.getRawDataItem(dataIndex);
  273. if (dataItem == null) {
  274. return;
  275. }
  276. var sourceFormat = data.getProvider().getSource().sourceFormat;
  277. var dimName;
  278. var dimIndex;
  279. var dimInfo = data.getDimensionInfo(dim);
  280. if (dimInfo) {
  281. dimName = dimInfo.name;
  282. dimIndex = dimInfo.index;
  283. }
  284. return rawValueGetters[sourceFormat](dataItem, dataIndex, dimIndex, dimName);
  285. }
  286. /**
  287. * Compatible with some cases (in pie, map) like:
  288. * data: [{name: 'xx', value: 5, selected: true}, ...]
  289. * where only sourceFormat is 'original' and 'objectRows' supported.
  290. *
  291. * ??? TODO
  292. * Supported detail options in data item when using 'arrayRows'.
  293. *
  294. * @param {module:echarts/data/List} data
  295. * @param {number} dataIndex
  296. * @param {string} attr like 'selected'
  297. */
  298. function retrieveRawAttr(data, dataIndex, attr) {
  299. if (!data) {
  300. return;
  301. }
  302. var sourceFormat = data.getProvider().getSource().sourceFormat;
  303. if (sourceFormat !== SOURCE_FORMAT_ORIGINAL && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS) {
  304. return;
  305. }
  306. var dataItem = data.getRawDataItem(dataIndex);
  307. if (sourceFormat === SOURCE_FORMAT_ORIGINAL && !isObject(dataItem)) {
  308. dataItem = null;
  309. }
  310. if (dataItem) {
  311. return dataItem[attr];
  312. }
  313. }
  314. exports.DefaultDataProvider = DefaultDataProvider;
  315. exports.defaultDimValueGetters = defaultDimValueGetters;
  316. exports.retrieveRawValue = retrieveRawValue;
  317. exports.retrieveRawAttr = retrieveRawAttr;