HeatmapView.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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 echarts = require("../../echarts");
  22. var graphic = require("../../util/graphic");
  23. var HeatmapLayer = require("./HeatmapLayer");
  24. var zrUtil = require("zrender/lib/core/util");
  25. /*
  26. * Licensed to the Apache Software Foundation (ASF) under one
  27. * or more contributor license agreements. See the NOTICE file
  28. * distributed with this work for additional information
  29. * regarding copyright ownership. The ASF licenses this file
  30. * to you under the Apache License, Version 2.0 (the
  31. * "License"); you may not use this file except in compliance
  32. * with the License. You may obtain a copy of the License at
  33. *
  34. * http://www.apache.org/licenses/LICENSE-2.0
  35. *
  36. * Unless required by applicable law or agreed to in writing,
  37. * software distributed under the License is distributed on an
  38. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  39. * KIND, either express or implied. See the License for the
  40. * specific language governing permissions and limitations
  41. * under the License.
  42. */
  43. function getIsInPiecewiseRange(dataExtent, pieceList, selected) {
  44. var dataSpan = dataExtent[1] - dataExtent[0];
  45. pieceList = zrUtil.map(pieceList, function (piece) {
  46. return {
  47. interval: [(piece.interval[0] - dataExtent[0]) / dataSpan, (piece.interval[1] - dataExtent[0]) / dataSpan]
  48. };
  49. });
  50. var len = pieceList.length;
  51. var lastIndex = 0;
  52. return function (val) {
  53. // Try to find in the location of the last found
  54. for (var i = lastIndex; i < len; i++) {
  55. var interval = pieceList[i].interval;
  56. if (interval[0] <= val && val <= interval[1]) {
  57. lastIndex = i;
  58. break;
  59. }
  60. }
  61. if (i === len) {
  62. // Not found, back interation
  63. for (var i = lastIndex - 1; i >= 0; i--) {
  64. var interval = pieceList[i].interval;
  65. if (interval[0] <= val && val <= interval[1]) {
  66. lastIndex = i;
  67. break;
  68. }
  69. }
  70. }
  71. return i >= 0 && i < len && selected[i];
  72. };
  73. }
  74. function getIsInContinuousRange(dataExtent, range) {
  75. var dataSpan = dataExtent[1] - dataExtent[0];
  76. range = [(range[0] - dataExtent[0]) / dataSpan, (range[1] - dataExtent[0]) / dataSpan];
  77. return function (val) {
  78. return val >= range[0] && val <= range[1];
  79. };
  80. }
  81. function isGeoCoordSys(coordSys) {
  82. var dimensions = coordSys.dimensions; // Not use coorSys.type === 'geo' because coordSys maybe extended
  83. return dimensions[0] === 'lng' && dimensions[1] === 'lat';
  84. }
  85. var _default = echarts.extendChartView({
  86. type: 'heatmap',
  87. render: function (seriesModel, ecModel, api) {
  88. var visualMapOfThisSeries;
  89. ecModel.eachComponent('visualMap', function (visualMap) {
  90. visualMap.eachTargetSeries(function (targetSeries) {
  91. if (targetSeries === seriesModel) {
  92. visualMapOfThisSeries = visualMap;
  93. }
  94. });
  95. });
  96. this.group.removeAll();
  97. this._incrementalDisplayable = null;
  98. var coordSys = seriesModel.coordinateSystem;
  99. if (coordSys.type === 'cartesian2d' || coordSys.type === 'calendar') {
  100. this._renderOnCartesianAndCalendar(seriesModel, api, 0, seriesModel.getData().count());
  101. } else if (isGeoCoordSys(coordSys)) {
  102. this._renderOnGeo(coordSys, seriesModel, visualMapOfThisSeries, api);
  103. }
  104. },
  105. incrementalPrepareRender: function (seriesModel, ecModel, api) {
  106. this.group.removeAll();
  107. },
  108. incrementalRender: function (params, seriesModel, ecModel, api) {
  109. var coordSys = seriesModel.coordinateSystem;
  110. if (coordSys) {
  111. this._renderOnCartesianAndCalendar(seriesModel, api, params.start, params.end, true);
  112. }
  113. },
  114. _renderOnCartesianAndCalendar: function (seriesModel, api, start, end, incremental) {
  115. var coordSys = seriesModel.coordinateSystem;
  116. var width;
  117. var height;
  118. if (coordSys.type === 'cartesian2d') {
  119. var xAxis = coordSys.getAxis('x');
  120. var yAxis = coordSys.getAxis('y');
  121. width = xAxis.getBandWidth();
  122. height = yAxis.getBandWidth();
  123. }
  124. var group = this.group;
  125. var data = seriesModel.getData();
  126. var itemStyleQuery = 'itemStyle';
  127. var hoverItemStyleQuery = 'emphasis.itemStyle';
  128. var labelQuery = 'label';
  129. var hoverLabelQuery = 'emphasis.label';
  130. var style = seriesModel.getModel(itemStyleQuery).getItemStyle(['color']);
  131. var hoverStl = seriesModel.getModel(hoverItemStyleQuery).getItemStyle();
  132. var labelModel = seriesModel.getModel(labelQuery);
  133. var hoverLabelModel = seriesModel.getModel(hoverLabelQuery);
  134. var coordSysType = coordSys.type;
  135. var dataDims = coordSysType === 'cartesian2d' ? [data.mapDimension('x'), data.mapDimension('y'), data.mapDimension('value')] : [data.mapDimension('time'), data.mapDimension('value')];
  136. for (var idx = start; idx < end; idx++) {
  137. var rect;
  138. if (coordSysType === 'cartesian2d') {
  139. // Ignore empty data
  140. if (isNaN(data.get(dataDims[2], idx))) {
  141. continue;
  142. }
  143. var point = coordSys.dataToPoint([data.get(dataDims[0], idx), data.get(dataDims[1], idx)]);
  144. rect = new graphic.Rect({
  145. shape: {
  146. x: Math.floor(Math.round(point[0]) - width / 2),
  147. y: Math.floor(Math.round(point[1]) - height / 2),
  148. width: Math.ceil(width),
  149. height: Math.ceil(height)
  150. },
  151. style: {
  152. fill: data.getItemVisual(idx, 'color'),
  153. opacity: data.getItemVisual(idx, 'opacity')
  154. }
  155. });
  156. } else {
  157. // Ignore empty data
  158. if (isNaN(data.get(dataDims[1], idx))) {
  159. continue;
  160. }
  161. rect = new graphic.Rect({
  162. z2: 1,
  163. shape: coordSys.dataToRect([data.get(dataDims[0], idx)]).contentShape,
  164. style: {
  165. fill: data.getItemVisual(idx, 'color'),
  166. opacity: data.getItemVisual(idx, 'opacity')
  167. }
  168. });
  169. }
  170. var itemModel = data.getItemModel(idx); // Optimization for large datset
  171. if (data.hasItemOption) {
  172. style = itemModel.getModel(itemStyleQuery).getItemStyle(['color']);
  173. hoverStl = itemModel.getModel(hoverItemStyleQuery).getItemStyle();
  174. labelModel = itemModel.getModel(labelQuery);
  175. hoverLabelModel = itemModel.getModel(hoverLabelQuery);
  176. }
  177. var rawValue = seriesModel.getRawValue(idx);
  178. var defaultText = '-';
  179. if (rawValue && rawValue[2] != null) {
  180. defaultText = rawValue[2];
  181. }
  182. graphic.setLabelStyle(style, hoverStl, labelModel, hoverLabelModel, {
  183. labelFetcher: seriesModel,
  184. labelDataIndex: idx,
  185. defaultText: defaultText,
  186. isRectText: true
  187. });
  188. rect.setStyle(style);
  189. graphic.setHoverStyle(rect, data.hasItemOption ? hoverStl : zrUtil.extend({}, hoverStl));
  190. rect.incremental = incremental; // PENDING
  191. if (incremental) {
  192. // Rect must use hover layer if it's incremental.
  193. rect.useHoverLayer = true;
  194. }
  195. group.add(rect);
  196. data.setItemGraphicEl(idx, rect);
  197. }
  198. },
  199. _renderOnGeo: function (geo, seriesModel, visualMapModel, api) {
  200. var inRangeVisuals = visualMapModel.targetVisuals.inRange;
  201. var outOfRangeVisuals = visualMapModel.targetVisuals.outOfRange; // if (!visualMapping) {
  202. // throw new Error('Data range must have color visuals');
  203. // }
  204. var data = seriesModel.getData();
  205. var hmLayer = this._hmLayer || this._hmLayer || new HeatmapLayer();
  206. hmLayer.blurSize = seriesModel.get('blurSize');
  207. hmLayer.pointSize = seriesModel.get('pointSize');
  208. hmLayer.minOpacity = seriesModel.get('minOpacity');
  209. hmLayer.maxOpacity = seriesModel.get('maxOpacity');
  210. var rect = geo.getViewRect().clone();
  211. var roamTransform = geo.getRoamTransform();
  212. rect.applyTransform(roamTransform); // Clamp on viewport
  213. var x = Math.max(rect.x, 0);
  214. var y = Math.max(rect.y, 0);
  215. var x2 = Math.min(rect.width + rect.x, api.getWidth());
  216. var y2 = Math.min(rect.height + rect.y, api.getHeight());
  217. var width = x2 - x;
  218. var height = y2 - y;
  219. var dims = [data.mapDimension('lng'), data.mapDimension('lat'), data.mapDimension('value')];
  220. var points = data.mapArray(dims, function (lng, lat, value) {
  221. var pt = geo.dataToPoint([lng, lat]);
  222. pt[0] -= x;
  223. pt[1] -= y;
  224. pt.push(value);
  225. return pt;
  226. });
  227. var dataExtent = visualMapModel.getExtent();
  228. var isInRange = visualMapModel.type === 'visualMap.continuous' ? getIsInContinuousRange(dataExtent, visualMapModel.option.range) : getIsInPiecewiseRange(dataExtent, visualMapModel.getPieceList(), visualMapModel.option.selected);
  229. hmLayer.update(points, width, height, inRangeVisuals.color.getNormalizer(), {
  230. inRange: inRangeVisuals.color.getColorMapper(),
  231. outOfRange: outOfRangeVisuals.color.getColorMapper()
  232. }, isInRange);
  233. var img = new graphic.Image({
  234. style: {
  235. width: width,
  236. height: height,
  237. x: x,
  238. y: y,
  239. image: hmLayer.canvas
  240. },
  241. silent: true
  242. });
  243. this.group.add(img);
  244. },
  245. dispose: function () {}
  246. });
  247. module.exports = _default;