HeatmapView.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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 { __extends } from "tslib";
  41. import * as graphic from '../../util/graphic.js';
  42. import { toggleHoverEmphasis } from '../../util/states.js';
  43. import HeatmapLayer from './HeatmapLayer.js';
  44. import * as zrUtil from 'zrender/lib/core/util.js';
  45. import ChartView from '../../view/Chart.js';
  46. import { isCoordinateSystemType, isGeoLikeCoordSys } from '../../coord/CoordinateSystem.js';
  47. import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle.js';
  48. import { calcBandWidth } from '../../coord/axisBand.js';
  49. import { getIncrementalId } from '../../util/model.js';
  50. function getIsInPiecewiseRange(dataExtent, pieceList, selected) {
  51. var dataSpan = dataExtent[1] - dataExtent[0];
  52. pieceList = zrUtil.map(pieceList, function (piece) {
  53. return {
  54. interval: [(piece.interval[0] - dataExtent[0]) / dataSpan, (piece.interval[1] - dataExtent[0]) / dataSpan]
  55. };
  56. });
  57. var len = pieceList.length;
  58. var lastIndex = 0;
  59. return function (val) {
  60. var i;
  61. // Try to find in the location of the last found
  62. for (i = lastIndex; i < len; i++) {
  63. var interval = pieceList[i].interval;
  64. if (interval[0] <= val && val <= interval[1]) {
  65. lastIndex = i;
  66. break;
  67. }
  68. }
  69. if (i === len) {
  70. // Not found, back interation
  71. for (i = lastIndex - 1; i >= 0; i--) {
  72. var interval = pieceList[i].interval;
  73. if (interval[0] <= val && val <= interval[1]) {
  74. lastIndex = i;
  75. break;
  76. }
  77. }
  78. }
  79. return i >= 0 && i < len && selected[i];
  80. };
  81. }
  82. function getIsInContinuousRange(dataExtent, range) {
  83. var dataSpan = dataExtent[1] - dataExtent[0];
  84. range = [(range[0] - dataExtent[0]) / dataSpan, (range[1] - dataExtent[0]) / dataSpan];
  85. return function (val) {
  86. return val >= range[0] && val <= range[1];
  87. };
  88. }
  89. var HeatmapView = /** @class */function (_super) {
  90. __extends(HeatmapView, _super);
  91. function HeatmapView() {
  92. var _this = _super !== null && _super.apply(this, arguments) || this;
  93. _this.type = HeatmapView.type;
  94. return _this;
  95. }
  96. HeatmapView.prototype.render = function (seriesModel, ecModel, api) {
  97. var visualMapOfThisSeries;
  98. ecModel.eachComponent('visualMap', function (visualMap) {
  99. visualMap.eachTargetSeries(function (targetSeries) {
  100. if (targetSeries === seriesModel) {
  101. visualMapOfThisSeries = visualMap;
  102. }
  103. });
  104. });
  105. if (process.env.NODE_ENV !== 'production') {
  106. if (!visualMapOfThisSeries) {
  107. throw new Error('Heatmap must use with visualMap');
  108. }
  109. }
  110. // Clear previously rendered progressive elements.
  111. this._progressiveEls = null;
  112. this.group.removeAll();
  113. var coordSys = seriesModel.coordinateSystem;
  114. if (coordSys.type === 'cartesian2d' || coordSys.type === 'calendar' || coordSys.type === 'matrix') {
  115. this._renderOnGridLike(seriesModel, api, 0, seriesModel.getData().count());
  116. } else if (isGeoLikeCoordSys(coordSys)) {
  117. this._renderOnGeo(coordSys, seriesModel, visualMapOfThisSeries, api);
  118. }
  119. };
  120. HeatmapView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) {
  121. this.group.removeAll();
  122. };
  123. HeatmapView.prototype.incrementalRender = function (params, seriesModel, ecModel, api) {
  124. var coordSys = seriesModel.coordinateSystem;
  125. if (coordSys) {
  126. // geo does not support incremental rendering?
  127. if (isGeoLikeCoordSys(coordSys)) {
  128. this.render(seriesModel, ecModel, api);
  129. } else {
  130. this._progressiveEls = [];
  131. this._renderOnGridLike(seriesModel, api, params.start, params.end, true);
  132. }
  133. }
  134. };
  135. HeatmapView.prototype.eachRendered = function (cb) {
  136. graphic.traverseElements(this._progressiveEls || this.group, cb);
  137. };
  138. HeatmapView.prototype._renderOnGridLike = function (seriesModel, api, start, end, useIncremental) {
  139. var coordSys = seriesModel.coordinateSystem;
  140. var isCartesian2d = isCoordinateSystemType(coordSys, 'cartesian2d');
  141. var isMatrix = isCoordinateSystemType(coordSys, 'matrix');
  142. var width;
  143. var height;
  144. var xAxisExtent;
  145. var yAxisExtent;
  146. if (isCartesian2d) {
  147. var xAxis = coordSys.getAxis('x');
  148. var yAxis = coordSys.getAxis('y');
  149. if (process.env.NODE_ENV !== 'production') {
  150. if (!(xAxis.type === 'category' && yAxis.type === 'category')) {
  151. throw new Error('Heatmap on cartesian must have two category axes');
  152. }
  153. if (!(xAxis.onBand && yAxis.onBand)) {
  154. throw new Error('Heatmap on cartesian must have two axes with boundaryGap true');
  155. }
  156. }
  157. // add 0.5px to avoid the gaps
  158. width = calcBandWidth(xAxis).w + .5;
  159. height = calcBandWidth(yAxis).w + .5;
  160. xAxisExtent = xAxis.scale.getExtent();
  161. yAxisExtent = yAxis.scale.getExtent();
  162. }
  163. var group = this.group;
  164. var data = seriesModel.getData();
  165. var emphasisStyle = seriesModel.getModel(['emphasis', 'itemStyle']).getItemStyle();
  166. var blurStyle = seriesModel.getModel(['blur', 'itemStyle']).getItemStyle();
  167. var selectStyle = seriesModel.getModel(['select', 'itemStyle']).getItemStyle();
  168. var borderRadius = seriesModel.get(['itemStyle', 'borderRadius']);
  169. var labelStatesModels = getLabelStatesModels(seriesModel);
  170. var emphasisModel = seriesModel.getModel('emphasis');
  171. var focus = emphasisModel.get('focus');
  172. var blurScope = emphasisModel.get('blurScope');
  173. var emphasisDisabled = emphasisModel.get('disabled');
  174. var dataDims = isCartesian2d || isMatrix ? [data.mapDimension('x'), data.mapDimension('y'), data.mapDimension('value')] : [data.mapDimension('time'), data.mapDimension('value')];
  175. for (var idx = start; idx < end; idx++) {
  176. var rect = void 0;
  177. var style = data.getItemVisual(idx, 'style');
  178. if (isCartesian2d) {
  179. var dataDimX = data.get(dataDims[0], idx);
  180. var dataDimY = data.get(dataDims[1], idx);
  181. // Ignore empty data and out of extent data
  182. if (isNaN(data.get(dataDims[2], idx)) || isNaN(dataDimX) || isNaN(dataDimY) || dataDimX < xAxisExtent[0] || dataDimX > xAxisExtent[1] || dataDimY < yAxisExtent[0] || dataDimY > yAxisExtent[1]) {
  183. continue;
  184. }
  185. var point = coordSys.dataToPoint([dataDimX, dataDimY]);
  186. rect = new graphic.Rect({
  187. shape: {
  188. x: point[0] - width / 2,
  189. y: point[1] - height / 2,
  190. width: width,
  191. height: height
  192. },
  193. style: style
  194. });
  195. } else if (isMatrix) {
  196. var shape = coordSys.dataToLayout([data.get(dataDims[0], idx), data.get(dataDims[1], idx)]).rect;
  197. if (zrUtil.eqNaN(shape.x)) {
  198. continue;
  199. }
  200. rect = new graphic.Rect({
  201. z2: 1,
  202. shape: shape,
  203. style: style
  204. });
  205. } else {
  206. // Calendar
  207. // Ignore empty data
  208. if (isNaN(data.get(dataDims[1], idx))) {
  209. continue;
  210. }
  211. var layout = coordSys.dataToLayout([data.get(dataDims[0], idx)]);
  212. var shape = layout.contentRect || layout.rect;
  213. if (zrUtil.eqNaN(shape.x) || zrUtil.eqNaN(shape.y)) {
  214. continue;
  215. }
  216. rect = new graphic.Rect({
  217. z2: 1,
  218. shape: shape,
  219. style: style
  220. });
  221. }
  222. // Optimization for large dataset
  223. if (data.hasItemOption) {
  224. var itemModel = data.getItemModel(idx);
  225. var emphasisModel_1 = itemModel.getModel('emphasis');
  226. emphasisStyle = emphasisModel_1.getModel('itemStyle').getItemStyle();
  227. blurStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle();
  228. selectStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle();
  229. // Each item value struct in the data would be firstly
  230. // {
  231. // itemStyle: { borderRadius: [30, 30] },
  232. // value: [2022, 02, 22]
  233. // }
  234. borderRadius = itemModel.get(['itemStyle', 'borderRadius']);
  235. focus = emphasisModel_1.get('focus');
  236. blurScope = emphasisModel_1.get('blurScope');
  237. emphasisDisabled = emphasisModel_1.get('disabled');
  238. labelStatesModels = getLabelStatesModels(itemModel);
  239. }
  240. rect.shape.r = borderRadius;
  241. var rawValue = seriesModel.getRawValue(idx);
  242. var defaultText = '-';
  243. if (rawValue && rawValue[2] != null) {
  244. defaultText = rawValue[2] + '';
  245. }
  246. setLabelStyle(rect, labelStatesModels, {
  247. labelFetcher: seriesModel,
  248. labelDataIndex: idx,
  249. defaultOpacity: style.opacity,
  250. defaultText: defaultText
  251. });
  252. rect.ensureState('emphasis').style = emphasisStyle;
  253. rect.ensureState('blur').style = blurStyle;
  254. rect.ensureState('select').style = selectStyle;
  255. toggleHoverEmphasis(rect, focus, blurScope, emphasisDisabled);
  256. rect.incremental = getIncrementalId(seriesModel, useIncremental);
  257. // PENDING
  258. if (useIncremental) {
  259. // Rect must use hover layer if it's incremental.
  260. rect.states.emphasis.hoverLayer = graphic.HOVER_LAYER_FOR_INCREMENTAL;
  261. }
  262. group.add(rect);
  263. data.setItemGraphicEl(idx, rect);
  264. if (this._progressiveEls) {
  265. this._progressiveEls.push(rect);
  266. }
  267. }
  268. };
  269. HeatmapView.prototype._renderOnGeo = function (geo, seriesModel, visualMapModel, api) {
  270. var inRangeVisuals = visualMapModel.targetVisuals.inRange;
  271. var outOfRangeVisuals = visualMapModel.targetVisuals.outOfRange;
  272. // if (!visualMapping) {
  273. // throw new Error('Data range must have color visuals');
  274. // }
  275. var data = seriesModel.getData();
  276. var hmLayer = this._hmLayer || this._hmLayer || new HeatmapLayer();
  277. hmLayer.blurSize = seriesModel.get('blurSize');
  278. hmLayer.pointSize = seriesModel.get('pointSize');
  279. hmLayer.minOpacity = seriesModel.get('minOpacity');
  280. hmLayer.maxOpacity = seriesModel.get('maxOpacity');
  281. var rect = geo.getViewRect().clone();
  282. var roamTransform = geo.getRoamTransform();
  283. rect.applyTransform(roamTransform);
  284. // Clamp on viewport
  285. var x = Math.max(rect.x, 0);
  286. var y = Math.max(rect.y, 0);
  287. var x2 = Math.min(rect.width + rect.x, api.getWidth());
  288. var y2 = Math.min(rect.height + rect.y, api.getHeight());
  289. var width = x2 - x;
  290. var height = y2 - y;
  291. var dims = [data.mapDimension('lng'), data.mapDimension('lat'), data.mapDimension('value')];
  292. var points = data.mapArray(dims, function (lng, lat, value) {
  293. var pt = geo.dataToPoint([lng, lat]);
  294. pt[0] -= x;
  295. pt[1] -= y;
  296. pt.push(value);
  297. return pt;
  298. });
  299. var dataExtent = visualMapModel.getExtent();
  300. var isInRange = visualMapModel.type === 'visualMap.continuous' ? getIsInContinuousRange(dataExtent, visualMapModel.option.range) : getIsInPiecewiseRange(dataExtent, visualMapModel.getPieceList(), visualMapModel.option.selected);
  301. hmLayer.update(points, width, height, inRangeVisuals.color.getNormalizer(), {
  302. inRange: inRangeVisuals.color.getColorMapper(),
  303. outOfRange: outOfRangeVisuals.color.getColorMapper()
  304. }, isInRange);
  305. var img = new graphic.Image({
  306. style: {
  307. width: width,
  308. height: height,
  309. x: x,
  310. y: y,
  311. image: hmLayer.canvas
  312. },
  313. silent: true
  314. });
  315. this.group.add(img);
  316. };
  317. HeatmapView.type = 'heatmap';
  318. return HeatmapView;
  319. }(ChartView);
  320. export default HeatmapView;