效果图

1. 项目背景与需求

我们希望在一个 Cesium 地图中叠加 Heatmap.js 热力图,热力图的数据来源于一个 JSON 文件。实现过程中,我们利用 Vue3 框架来管理组件,html2canvas 来处理生成的热力图图像,最后通过 Cesium 的 ImageMaterialProperty 将热力图覆盖到地图上。

2. 项目结构与思路

需求完成思路:

  • **Cesium 初始化和地图设置:**在 Vue 组件的 mounted 钩子函数中初始化 Cesium,配置底图、标注层等内容。
  • **热力图数据处理与转换:**通过 axios 获取热力图数据,并进行必要的数据转换,将经纬度映射到屏幕坐标上。
  • **生成热力图并叠加到Cesium 地图:**利用 Heatmap.js 创建热力图,并通过 html2canvas 将其转化为图像,然后使用 Cesium 的 ImageMaterialProperty 将热力图覆盖到地图区域。

3. 代码实现

3.1 HTML 模板结构

<template>
	<div id="cesiumContainer" style="width: 100vw; height: 100vh;"></div>
	<div id="heatMap" v-show="true"></div>
</template>
  • **cesiumContainer:**容器用于显示 Cesium 地图。
  • **heatMap:**容器用于显示热力图,将通过 Heatmap.js
    渲染。

3.2 Cesium 初始化与地图设置

export default {
  name: 'CesiumMap',
  async mounted() {
    var token = 'your-api-key-here';  // 你的天地图 Token
    var tdtUrl = 'https://t{s}.tianditu.gov.cn/';  // 天地图服务 URL
    var subdomains = ['0', '1', '2', '3', '4', '5', '6', '7'];  // 子域配置
    let maximumLevel = 18;  // 最大缩放级别

    // 初始化 Cesium Viewer
    viewer = new Cesium.Viewer('cesiumContainer');

    // 叠加天地图的国界服务
    var iboMap = new Cesium.UrlTemplateImageryProvider({
      url: tdtUrl + 'DataServer?T=ibo_w&x={x}&y={y}&l={z}&tk=' + token,
      subdomains,
      tilingScheme: new Cesium.WebMercatorTilingScheme(),
      maximumLevel,
    });
    viewer.imageryLayers.addImageryProvider(iboMap);

    // 叠加天地图的注记服务
    var labelMap = new Cesium.UrlTemplateImageryProvider({
      url: tdtUrl + 'DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=' + token,
      subdomains,
      tilingScheme: new Cesium.WebMercatorTilingScheme(),
      maximumLevel,
    });
    viewer.imageryLayers.addImageryProvider(labelMap);

    // 加载热力图
    this.loadHeatMap();
  }
}

3.3 加载热力图

