接到一位知識星友的邀請,隨機模擬三維數據點,結合heatmap.js實現基于cesium+vue的3D熱力圖需求,適合學習Cesium與前端框架結合開發3D可視化項目。
demo源碼運行環境以及配置
運行環境:依賴Node安裝環境,demo本地Node版本:推薦v18+。
運行工具:vscode或者其他工具。
配置方式:下載demo源碼,vscode打開,然后順序執行以下命令:
(1)下載demo環境依賴包命令:npm install
(2)啟動demo命令:npm run dev
(3)打包demo命令: npm run build
技術棧
Vue 3.5.13
Vite 6.2.0
Cesium 1.128.0
示例效果
核心源碼
<template><div id="cesiumContainer" class="cesium-container"></div>
</template>
<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
import * as Cesium from 'cesium';
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxZjhjYjhkYS1jMzA1LTQ1MTEtYWE1Mi0zODc5NDljOGVkMDYiLCJpZCI6MTAzNjEsInNjb3BlcyI6WyJhc2wiLCJhc3IiLCJhc3ciLCJnYyJdLCJpYXQiOjE1NzA2MDY5ODV9.X7tj92tunUvx6PkDpj3LFsMVBs_SBYyKbIL_G9xKESA';
// 聲明Cesium Viewer實例
let viewer = null;
// 組件掛載后初始化Cesium
onMounted(async () => {const files = ["./heatmap/heatmap.js"];loadScripts(files, function () {console.log("All scripts loaded");initMap();});
});
const loadScripts = (files, callback) => {// Make Cesium available globally for the scripts// window.Cesium = Cesium;if (files.length === 0) {callback();return;}const file = files.shift();const script = document.createElement("script");script.onload = function () {loadScripts(files, callback);};script.src = file;document.head.appendChild(script);
};
const initMap = async () => {// 初始化Cesium Viewerviewer = new Cesium.Viewer('cesiumContainer', {// 基礎配置animation: false, // 動畫小部件baseLayerPicker: false, // 底圖選擇器fullscreenButton: false, // 全屏按鈕vrButton: false, // VR按鈕geocoder: false, // 地理編碼搜索框homeButton: false, // 主頁按鈕infoBox: false, // 信息框 - 禁用點擊彈窗sceneModePicker: false, // 場景模式選擇器selectionIndicator: false, // 選擇指示器timeline: false, // 時間軸navigationHelpButton: false, // 導航幫助按鈕navigationInstructionsInitiallyVisible: false, // 導航說明初始可見性scene3DOnly: false, // 僅3D場景terrain: Cesium.Terrain.fromWorldTerrain(), // 使用世界地形});// 隱藏logoviewer.cesiumWidget.creditContainer.style.display = "none";viewer.scene.globe.enableLighting = true;// 禁用大氣層和太陽viewer.scene.skyAtmosphere.show = false;//前提先把場景上的圖層全部移除或者隱藏 // viewer.scene.globe.baseColor = Cesium.Color.BLACK; //修改地圖藍色背景viewer.scene.globe.baseColor = new Cesium.Color(0.0, 0.1, 0.2, 1.0); //修改地圖為暗藍色背景// 設置抗鋸齒viewer.scene.postProcessStages.fxaa.enabled = true;// 清除默認底圖viewer.imageryLayers.remove(viewer.imageryLayers.get(0));// 加載底圖 - 使用更暗的地圖服務const imageryProvider = await Cesium.ArcGisMapServerImageryProvider.fromUrl("https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer");viewer.imageryLayers.addImageryProvider(imageryProvider);// 設置默認視圖位置 - 默認顯示全球視圖// viewer.camera.setView({// destination: Cesium.Cartesian3.fromDegrees(104.0, 30.0, 10000000.0), // 中國中部上空// orientation: {// heading: 0.0,// pitch: -Cesium.Math.PI_OVER_TWO,// roll: 0.0// }// });// 啟用地形深度測試,確保地形正確渲染viewer.scene.globe.depthTestAgainstTerrain = true;// 模擬數值const points = new Array(50).fill("").map(() => {return {lnglat: [116.46 + Math.random() * 0.1 * (Math.random() > 0.5 ? 1 : -1),39.92 + Math.random() * 0.1 * (Math.random() > 0.5 ? 1 : -1),],value: 1000 * Math.random(),};});// 創建熱力圖create3DHeatmap(viewer, {dataPoints: points,radius: 15,baseElevation: 0,primitiveType: "TRIANGLES",colorGradient: {".3": "blue",".5": "green",".7": "yellow",".95": "red",},});viewer.camera.flyTo({destination: Cesium.Cartesian3.fromDegrees(116.46, 39.92, 100000),orientation: {},duration: 3,});
}
// 組件卸載前清理資源
onUnmounted(() => {if (viewer) {viewer.destroy();viewer = null;}
});
/*** 創建三維熱力圖* @param {Cesium.Viewer} viewer 地圖viewer對象* @param {Object} options 基礎參數* @param {Array} options.dataPoints 熱力值數組* @param {Array} options.radius 熱力點半徑* @param {Array} options.baseElevation 最低高度* @param {Array} options.colorGradient 顏色配置*/
function create3DHeatmap(viewer, options = {}) {const heatmapState = {viewer,options,dataPoints: options.dataPoints || [],containerElement: undefined,instanceId: Number(`${new Date().getTime()}${Number(Math.random() * 1000).toFixed(0)}`),canvasWidth: 200,boundingBox: undefined, // 四角坐標boundingRect: {}, // 經緯度范圍xAxis: undefined, // x 軸yAxis: undefined, // y 軸xAxisLength: 0, // x軸長度yAxisLength: 0, // y軸長度baseElevation: options.baseElevation || 0,heatmapPrimitive: undefined,positionHierarchy: [],heatmapInstance: null,};if (!heatmapState.dataPoints || heatmapState.dataPoints.length < 2) {console.log("熱力圖點位不得少于3個!");return;}createHeatmapContainer(heatmapState);const heatmapConfig = {container: document.getElementById(`heatmap-${heatmapState.instanceId}`),radius: options.radius || 20,maxOpacity: 0.7,minOpacity: 0,blur: 0.75,gradient: options.colorGradient || {".1": "blue",".5": "yellow",".7": "red",".99": "white",},};heatmapState.primitiveType = options.primitiveType || "TRIANGLES";heatmapState.heatmapInstance = h337.create(heatmapConfig);initializeHeatmap(heatmapState);return {destroy: () => destroyHeatmap(heatmapState),heatmapState,};
}
……