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.

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