当前位置:   article > 正文

Cesium 三维热力图_cesium 热力图

cesium 热力图

要用cesium做个三维热力图的效果,但在网上没找到现成的方案,摸索了很久,最终效果如下:

三维热力图

实现思路如下:

1、通过heatmapjs库生成二维热力图,拿到canvas;

  1. addHeatMap() {
  2. let width = 500
  3. let height = 500
  4. this.heatMap = h337.create(this.getDefaultData(width, height))
  5. let bounds = [113.5, 22, 114.5, 23]
  6. this.bounds = {
  7. west: 113.5,
  8. south: 22,
  9. east: 114.5,
  10. north: 23,
  11. }
  12. let data = [];
  13. let max = 0
  14. for (let i = 0; i < 200; i++) {
  15. let value = Math.floor(Math.random() * 100)
  16. max = Math.max(max, value)
  17. let x = Math.floor(Math.random() * width)
  18. let y = Math.floor(Math.random() * height)
  19. data.push({ x: x, y: y, value: value });
  20. }
  21. this.heatMap.setData({
  22. // min: 0,
  23. max: max,
  24. data: data
  25. })
  26. this.addcanvas(bounds, true)
  27. }

2、canvas的rgb像素值转hsl,将h分量作为该像素点的高度值的参考(即越红高度越高);

先转灰度图

  1. // 图片转灰度图
  2. getGrayMap(img) {
  3. const canvas = document.createElement('canvas')
  4. canvas.width = img.width
  5. canvas.height = img.height
  6. const ctx = canvas.getContext('2d')
  7. ctx.drawImage(img, 0, 0)
  8. const c = ctx.getImageData(0, 0, img.width, img.height)
  9. for (let i = 0; i < img.width; i++) {
  10. for (let j = 0; j < img.height; j++) {
  11. const x = (j * 4) * c.width + (i * 4)
  12. const r = c.data[x]
  13. const g = c.data[x + 1]
  14. const b = c.data[x + 2]
  15. this.colors.push([r / 255, g / 255, b / 255, 1], [r / 255, g / 255, b / 255, 1])
  16. const gray = 240 - this.rgbtohsl(r, g, b)[0]
  17. c.data[x] = c.data[x + 1] = c.data[x + 2] = gray
  18. }
  19. }
  20. ctx.putImageData(c, 0, 0, 0, 0, c.width, c.height)
  21. return canvas.toDataURL()
  22. }

灰度转出来是一维数组,转为rgba四通道 

  1. getGrayValues(img) {
  2. const ch = new CanvasHeightmap()
  3. ch.use(img).then(() => {
  4. ch.draw()
  5. return ch.getRgbaArray()
  6. }).then((data) => { this.createTerrain(data) })
  7. }

3、将整个canvas划分,获取每个顶点的坐标值(经纬度+通过2中得到的高度);

  1. getPos(rgbaArray, bounds) {
  2. const arr = []
  3. const row = rgbaArray.length
  4. const col = rgbaArray[0].length
  5. const everyLon = (bounds.east - bounds.west) / col
  6. const everyLat = (bounds.north - bounds.south) / row
  7. for (let i = 0; i <= row ; i++) {
  8. for (let j = 0; j <= col ; j++) {
  9. if ((i * col + j + 1) % (col + 1) !== 0 && (i * col + j) < (col + 1) * (row)) {
  10. this.indices.push(i * col + j, i * col + j + 1, i * col + j + 1 + col + 1, i * col + j + 1 + col + 1, i * col + j, i * col + j + col + 1)
  11. }
  12. const lon1 = bounds.west + j * everyLon
  13. const lat1 = bounds.north - i * everyLat
  14. // let height1 = rgbaArray[i][j][0]
  15. let height1 = this.getHeight(i, j, col, row, rgbaArray)
  16. const position = Cesium.Cartesian3.fromDegrees(
  17. lon1,
  18. lat1, height1 * 10
  19. )
  20. arr.push(position.x, position.y, position.z)
  21. this.sts.push((j / rgbaArray[0].length), (1 - i / rgbaArray.length))
  22. }
  23. }
  24. return arr
  25. }
  26. getHeight(i, j, col, row ,rgbaArray) {
  27. let height1
  28. if (i === 0 && j === 0) {
  29. height1 = rgbaArray[i][j][0] }
  30. if (i === 0 && j !== 0 && j !== col) { height1 = (rgbaArray[i][j][0] + rgbaArray[i][j - 1][0]) / 2 }
  31. if (i === 0 && j === col) { height1 = rgbaArray[i][j - 1][0] }
  32. if (i !== 0 && i !== row && j === 0) { height1 = (rgbaArray[i][j][0] + rgbaArray[i - 1][j][0]) / 2 }
  33. if (i !== 0 && i !== row && j !== 0 && j !== col) { height1 = (rgbaArray[i][j][0] + rgbaArray[i - 1][j][0] + rgbaArray[i - 1][j - 1][0] + rgbaArray[i][j - 1][0]) / 4 }
  34. if (i !== 0 && i !== row && j === col) { height1 = (rgbaArray[i - 1][j - 1][0] + rgbaArray[i][j - 1][0]) / 2 }
  35. if (i !== 0 && i !== row && j !== 0 && j !== col) { height1 = (rgbaArray[i][j][0] + rgbaArray[i - 1][j][0] + rgbaArray[i - 1][j - 1][0] + rgbaArray[i][j - 1][0]) / 4 }
  36. if (i !== 0 && i !== row && j === col) { height1 = (rgbaArray[i - 1][j - 1][0] + rgbaArray[i][j - 1][0]) / 2 }
  37. if (i === row && j === 0) { height1 = rgbaArray[i - 1][j][0] }
  38. if (i === row && j !== 0 && j !== col) { height1 = (rgbaArray[i - 1][j][0] + rgbaArray[i - 1][j - 1][0]) / 2 }
  39. if (i === row && j === col) { height1 = rgbaArray[i - 1][j - 1][0] }
  40. return height1
  41. }