async loadHeatMap() {
  let { data } = await axios("/echarts/examples/data/asset/data/hangzhou-tracks.json");  // 获取热力图数据
  	//数据格式
	// [
	// 	[{
	// 		"coord": [
	// 			120.14322240845,
	// 			30.236064370321
	// 		],
	// 		"elevation": 21
	// 	}, ]
	// ]
  let heatmapData = data.flat();  // 扁平化数据
  let points = [];
  let max = 0;
  let width = 400, height = 400;
  let latMin = 90, latMax = 0;
  let lonMin = 180, lonMax = 0;

  // 获取最小最大经纬度和最大值
  latMin = Math.min(latMin, ...heatmapData.map(point => point.coord[1]));
  latMax = Math.max(latMax, ...heatmapData.map(point => point.coord[1]));
  lonMin = Math.min(lonMin, ...heatmapData.map(point => point.coord[0]));
  lonMax = Math.max(lonMax, ...heatmapData.map(point => point.coord[0]));
  max = Math.max(max, ...heatmapData.map(point => point.elevation));

  // 数据转换为屏幕坐标
  heatmapData.forEach(point => {
    points.push({
      x: Math.floor((point.coord[1] - latMin) / (latMax - latMin) * width),
      y: Math.floor((point.coord[0] - lonMin) / (lonMax - lonMin) * height),
      value: Math.floor(point.elevation),
    });
  });

  // 创建热力图实例
  let heatMapDom = document.querySelector("#heatMap");
  let heatMapInstance = h337.create({
    container: heatMapDom,
    radius: 3,
    blur: 1,
  });

  // 设置热力图数据
  heatMapInstance.setData({
    max: max,
    data: points,
  });

  // 使用 html2canvas 生成热力图图像并叠加到 Cesium 地图
  let canvasDom = document.querySelector('#heatMap');
  html2canvas(canvasDom, {
    backgroundColor: null  // 确保背景透明
  }).then(canvas => {
    const imageUrl = canvas.toDataURL('image/png');  // 转为图像 URL
    viewer.entities.add({
      name: 'heatmap',
      rectangle: {
        coordinates: Cesium.Rectangle.fromDegrees(lonMin, latMin, lonMax, latMax),
        material: new Cesium.ImageMaterialProperty({
          image: imageUrl,
          transparent: true,
        }),
      }
    });

    viewer.zoomTo(viewer.entities);
  });
}
  • **获取热力图数据:**通过 axios 请求获取热力图数据,数据结构包含经纬度和数值(elevation)。
  • **数据处理:**将经纬度映射为屏幕坐标(宽度 400px,高度 400px),并计算最大值来确定热力图强度。
  • **热力图实例化:**使用 h337.create() 方法创建热力图实例,并将数据传入。
  • **渲染到 Cesium:**通过 html2canvas 将热力图生成图像,并使用 Cesium.ImageMaterialProperty 将其叠加到 Cesium 地图上。

4. 样式与布局

#cesiumContainer {
  width: 100vw;
  height: 100vh;
}

#heatMap {
  width: 400px;
  height: 400px;
  z-index: 10000;
}

5. 重要一点

把图片上这个代码删掉,否则heatmapjs还会报错
h337对象 要这么引入,路径要写全,指定到你修改的那个文件

import * as h337 from "heatmapjs/heatmap.min.js";

6. 总结

在本示例中,我们通过 Vue3 和 Cesium 集成了 Heatmap.js 来渲染热力图。通过合理的数据转换、图像生成和地图覆盖技术,成功将热力图叠加到 Cesium 地图上,提供了一个直观的数据可视化方式。通过 html2canvas 的辅助,我们还能够将热力图转换为图像并直接用于 Cesium 的 ImageMaterialProperty 中,从而实现图像叠加。

最后附上源码

<template>
	<!-- Cesium 容器,占满整个屏幕 -->
	<div id="cesiumContainer" style="width: 100vw; height: 100vh;"></div>
	<!-- 热力图容器,初始可见 -->
	<div id="heatMap" v-show="true"></div>
</template>

<script>
// 导入需要的库和工具
import axios from "axios";  // 用于发送 HTTP 请求
import * as h337 from "heatmapjs/heatmap.min.js";  // 导入 Heatmap.js
import html2canvas from 'html2canvas';  // 用于将 HTML 转换为 Canvas

// 定义一个全局变量来存储热力图 DOM 元素
var heatMapDom

// 定义一个全局变量来保存 Cesium Viewer 实例
window.viewer = null

