You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

HeatLayer.js 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /**
  2. * @Author: Caven
  3. * @Date: 2020-02-27 00:35:35
  4. */
  5. import { Cesium } from '@dc-modules/namespace'
  6. import State from '@dc-modules/state/State'
  7. import { Layer } from '@dc-modules/layer'
  8. import { createWebGLHeatmap } from './webgl-heatmap'
  9. const WMP = new Cesium.WebMercatorProjection()
  10. const DEF_OPTS = {
  11. radius: 30,
  12. height: 0,
  13. gradient: undefined
  14. }
  15. class HeatLayer extends Layer {
  16. constructor(id, options = {}) {
  17. super(id)
  18. this._options = {
  19. ...DEF_OPTS,
  20. ...options
  21. }
  22. this._canvas = document.createElement('canvas')
  23. this._canvas.setAttribute('id', id)
  24. this._heat = undefined
  25. this._mRect = undefined
  26. this._rect = new Cesium.Rectangle()
  27. this._delegate = new Cesium.PrimitiveCollection()
  28. this._primitive = this._delegate.add(
  29. new Cesium.Primitive({
  30. geometryInstances: new Cesium.GeometryInstance({
  31. geometry: {}
  32. })
  33. })
  34. )
  35. this._scale = 1
  36. this._points = []
  37. this._positions = []
  38. this._state = State.INITIALIZED
  39. }
  40. /**
  41. *
  42. * @private
  43. */
  44. _addedHook() {
  45. this._canvas.style.cssText = `
  46. visibility:hidden;
  47. width:${this._viewer.canvas.width}px;
  48. height:${this._viewer.canvas.height}px;
  49. `
  50. this._viewer.dcContainer.appendChild(this._canvas)
  51. }
  52. /**
  53. *
  54. * @returns {string|undefined}
  55. * @private
  56. */
  57. _createGradientTexture() {
  58. if (!this._options.gradient) {
  59. return undefined
  60. }
  61. let canvas = document.createElement('canvas')
  62. canvas.width = 200
  63. canvas.height = 10
  64. let ctx = canvas.getContext('2d')
  65. let grd = ctx.createLinearGradient(0, 0, 200, 0)
  66. for (let key in this._options.gradient) {
  67. grd.addColorStop(+key, this._options.gradient[+key])
  68. }
  69. ctx.fillStyle = grd
  70. ctx.fillRect(0, 0, 200, 10)
  71. return canvas.toDataURL()
  72. }
  73. /**
  74. *
  75. * @param position
  76. * @returns {{intensity: number, size: (*|number), x: number, y: number}}
  77. * @private
  78. */
  79. _parsePosition(position) {
  80. let point = WMP.project(
  81. Cesium.Cartographic.fromDegrees(position.lng, position.lat)
  82. )
  83. return {
  84. x: (point.x - this._mRect.west) / this._scale,
  85. y: (point.y - this._mRect.south) / this._scale,
  86. size: this._options.radius,
  87. intensity: position.value || Math.random()
  88. }
  89. }
  90. /**
  91. *
  92. * @param positions
  93. * @returns {*}
  94. * @private
  95. */
  96. _parsePositions(positions) {
  97. return positions.map(this._parsePosition.bind(this))
  98. }
  99. /**
  100. *
  101. * @private
  102. */
  103. _setRect() {
  104. Cesium.Rectangle.fromCartographicArray(
  105. this._positions.map(item =>
  106. Cesium.Cartographic.fromDegrees(item.lng, item.lat)
  107. ),
  108. this._rect
  109. )
  110. }
  111. /**
  112. *
  113. * @returns {{east, south, north, west}}
  114. * @private
  115. */
  116. _getMRect() {
  117. let mSouthwest = WMP.project(Cesium.Rectangle.southwest(this._rect))
  118. let mNortheast = WMP.project(Cesium.Rectangle.northeast(this._rect))
  119. return {
  120. west: mSouthwest.x,
  121. south: mSouthwest.y,
  122. east: mNortheast.x,
  123. north: mNortheast.y
  124. }
  125. }
  126. /**
  127. *
  128. * @returns {boolean}
  129. * @private
  130. */
  131. _update() {
  132. if (!this._points || !this._points.length) {
  133. return false
  134. }
  135. this._heat.adjustSize()
  136. this._heat.update()
  137. this._heat.display()
  138. if (this._primitive && this._primitive.geometryInstances) {
  139. this._primitive.geometryInstances.geometry = new Cesium.RectangleGeometry(
  140. {
  141. rectangle: this._rect,
  142. height: this._options.height
  143. }
  144. )
  145. }
  146. this._primitive.appearance = new Cesium.MaterialAppearance({
  147. material: new Cesium.Material({
  148. fabric: {
  149. type: 'Heat-Image',
  150. uniforms: {
  151. image: this._canvas
  152. },
  153. source: `
  154. uniform sampler2D image;
  155. czm_material czm_getMaterial(czm_materialInput materialInput){
  156. czm_material material = czm_getDefaultMaterial(materialInput);
  157. vec2 st = materialInput.st;
  158. vec4 colorImage = texture2D(image,st);
  159. if(colorImage.rgb == vec3(1.0) || colorImage.rgb == vec3(0.0)){
  160. discard;
  161. }
  162. material.diffuse = colorImage.rgb;
  163. material.alpha = colorImage.a;
  164. return material;
  165. }
  166. `
  167. },
  168. translucent: function(material) {
  169. return true
  170. }
  171. }),
  172. flat: true
  173. })
  174. }
  175. /**
  176. *
  177. * @param positions
  178. */
  179. setPositions(positions) {
  180. this._positions = positions
  181. this._setRect()
  182. let mRect = this._getMRect()
  183. if (
  184. !this._mRect ||
  185. !mRect.west === this._mRect.west ||
  186. !mRect.south === this._mRect.south ||
  187. !mRect.east === this._mRect.east ||
  188. !mRect.north === this._mRect.north
  189. ) {
  190. this._mRect = mRect
  191. this._heat = createWebGLHeatmap({
  192. canvas: this._canvas,
  193. gradientTexture: this._createGradientTexture()
  194. })
  195. this._scale = Math.min(
  196. Math.abs(this._mRect.west - this._mRect.east) / this._canvas.width,
  197. Math.abs(this._mRect.north - this._mRect.south) / this._canvas.height
  198. )
  199. }
  200. this._points = this._parsePositions(this._positions)
  201. if (this._heat) {
  202. this._heat.blur()
  203. this._heat.addPoints(this._points)
  204. this._update()
  205. }
  206. return this
  207. }
  208. /**
  209. *
  210. * @param position
  211. */
  212. addPosition(position) {
  213. this._positions.push(position)
  214. this._setRect()
  215. let mRect = this._getMRect()
  216. if (
  217. !this._mRect ||
  218. !mRect.west === this._mRect.west ||
  219. !mRect.south === this._mRect.south ||
  220. !mRect.east === this._mRect.east ||
  221. !mRect.north === this._mRect.north
  222. ) {
  223. this._mRect = mRect
  224. this._heat = createWebGLHeatmap({
  225. canvas: this._canvas,
  226. gradientTexture: this._createGradientTexture()
  227. })
  228. this._scale = Math.min(
  229. Math.abs(this._mRect.west - this._mRect.east) / this._canvas.width,
  230. Math.abs(this._mRect.north - this._mRect.south) / this._canvas.height
  231. )
  232. this._heat.addPoints(this._points)
  233. }
  234. let point = this._parsePosition(position)
  235. this._points.push(point)
  236. if (this._heat) {
  237. this._heat.addPoint(point.x, point.y, point.size, point.intensity)
  238. this._update()
  239. }
  240. return this
  241. }
  242. }
  243. Layer.registerType('heat')
  244. export default HeatLayer