4、通过3中的坐标创建三角网,参考:

cesium 绘制自定义geometry、三角面_liuqing0.0的博客-CSDN博客_cesium geometry

  1. createGeometry(realPos) {
  2. const attributes = new Cesium.GeometryAttributes({
  3. position: new Cesium.GeometryAttribute({
  4. componentDatatype: Cesium.ComponentDatatype.DOUBLE,
  5. componentsPerAttribute: 3,
  6. values: new Float64Array(realPos)
  7. }),
  8. st: new Cesium.GeometryAttribute({
  9. componentDatatype: Cesium.ComponentDatatype.FLOAT,
  10. componentsPerAttribute: 2,
  11. values: new Float32Array(this.sts)
  12. })
  13. })
  14. const boundingSphere = Cesium.BoundingSphere.fromVertices(realPos,
  15. new Cesium.Cartesian3(0.0, 0.0, 0.0), 3)
  16. const geometry = new Cesium.Geometry({
  17. attributes: attributes,
  18. indices: this.indices,
  19. primitiveType: Cesium.PrimitiveType.TRIANGLES,
  20. boundingSphere: boundingSphere
  21. })
  22. return new Cesium.GeometryInstance({ geometry })
  23. }
  24. addGeometry(instance) {
  25. this.primitive = this.viewer.scene.primitives.add(
  26. new Cesium.Primitive({
  27. geometryInstances: instance,
  28. appearance: new Cesium.Appearance({
  29. material: new Cesium.Material({
  30. fabric: {
  31. uniforms: {
  32. image: this.heatMap._renderer.canvas.toDataURL()
  33. },
  34. source: this.getMS()
  35. }
  36. }),
  37. aboveGround: true,
  38. faceForward: true,
  39. flat: true,
  40. translucent: false,
  41. renderState: {
  42. blending: Cesium.BlendingState.PRE_MULTIPLIED_ALPHA_BLEND,
  43. depthTest: {
  44. enabled: true,
  45. },
  46. depthMask: true,
  47. },
  48. fragmentShaderSource: this.getFS(),
  49. vertexShaderSource: this.getVS()
  50. }),
  51. asynchronous: false
  52. }))
  53. }

主要需要处理position的values、st的values、和indices这三个属性的值。

我的思路是求出每个像素块的四个顶点的经纬度坐标,高度为周围的像素的h分量值的平均值;

每个像素块绘制两个三角形,如:左下--左上--右上 + 右上--左下--右下,当然顺序可以自己定义,只要保持每个三角形的起点与上一个三角形的终点连续就行;

5、将二维热力图作为三角网的材质贴图,并将三角网添加进场景;

贴图参考:

Cesium学习笔记-工具篇20-PrimitiveTexture自定义渲染-贴图【转】 | PNG

 

源码cesium三维热力图资源-CSDN文库 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/760708
推荐阅读
相关标签
  

闽ICP备14008679号