乐于分享
好东西不私藏

vue+cesium示例:透视分析(附源码下载)

vue+cesium示例:透视分析(附源码下载)

基于cesium和vue实现透视分析效果,适合学习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>  <divid="cesiumContainer"class="cesium-container"></div>    <divclass="toolsDiv">    <el-buttontype="danger" @click="clearAll">      清除透视分析    </el-button>  </div></template><scriptsetup>import { onMounted, onUnmounted, ref } from 'vue';import * as Cesium from 'cesium';// 使用loadScripts函数加载viewshed相关JS文件Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxZjhjYjhkYS1jMzA1LTQ1MTEtYWE1Mi0zODc5NDljOGVkMDYiLCJpZCI6MTAzNjEsInNjb3BlcyI6WyJhc2wiLCJhc3IiLCJhc3ciLCJnYyJdLCJpYXQiOjE1NzA2MDY5ODV9.X7tj92tunUvx6PkDpj3LFsMVBs_SBYyKbIL_G9xKESA';// 声明Cesium Viewer实例let viewer = null;let start = null;let end = null;let hello = null;let word = null;let line1 = null;let line2 = null;let clickHandler = null;let clickStage = 0;// 组件挂载后初始化CesiumonMounted(async () => {  initMap();});const initMap = async () => {  // 初始化Cesium Viewer  viewer = new Cesium.Viewer('cesiumContainer', {    // 基础配置    animationfalse// 动画小部件    baseLayerPickerfalse// 底图选择器    fullscreenButtonfalse// 全屏按钮    vrButtonfalse// VR按钮    geocoderfalse// 地理编码搜索框    homeButtonfalse// 主页按钮    infoBoxfalse// 信息框    sceneModePickerfalse// 场景模式选择器    selectionIndicatorfalse// 选择指示器    timelinefalse// 时间轴    navigationHelpButtonfalse// 导航帮助按钮    navigationInstructionsInitiallyVisiblefalse// 导航说明初始可见性    scene3DOnlyfalse// 仅3D场景    contextOptions: {      requestWebgl1true,    }  });  // 隐藏logo  viewer.cesiumWidget.creditContainer.style.display = "none";  //前提先把场景上的图层全部移除或者隐藏   viewer.scene.globe.baseColor = new Cesium.Color(0.00.10.21.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);  // 加载Cesium在线地形  const terrainProvider = await Cesium.CesiumTerrainProvider.fromIonAssetId(3956);  viewer.terrainProvider = terrainProvider;  // 启用地形照明效果  viewer.scene.globe.enableLighting = true;  // 启用深度测试,确保地形正确渲染  viewer.scene.globe.depthTestAgainstTerrain = true;  // 设置默认视图位置 - 默认显示全球视图  viewer.camera.setView({    destinationCesium.Cartesian3.fromDegrees(98.68533127.7803257318.6),    orientation: {      headingCesium.Math.toRadians(73),      pitchCesium.Math.toRadians(-52.2),      roll0.0    }  });  setupClickHandler();}// 抬高位置:沿法线方向增加 offset 米(默认 10)function raisePosition(cartesian, offset = 10) {  const cartographic = Cesium.Cartographic.fromCartesian(cartesian);  cartographic.height += offset;  return Cesium.Cartesian3.fromRadians(    cartographic.longitude,    cartographic.latitude,    cartographic.height  );}const setupClickHandler = () => {  if (clickHandler) {    clickHandler.destroy();  }  clickHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);  clickHandler.setInputAction((movement) => {    const picked = pickCartesian(viewer, movement.position);    if (!picked || !picked.cartesian) {      return;    }    if (clickStage === 0) {      start = picked.cartesian;                     // 原始坐标,用于通视计算      const raisedStart = raisePosition(start);      // 抬高后的坐标,用于显示      if (hello) {        hello.position = raisedStart;      } else {        hello = viewer.entities.add({          name'观测点',          position: raisedStart,          point: {            pixelSize5,            colorCesium.Color.RED,            outlineColorCesium.Color.WHITE,            outlineWidth2,            // 可选:禁用深度测试,确保点始终可见(与抬高并用更保险)            disableDepthTestDistanceNumber.POSITIVE_INFINITY,          },          label: {            text'观测点',            font'14pt monospace',            outlineWidth2,            pixelOffsetnew Cesium.Cartesian2(0, -20), // 将标签放在点上方            verticalOriginCesium.VerticalOrigin.BOTTOM,          }        });      }      clearLine();      clickStage = 1;    } else {      end = picked.cartesian;                        // 原始坐标      const raisedEnd = raisePosition(end);      if (word) {        word.position = raisedEnd;      } else {        word = viewer.entities.add({          name'目的点',          position: raisedEnd,          point: {            pixelSize5,            colorCesium.Color.RED,            outlineColorCesium.Color.WHITE,            outlineWidth2,            disableDepthTestDistanceNumber.POSITIVE_INFINITY,          },          label: {            text'目的点',            font'14pt monospace',            outlineWidth2,            pixelOffsetnew Cesium.Cartesian2(0, -20),            verticalOriginCesium.VerticalOrigin.BOTTOM,          }        });      }      clearLine();      loadLine();      clickStage = 0;    }  }, Cesium.ScreenSpaceEventType.LEFT_CLICK);}const loadLine = () => {  if (!start || !end) {    return;  }  const center = sightline(start, end);  // return;  console.log("障碍点坐标-------------------------" + center)  if (center.x == 0 && center.y == 0 && center.z == 0) {    // alert("可视")    line1 = viewer.entities.add({      polyline: {        positions: [start, end],        width3,        materialCesium.Color.GREEN,        clampToGroundfalse,      }    });  } else {    // alert("不可视")    line1 = viewer.entities.add({      polyline: {        positions: [start, center],        width3,        materialCesium.Color.GREEN,        clampToGroundfalse,      }    });    line2 = viewer.entities.add({      polyline: {        positions: [center, end],        width3,        materialCesium.Color.RED,        clampToGroundfalse,      }    });  }}const clearAll = () => {  clearLine();  clearPoint();}const clearLine = () => {  if (line1) {    viewer.entities.remove(line1);    line1 = null;  }  if (line2) {    viewer.entities.remove(line2);    line2 = null;  }}const clearPoint = () => {  if (hello) {    viewer.entities.remove(hello);    hello = null;  }  if (word) {    viewer.entities.remove(word);    word = null;  }}function sightline(startWorldPoint, endWorldPoint) {  var barrierPoint = Cesium.Cartesian3.ZERO;  var startPoint = convertCartesian3ToCartesian2(viewer, startWorldPoint);  var endPoint = convertCartesian3ToCartesian2(viewer, endWorldPoint);  var worldLength = calculateSpatialDistance(startWorldPoint, endWorldPoint);  var windowLength = calculateWindowDistance(startPoint, endPoint);  var worldInterval = worldLength / 100.0;  var windowInterval = windowLength / 100.0;  for (var i = 1; i < 100; i++) {    var tempWindowPoint = findWindowPositionByPixelInterval(startPoint, endPoint, windowInterval * i);    var tempPoint = findCartesian3ByDistance(startWorldPoint, endWorldPoint, worldInterval * i);    var surfacePoint = pickCartesian(viewer, tempWindowPoint);    var tempRad = Cesium.Cartographic.fromCartesian(tempPoint);    var surfaceRad = Cesium.Cartographic.fromCartesian(surfacePoint.cartesian);    if (surfaceRad.height > tempRad.height) {      barrierPoint = tempPoint;      break;    }  }  return barrierPoint;}function convertCartesian3ToCartesian2(viewer, position) {  return Cesium.SceneTransforms.worldToWindowCoordinates(viewer.scene, position);}function calculateSpatialDistance(startPoint, endPoint) {  return Math.sqrt(Math.pow(endPoint.x - startPoint.x2) + Math.pow(endPoint.y - startPoint.y2) + Math.pow(endPoint.z - startPoint.z2));}function calculateWindowDistance(startPoint, endPoint) {  return Math.sqrt(Math.pow(endPoint.y - startPoint.y2) + Math.pow(endPoint.x - startPoint.x2));}function findWindowPositionByPixelInterval(startPosition, endPosition, interval) {  var result = new Cesium.Cartesian2(00);  var length = Math.sqrt(Math.pow(endPosition.x - startPosition.x2) + Math.pow(endPosition.y - startPosition.y2));  if (length < interval) {    return result;  }  else {    var x = (interval / length) * (endPosition.x - startPosition.x) + startPosition.x;    var y = (interval / length) * (endPosition.y - startPosition.y) + startPosition.y;    result.x = x;    result.y = y;  }  return result;}function findCartesian3ByDistance(startPosition, endPosition, interval) {  var result = new Cesium.Cartesian3(000);  var length = Math.sqrt(Math.pow(endPosition.z - startPosition.z2) + Math.pow(endPosition.x - startPosition.x2) + Math.pow(endPosition.y - startPosition.y2));  if (length < interval) {    return result;  }  else {    var x = (interval / length) * (endPosition.x - startPosition.x) + startPosition.x;    var y = (interval / length) * (endPosition.y - startPosition.y) + startPosition.y;    var z = (interval / length) * (endPosition.z - startPosition.z) + startPosition.z;    result.x = x;    result.y = y;    result.z = z;  }  return result;}function pickCartesian(viewer, windowPosition) {  //根据窗口坐标,从场景的深度缓冲区中拾取相应的位置,返回笛卡尔坐标。  var cartesianModel = viewer.scene.pickPositionSupported ? viewer.scene.pickPosition(windowPosition) : undefined;  //场景相机向指定的鼠标位置(屏幕坐标)发射射线  var ray = viewer.camera.getPickRay(windowPosition);  //获取射线与三维球相交的点(即该鼠标位置对应的三维球坐标点,因为模型不属于球面的物体,所以无法捕捉模型表面)  var cartesianTerrain = Cesium.defined(ray) ? viewer.scene.globe.pick(ray, viewer.scene) : undefined;  // var result = new PickResult();  var result = {};  if (Cesium.defined(cartesianModel) || Cesium.defined(cartesianTerrain)) {    result.cartesian = cartesianModel || cartesianTerrain;    result.CartesianModel = cartesianModel;    result.cartesianTerrain = cartesianTerrain;    result.windowCoordinates = windowPosition.clone();    //坐标不一致,证明是模型,采用绝对高度。否则是地形,用贴地模式。    if (Cesium.defined(cartesianModel) && Cesium.defined(cartesianTerrain)) {      result.altitudeMode = cartesianModel.z.toFixed(0) !== cartesianTerrain.z.toFixed(0) ? Cesium.HeightReference.NONE : Cesium.HeightReference.CLAMP_TO_GROUND;    } else {      result.altitudeMode = Cesium.defined(cartesianModel) ? Cesium.HeightReference.NONE : Cesium.HeightReference.CLAMP_TO_GROUND;    }  }  return result;}// 组件卸载前清理资源onUnmounted(() => {  // 销毁viewer  if (viewer && !viewer.isDestroyed()) {    try {      if (clickHandler) {        clickHandler.destroy();        clickHandler = null;      }      viewer.destroy();    } catch (error) {      console.error('Error destroying viewer:', error);    } finally {      viewer = null;    }  }});</script><stylescoped>.cesium-container {  width100%;  height100vh;  margin0;  padding0;  overflow: hidden;}.toolsDiv {  position: absolute;  top10px;  left10px;  button {    margin-right10px;  }}</style>

源码下载

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » vue+cesium示例:透视分析(附源码下载)

评论 抢沙发

3 + 8 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