export default {
  name: 'CesiumMap',  // 组件名称
  async mounted() {
    // 初始化变量和配置项
    var token = 'your-api-key-here';  // 这里需要替换为天地图的 Token
    var tdtUrl = 'https://t{s}.tianditu.gov.cn/';  // 天地图服务的基础 URL
    var subdomains = ['0', '1', '2', '3', '4', '5', '6', '7'];  // 天地图子域
    let maximumLevel = 18;  // 最大缩放级别

    // 初始化 Cesium Viewer(地图显示容器)
    viewer = new Cesium.Viewer('cesiumContainer');
    
    // 设置天地图的国界服务图层
    var iboMap = new Cesium.UrlTemplateImageryProvider({
      url: tdtUrl + 'DataServer?T=ibo_w&x={x}&y={y}&l={z}&tk=' + token,  // 配置图层 URL,包含 Token
      subdomains,  // 设置子域配置
      tilingScheme: new Cesium.WebMercatorTilingScheme(),  // 使用 WebMercator 瓦片坐标系统
      maximumLevel,  // 最大缩放级别
    });
    viewer.imageryLayers.addImageryProvider(iboMap);  // 将图层添加到 Cesium 中

    // 设置天地图的标注服务图层
    var labelMap = new Cesium.UrlTemplateImageryProvider({
      url: tdtUrl + 'DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=' + token,  // 配置标注图层 URL,包含 Token
      subdomains,  // 设置子域配置
      tilingScheme: new Cesium.WebMercatorTilingScheme(),  // 使用 WebMercator 瓦片坐标系统
      maximumLevel,  // 最大缩放级别
    });
    viewer.imageryLayers.addImageryProvider(labelMap);  // 将标注图层添加到 Cesium 中

    // 调用加载热力图的方法
    this.loadHeatMap();
  },

  methods: {
    // 加载热力图的逻辑
    async loadHeatMap() {
      // 使用 axios 请求热力图数据(这里的路径需要根据实际情况修改)
      let { data } = await axios("/echarts/examples/data/asset/data/hangzhou-tracks.json");

      // 将数据进行扁平化处理
      let heatmapData = data.flat();
      let points = [];  // 用于存储热力图的数据点
      let max = 0;  // 最大值,用于热力图强度

      // 设置热力图的宽度和高度
      let width = 400;
      let height = 400;

      // 设置经纬度的最小值和最大值
      let latMin = 90;
      let latMax = 0;
      let lonMin = 180;
      let lonMax = 0;

      // 遍历热力图数据,计算经纬度范围,并找到最大值
      latMin = Math.min(latMin, ...heatmapData.map(point => point.coord[1]));
      latMax = Math.max(latMax, ...heatmapData.map(point => point.coord[1]));
      lonMin = Math.min(lonMin, ...heatmapData.map(point => point.coord[0]));
      lonMax = Math.max(lonMax, ...heatmapData.map(point => point.coord[0]));
      max = Math.max(max, ...heatmapData.map(point => point.elevation));

      // 将经纬度转换为屏幕坐标
      heatmapData.forEach(point => {
        points.push({
          x: Math.floor((point.coord[1] - latMin) / (latMax - latMin) * width),  // 转换纬度
          y: Math.floor((point.coord[0] - lonMin) / (lonMax - lonMin) * height),  // 转换经度
          value: Math.floor(point.elevation)  // 计算强度值
        });
      });

      // 获取热力图的 DOM 元素
      heatMapDom = document.querySelector("#heatMap");

      // 创建 Heatmap.js 实例
      let heatMapInstance = h337.create({
        container: heatMapDom,  // 设置容器为热力图的 DOM 元素
        radius: 3,  // 热力图的半径
        blur: 1,  // 模糊程度
      });

      // 设置热力图的数据
      heatMapInstance.setData({
        max: max,  // 最大强度值
        data: points,  // 热力图数据点
      });

      // 使用 html2canvas 将热力图生成 Canvas 图像
      let canvasDom = document.querySelector('#heatMap');
      html2canvas(canvasDom, {
        backgroundColor: null  // 确保背景透明
      }).then(canvas => {
        // 将 Canvas 转换为图像 URL
        const imageUrl = canvas.toDataURL('image/png');
        console.log(lonMin, latMin, lonMax, latMax);  // 打印经纬度范围

        // 将热力图图像添加到 Cesium 地图中
        viewer.entities.add({
          name: 'heatmap',
          rectangle: {
            coordinates: Cesium.Rectangle.fromDegrees(lonMin, latMin, lonMax, latMax),  // 设置热力图的地理范围
            material: new Cesium.ImageMaterialProperty({
              image: imageUrl,  // 使用热力图的图像
              transparent: true  // 设置透明
            }),
          }
        });

        // 视图自动缩放以适应热力图的区域
        viewer.zoomTo(viewer.entities);
      });
    },
  }
};
</script>

<style>
/* Cesium 容器样式,确保地图覆盖整个屏幕 */
#cesiumContainer {
  width: 100vw;
  height: 100vh;
}

/* 热力图容器样式,设置固定大小 */
#heatMap {
  width: 400px;
  height: 400px;
  z-index: 10000;  /* 确保热力图在 Cesium 上方显示 */
}
</style>

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