Vue3加vite使用Cesium绘制图形

1 篇文章 0 订阅

Vue3加vite使用Cesium绘制图形

1、项目开发准备

Node版本:16.20.2

1.1创建一个新的工程:my-cesium-app

npm create vite@latest my-cesium-app – --template vue

1.2 安装Element Plus

npm install element-plus --save
// main.js
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
//字体图标注册
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

1.3安装cesium

npm i cesium@1.99 vite-plugin-cesium
// main.js
import * as Cesium from 'cesium';
const app = createApp(App)
app.use(Cesium)

//vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import cesium from 'vite-plugin-cesium'
export default defineConfig({  plugins: [ vue(),cesium()]})

1.4注册Cesium

首先需要去注册一个免费的Cesium ion账户
打开https://ion.cesium.com/ 然后注册一个新的账户。
点击”Access Token”,跳转到Access Tokens page页面。
选择Default默认的access token拷贝到contents中。
在这里插入图片描述

2、使用Draw.js绘制对应图形

2.1基础参数

  constructor(viewer, config) {
    this.viewer = viewer;
    this.config = config || {
      borderColor: Cesium.Color.BLUE,
      borderWidth: 2,
      material: Cesium.Color.GREEN.withAlpha(0.5),
    };
    this.infoDetail = { point: [], line: [], rectangle: [], circle: [], planeSelf: [], sector: [] };
    //判断是否右键销毁
    this.isDestroy = false
    //是否是销毁状态
    this.isRemove = false
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
  }

2.2 绘制点

  drawPoint () {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isDestroy = false
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    this.handler.setInputAction((click) => {
      let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);
      let id = new Date().getTime();
      this.viewer.entities.add({
        position: Cesium.Cartesian3.fromDegrees(lng, lat, 0),
        name: 'point',
        id: id,
        point: {
          color: this.config.material,
          pixelSize: 12,
          outlineColor: this.config.borderColor,
          outlineWidth: this.config.borderWidth,
        },
      });
      this.infoDetail.point.push({ id: id, position: [lng, lat] });
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this.handler.setInputAction((click) => {
      if (this.handler) {
        this.isDestroy = true
        this.handler.destroy();
        //重新点击进行绘制
        this.drawPoint()
      }
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

2.3绘制矩形区域

  /******* 
    * @function: function
    * @description: 绘制矩形区域
    */
  drawRectangle () {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = false
    this.isDestroy = false
    let westSouthEastNorth = [];
    let id = null;
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    this.handler.setInputAction((click) => {
      let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
      let lng1 = Cesium.Math.toDegrees(cartographic.longitude);
      let lat1 = Cesium.Math.toDegrees(cartographic.latitude);
      westSouthEastNorth = [lng1, lat1];
      id = new Date().getTime();
      if (westSouthEastNorth) {
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
      }
      let polygons = this.viewer.entities.add({
        name: 'rectangle',
        id: id,
        polygon: {
          hierarchy: new Cesium.CallbackProperty(function () {
            return {
              positions: Cesium.Cartesian3.fromDegreesArray(westSouthEastNorth),
            };
          }, false),
          height: 0,
          material: this.config.material,
          fill: true,
          show: true,
        },
        polyline: {
          positions: new Cesium.CallbackProperty(function () { return Cesium.Cartesian3.fromDegreesArray(westSouthEastNorth); }, false),
          material: this.config.borderColor,
          width: this.config.borderWidth,
          zIndex: 1,
        },
      });
      this.handler.setInputAction((move) => {
        let cartesian = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
        let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
        let lng = Cesium.Math.toDegrees(cartographic.longitude);
        let lat = Cesium.Math.toDegrees(cartographic.latitude);
        westSouthEastNorth = [lng1, lat1, lng1, lat, lng, lat, lng, lat1, lng1, lat1];
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    //右键完成绘制
    this.handler.setInputAction(() => {
      if (this.handler) {
        this.isDestroy = true
        this.handler.destroy();
        //重新点击进行绘制
        this.drawRectangle()
      }
      this.infoDetail.rectangle.push({ id: id, position: westSouthEastNorth });
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

2.4绘制圆形区域,并计算面积和周长

  drawCircle() {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = false;
    this.isDestroy = false;
    let id = null;
    let radius = 0;
    let lngLat = [];
    let circleEntity = null;

    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);

    this.handler.setInputAction((click) => {
      id = new Date().getTime();
      let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);
      lngLat = [lng, lat];

      circleEntity = this.viewer.entities.add({
        position: new Cesium.CallbackProperty(function () { return Cesium.Cartesian3.fromDegrees(...lngLat, 0); }, false),
        name: 'circle',
        id: id,
        ellipse: {
          height: 0,
          outline: true,
          material: this.config.material,
          outlineColor: this.config.borderColor,
          outlineWidth: this.config.borderWidth,
          semiMajorAxis: new Cesium.CallbackProperty(function () { return radius; }, false),
          semiMinorAxis: new Cesium.CallbackProperty(function () { return radius; }, false),
        },
      });

      this.handler.setInputAction((move) => {
        let cartesian2 = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
        radius = Cesium.Cartesian3.distance(cartesian, cartesian2);
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

      this.handler.setInputAction(() => {
        // 计算面积(πr²)和周长(2πr)
        let area = Math.PI * Math.pow(radius, 2); // 单位:平方米
        let circumference = 2 * Math.PI * radius; // 单位:米

        this.infoDetail.circle.push({
          id: id,
          center: lngLat,
          radius: radius,
          area: area, // 保存面积
          circumference: circumference // 保存周长
        });

        if (this.handler) {
          this.isDestroy = true;
          this.handler.destroy();
          this.drawCircle();
          //this.enableCircleDragging(circleEntity, id); // 启用拖动功能
        }
      }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  }

2.5绘制自定义区域,并计算面积

  drawPlane() {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = false;
    this.isDestroy = false;
    let id = new Date().getTime();
    let positions = [];
    let codeInfo = [];
    let polygon = new Cesium.PolygonHierarchy();
    let _polygonEntity = new Cesium.Entity();
    let polyObj = null;

    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);

    this.handler.setInputAction((movement) => {
      let cartesian = this.viewer.camera.pickEllipsoid(movement.position, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);
      if (cartesian && cartesian.x) {
        if (positions.length == 0) {
          positions.push(cartesian.clone());
        }
        codeInfo.push([lng, lat]);
        positions.push(cartesian.clone());
        polygon.positions.push(cartesian.clone());
        if (!polyObj) {
          _polygonEntity.polyline = {
            width: this.config.borderWidth,
            material: this.config.borderColor,
            clampToGround: false,
          };
          _polygonEntity.polyline.positions = new Cesium.CallbackProperty(function () {
            return positions;
          }, false);
          _polygonEntity.polygon = {
            hierarchy: new Cesium.CallbackProperty(function () {
              return polygon;
            }, false),
            material: this.config.material,
            clampToGround: false,
          };
          _polygonEntity.name = 'planeSelf';
          _polygonEntity._id = id;
          polyObj = this.viewer.entities.add(_polygonEntity);
        }
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    this.handler.setInputAction((movement) => {
      let cartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);
      if (positions.length >= 0) {
        if (cartesian && cartesian.x) {
          positions.pop();
          positions.push(cartesian);
          polygon.positions.pop();
          polygon.positions.push(cartesian);
          codeInfo.pop();
          codeInfo.push([lng, lat]);
        }
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    // 右键完成绘制
    this.handler.setInputAction((movement) => {
      // 计算多边形面积
      let area = this.calculatePolygonArea(positions);

      this.infoDetail.planeSelf.push({
        id: id,
        positions: codeInfo,
        area: area // 保存面积
      });

      if (this.handler) {
        this.isDestroy = true;
        this.handler.destroy();
        this.drawPlane();
        //this.enablePolygonDragging(polyObj, id); // 启用拖动功能
      }
      positions.push(positions[0]);
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

2.6绘制线段并计算其长度

  drawLine () {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = false;
    this.isDestroy = false;
    let id = null;
    let positions = [];
    let codeInfo = [];
    let polyObj = null;
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);

    this.handler.setInputAction((movement) => {
      id = new Date().getTime();
      let cartesian = this.viewer.camera.pickEllipsoid(movement.position, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);

      if (cartesian && cartesian.x) {
        if (positions.length == 0) {
          positions.push(cartesian.clone());
        }
        codeInfo.push([lng, lat]);
        positions.push(cartesian.clone());

        if (!polyObj) {
          polyObj = this.viewer.entities.add({
            polyline: {
              positions: new Cesium.CallbackProperty(() => positions, false),
              width: this.config.borderWidth,
              material: this.config.borderColor,
              clampToGround: false,
            },
            name: 'line',
            id: id,
          });
        }
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    this.handler.setInputAction((movement) => {
      let cartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);

      if (positions.length >= 0) {
        if (cartesian && cartesian.x) {
          positions.pop();
          positions.push(cartesian);
          codeInfo.pop();
          codeInfo.push([lng, lat]);
        }
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    // 右键完成绘制
    this.handler.setInputAction(() => {
      // 计算线段长度
      let length = this.calculateLineLength(positions);

      this.infoDetail.line.push({
        id: id,
        positions: codeInfo,
        length: length // 保存线段长度
      });

      if (this.handler) {
        this.isDestroy = true;
        this.handler.destroy();
        this.drawLine(); // 重新启用绘制功能
      }
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

/******* 
   * @function: function
   * @description: 计算线段的总长度
   */
  calculateLineLength (positions) {
    let totalLength = 0;

    for (let i = 0; i < positions.length - 1; i++) {
      let startCartographic = Cesium.Cartographic.fromCartesian(positions[i]);
      let endCartographic = Cesium.Cartographic.fromCartesian(positions[i + 1]);

      let startPos = Cesium.Cartesian3.fromRadians(
        startCartographic.longitude,
        startCartographic.latitude
      );
      let endPos = Cesium.Cartesian3.fromRadians(
        endCartographic.longitude,
        endCartographic.latitude
      );

      let segmentLength = Cesium.Cartesian3.distance(startPos, endPos);
      totalLength += segmentLength;
    }

    return totalLength; // 返回线段总长度,单位:米
  }

2.7绘制扇形,并计算面积和弧长

  drawSector () {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = false;
    this.isDestroy = false;
    let id = null;
    let center = null;
    let radius = 0;
    let startAngle = 0;
    let endAngle = 0;
    let positions = [];

    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);

    // 第一次点击确定中心点
    this.handler.setInputAction((click) => {
      id = new Date().getTime();
      center = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
      if (!center) return;
      const cartographic = Cesium.Cartographic.fromCartesian(center);
      const lng = Cesium.Math.toDegrees(cartographic.longitude);
      const lat = Cesium.Math.toDegrees(cartographic.latitude);

      let entity = this.viewer.entities.add({
        id: id,
        position: center,
        polygon: {
          hierarchy: new Cesium.CallbackProperty(() => new Cesium.PolygonHierarchy(positions), false),
          material: this.config.material,
          outline: true,
          outlineColor: this.config.borderColor,
          outlineWidth: this.config.borderWidth,
        },
      });

      this.handler.setInputAction((move) => {
        const movePosition = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
        if (!movePosition) return;
        radius = Cesium.Cartesian3.distance(center, movePosition);
        const cartographicMove = Cesium.Cartographic.fromCartesian(movePosition);
        const lngMove = Cesium.Math.toDegrees(cartographicMove.longitude);
        const latMove = Cesium.Math.toDegrees(cartographicMove.latitude);

        endAngle = Math.atan2(latMove - lat, lngMove - lng);

        // 动态计算扇形边界点
        positions = this.computeSectorPositions(center, radius, startAngle, endAngle);
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

      // 设置起始角度
      this.handler.setInputAction((click) => {
        const startCartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
        if (!startCartesian) return;
        const cartographicStart = Cesium.Cartographic.fromCartesian(startCartesian);
        const lngStart = Cesium.Math.toDegrees(cartographicStart.longitude);
        const latStart = Cesium.Math.toDegrees(cartographicStart.latitude);
        startAngle = Math.atan2(latStart - lat, lngStart - lng);

        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    // 右键完成绘制
    this.handler.setInputAction(() => {
      // 计算扇形的面积和弧长
      let area = this.calculateSectorArea(radius, startAngle, endAngle);
      let arcLength = this.calculateArcLength(radius, startAngle, endAngle);

      this.infoDetail.sector.push({
        id: id,
        center: Cesium.Cartographic.fromCartesian(center),
        radius: radius,
        startAngle: startAngle,
        endAngle: endAngle,
        area: area,         // 保存面积
        arcLength: arcLength // 保存弧长
      });

      if (this.handler) {
        this.isDestroy = true;
        this.handler.destroy();
        // 重新启用绘制功能
        this.drawSector();
      }
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

  /******* 
   * @function: function
   * @description: 计算扇形的面积
   */
  calculateSectorArea (radius, startAngle, endAngle) {
    let angleDiff = endAngle - startAngle;
    if (angleDiff < 0) {
      angleDiff += 2 * Math.PI;  // 确保角度差为正
    }
    return 0.5 * Math.pow(radius, 2) * angleDiff;
  }

2.8移除绘制对象,获取绘制对象的数据

  /******* 
     * @function: function
     * @description: 移除绘制对象
     */
  removeEntity () {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = true
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    this.handler.setInputAction((move) => {
      let pick = this.viewer.scene.pick(move.endPosition);
      if (pick && pick.id && pick.id.id && this.isRemove) {
        document.body.style.cursor = 'pointer';
        this.handler.setInputAction((click) => {
          let newPoint;
          switch (pick.id.name) {
            case 'point':
              newPoint = this.infoDetail.point.filter((item) => item.id != pick.id._id);
              this.infoDetail.point = newPoint;
              break;
            case 'line':
              newPoint = this.infoDetail.line.filter((item) => item.id != pick.id._id);
              this.infoDetail.line = newPoint;
              break;
            case 'rectangle':
              newPoint = this.infoDetail.rectangle.filter((item) => item.id != pick.id._id);
              this.infoDetail.rectangle = newPoint;
              break;
            case 'planeSelf':
              newPoint = this.infoDetail.planeSelf.filter((item) => item.id != pick.id._id);
              this.infoDetail.planeSelf = newPoint;
              break;
            case 'circle':
              newPoint = this.infoDetail.circle.filter((item) => item.id != pick.id._id);
              this.infoDetail.circle = newPoint;
              break;
            case 'sector':
              newPoint = this.infoDetail.sector.filter((item) => item.id != pick.id._id);
              this.infoDetail.sector = newPoint;
              break;
            default:
              break;
          }
          this.viewer.entities.remove(pick.id);
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
      } else {
        document.body.style = 'cursor: default;';
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  }
  /******* 
     * @function: function
     * @description: 获取绘制对象
     */
  backInfoDetail () {
    return this.infoDetail;
  }

3、页面代码

<template>
  <div class="map">
    <div class="flex-items search">
      <el-radio-group v-model="btnType"
                      size="large"
                      @change="changeBtn">
        <!-- <el-radio-button label="绘制点"
                     value="drawPoint" /> -->
        <!-- <el-radio-button label="绘制矩形"
                     value="drawRectangle" /> -->
        <el-radio-button label="绘制线段"
                         value="drawLine" />
        <el-radio-button label="绘制多边形"
                         value="drawPlane" />
        <el-radio-button label="绘制圆形"
                         value="drawCircle" />
        <el-radio-button label="扇形"
                         value="drawSector" />
        <el-radio-button label="移除绘制对象"
                         value="removeEntity" />
      </el-radio-group>
      <div class="flex-items">
        <!--输入框形式-->
        <!-- <el-input v-model="coordinates"
                  style="width: 240px"
                  :rows="2"
                  type="textarea"
                  placeholder="请输入坐标集" /> -->
        <el-dropdown @command="addDraw">
          <el-button type="primary"
                     style="margin-right: 10px;">
            生成图形<el-icon class="el-icon--right"><arrow-down /></el-icon>
          </el-button>
          <template #dropdown>
            <el-dropdown-menu>
              <el-dropdown-item command="drawLineFromCoordinates">生成线段</el-dropdown-item>
              <el-dropdown-item command="drawPlaneFromCoordinates">生成多边形</el-dropdown-item>
              <el-dropdown-item command="drawCircleFromCoordinates">生成圆形</el-dropdown-item>
              <el-dropdown-item command="drawSectorFromCoordinates">生成扇形</el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
        <el-button type="success"
                   @click="getData()">获取图形数据</el-button>
      </div>
    </div>
  </div>
  <el-dialog v-model="drawVisible"
             title="生成图形"
             width="680">
    <template v-if="drawInfo.key=='drawCircleFromCoordinates'">
      <el-row :gutter="20">
        <el-col :span="8">
          <div>lng</div>
        </el-col>
        <el-col :span="8">
          <div>lat</div>
        </el-col>
        <el-col :span="8">
          <div>半径(米)</div>
        </el-col>
        <div class="flex-items draw-item"
             v-for="(item,index) in drawInfo.coordinates"
             :key="index">
          <el-col :span="8">
            <el-input v-model="item.lng"
                      placeholder="请输入lng" />
          </el-col>
          <el-col :span="8">
            <el-input v-model="item.lat"
                      placeholder="请输入lat" /> </el-col>
          <el-col :span="8">
            <el-input v-model="item.radius"
                      placeholder="请输入半径">

            </el-input>
          </el-col>

        </div>
      </el-row>

    </template>
    <template v-else-if="drawInfo.key=='drawSectorFromCoordinates'">
      <el-row :gutter="20">
        <el-col :span="6">
          <div>lng</div>
        </el-col>
        <el-col :span="6">
          <div>lat</div>
        </el-col>
        <el-col :span="4">
          <div>半径(米)</div>
        </el-col>
        <el-col :span="4">
          <div>起始角度</div>
        </el-col>
        <el-col :span="4">
          <div>结束角度</div>
        </el-col>
        <div class="flex-items draw-item"
             v-for="(item,index) in drawInfo.coordinates"
             :key="index">
          <el-col :span="6">
            <el-input v-model="item.lng"
                      placeholder="请输入lng" />
          </el-col>
          <el-col :span="6">
            <el-input v-model="item.lat"
                      placeholder="请输入lat" />
          </el-col>
          <el-col :span="4">
            <el-input v-model="item.radius"
                      placeholder="请输入半径">

            </el-input>
          </el-col>
          <el-col :span="4">
            <el-input v-model="item.startAngle"
                      placeholder="请输入">

            </el-input>
          </el-col>
          <el-col :span="4">
            <el-input v-model="item.endAngle"
                      placeholder="请输入">

            </el-input>
          </el-col>

        </div>
      </el-row>
    </template>
    <template v-else>
      <el-row :gutter="20">
        <el-col :span="10">
          <div>lng</div>
        </el-col>
        <el-col :span="10">
          <div>lat</div>
        </el-col>
        <el-col :span="4">
          <div>操作</div>
        </el-col>

        <div class="flex-items draw-item"
             v-for="(item,index) in drawInfo.coordinates"
             :key="index">
          <el-col :span="10">
            <el-input v-model="item.lng"
                      placeholder="请输入lng" />
          </el-col>
          <el-col :span="10">
            <el-input v-model="item.lat"
                      placeholder="请输入lat" />
          </el-col>
          <el-col :span="4">
            <el-button type="danger"
                       circle
                       v-if="drawInfo.coordinates.length>1"
                       @click="delItem(index)">
              <el-icon>
                <delete />
              </el-icon>
            </el-button>
          </el-col>

        </div>
      </el-row>
      <el-button type="primary"
                 @click="addItem">添加坐标</el-button>
    </template>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="drawVisible = false">取消</el-button>
        <el-button type="primary"
                   @click="createDraw()">
          确认生成
        </el-button>
      </div>
    </template>
  </el-dialog>
  <div id="cesiumContainer"
       ref="cesiumContainer"></div>

</template>

<script>
import { onMounted, ref } from 'vue'
import * as Cesium from 'cesium'
import Draw from './Draw' // 确保路径正确
import { ArrowDown, Delete } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'

export default {
  name: 'CesiumMap',
  setup() {
    const btnType = ref('')
    const cesiumContainer = ref(null)
    const coordinates = ref('')
    const drawVisible = ref(false)
    const drawInfo = ref({
      key: '',
      coordinates: [
        { lng: '', lat: '', radius: 0, startAngle: 0, endAngle: 90 },
      ],
      center: [],
      radius: 0,
    })
    let viewer
    let draw

    onMounted(() => {
      // 初始化地球
      Cesium.Ion.defaultAccessToken = '注册的token'

      viewer = new Cesium.Viewer(cesiumContainer.value, {
        geocoder: true,
        homeButton: true, //是否显示首页位置工具
        sceneModePicker: true, //是否显示视角模式切换工具
        baseLayerPicker: true, //是否显示默认图层选择工具
        navigationHelpButton: false, //是否显示导航帮助工具
        animation: false, //是否显示动画工具
        timeline: false, //是否显示时间轴工具
        fullscreenButton: true, //是否显示全屏按钮工具
      })
      //初始化绘制方法
      draw = new Draw(viewer, {
        borderColor: Cesium.Color.RED,
        borderWidth: 2,
        material: Cesium.Color.BLUE.withAlpha(0.5),
      })
    })
    //点击不同的类型按钮,触发不同的绘制效果
    function changeBtn(val) {
      draw[val]()
    }
    //获取绘制的图形信息
    function getData() {
      console.log(draw.backInfoDetail())
    }
    //添加生成图形中的坐标对象
    const addItem = () => {
      drawInfo.value.coordinates.push({
        lng: '',
        lat: '',
        radius: 0,
        startAngle: 0,
        endAngle: 90,
      })
    }
    //删除生成图形中的坐标对象
    const delItem = (index) => {
      drawInfo.value.coordinates.splice(index, 1)
    }
    //弹窗形式,逐个添加坐标对象
    const addDraw = (command) => {
      drawVisible.value = true
      drawInfo.value.key = command
      drawInfo.value.coordinates = [
        { lng: '', lat: '', radius: 0, startAngle: 0, endAngle: 90 },
      ]
    }
    //生成图形
    const createDraw = () => {
      let isEmpty = false
      drawInfo.value.coordinates.forEach((item) => {
        if (item.lng === '' || item.lat === '') {
          isEmpty = true
        }
      })
      if (isEmpty) {
        ElMessage({
          message: '坐标内容不能为空!',
          type: 'warning',
        })
        return
      }
      if (drawInfo.value.key == 'drawCircleFromCoordinates') {
        draw[drawInfo.value.key](
          [
            Number(drawInfo.value.coordinates[0].lng),
            Number(drawInfo.value.coordinates[0].lat),
          ],
          Number(drawInfo.value.coordinates[0].radius)
        )
      } else if (drawInfo.value.key == 'drawSectorFromCoordinates') {
        const startAngle = Cesium.Math.toRadians(
          Number(drawInfo.value.coordinates[0].startAngle)
        ) // 起始角度,0度(东向)
        const endAngle = Cesium.Math.toRadians(
          Number(drawInfo.value.coordinates[0].endAngle)
        ) // 结束角度,90度(北向)
        draw[drawInfo.value.key](
          [
            Number(drawInfo.value.coordinates[0].lng),
            Number(drawInfo.value.coordinates[0].lat),
          ],
          Number(drawInfo.value.coordinates[0].radius),
          startAngle,
          endAngle
        )
      } else {
        let coordinates = []
        drawInfo.value.coordinates.forEach((item) => {
          coordinates.push([Number(item.lng), Number(item.lat)])
        })
        draw[drawInfo.value.key](coordinates)
      }
      drawVisible.value = false
    }
    //输入框形式,直接输入坐标集,生成图形
    const handleCommand = (command) => {
      if (command == 'drawCircleFromCoordinates') {
        const center = [-84.89521051429298, 47.804323372822424] // 圆心的经纬度
        const radius = 944831.9687022993 // 半径,单位:米
        draw[command](center, radius)
      } else if (command == 'drawSectorFromCoordinates') {
        const startAngle = Cesium.Math.toRadians(Number(0)) // 起始角度,0度(东向)
        const endAngle = Cesium.Math.toRadians(Number(90)) // 结束角度,90度(北向)
        const center = [116.391193, 39.907776] // 圆心的经纬度
        const radius = 944831.9687022993 // 半径,单位:米
        draw[command](center, radius, startAngle, endAngle)
      } else {
        // 清理输入字符串
        let cleanedInput = coordinates.value.trim()
        if (!cleanedInput) {
          ElMessage({
            message: '坐标集不能为空!',
            type: 'warning',
          })
          return
        }
        let parsedArray = null
        // 修复尾随逗号
        cleanedInput = cleanedInput.replace(/,\s*([}\]])/g, '$1')

        // 处理其他常见问题
        // 替换单引号为双引号
        cleanedInput = cleanedInput.replace(/'/g, '"')
        // 尝试解析 JSON
        try {
          // 解析字符串为数组
          parsedArray = JSON.parse(cleanedInput)
          draw[command](parsedArray)
        } catch (error) {
          ElMessage.error(error)
        }
      }
    }
    return {
      handleCommand,
      changeBtn,
      getData,
      addItem,
      createDraw,
      delItem,
      addDraw,
      cesiumContainer,
      btnType,
      coordinates,
      drawInfo,
      drawVisible,
    }
  },
}
</script>

<style scoped>
#cesiumContainer {
  width: 100%;
  height: calc(100vh - 120px);
}
.flex-items {
  display: flex;
  align-items: center;
}
.search {
  margin-bottom: 10px;
  justify-content: space-between;
}
.draw-item {
  margin: 10px 0;
  width: 100%;
}
</style>

4页面效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

除了绘制图形之外,还新增了使用通过输入坐标点集合生成上述图形(WGS84 坐标) ,以及圆形、线段、多边形的拖拽,但是拖拽效果不是很理想,只能当做参考

5、完整的Draw.js

// Draw.js 绘制方法
import * as Cesium from 'cesium'
export default class Draw {
  constructor(viewer, config) {
    this.viewer = viewer;
    this.config = config || {
      borderColor: Cesium.Color.BLUE,
      borderWidth: 2,
      material: Cesium.Color.GREEN.withAlpha(0.5),
    };
    this.infoDetail = { point: [], line: [], rectangle: [], circle: [], planeSelf: [], sector: [] };
    //判断是否右键销毁
    this.isDestroy = false
    //是否是销毁状态
    this.isRemove = false
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
  }
  /******* 
    * @function: function
    * @description: 绘制点
    */
  drawPoint () {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isDestroy = false
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    this.handler.setInputAction((click) => {
      let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);
      let id = new Date().getTime();
      this.viewer.entities.add({
        position: Cesium.Cartesian3.fromDegrees(lng, lat, 0),
        name: 'point',
        id: id,
        point: {
          color: this.config.material,
          pixelSize: 12,
          outlineColor: this.config.borderColor,
          outlineWidth: this.config.borderWidth,
        },
      });
      this.infoDetail.point.push({ id: id, position: [lng, lat] });
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this.handler.setInputAction((click) => {
      if (this.handler) {
        this.isDestroy = true
        this.handler.destroy();
        //重新点击进行绘制
        this.drawPoint()
      }
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }
  /******* 
    * @function: function
    * @description: 绘制矩形区域
    */
  drawRectangle () {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = false
    this.isDestroy = false
    let westSouthEastNorth = [];
    let id = null;
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    this.handler.setInputAction((click) => {
      let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
      let lng1 = Cesium.Math.toDegrees(cartographic.longitude);
      let lat1 = Cesium.Math.toDegrees(cartographic.latitude);
      westSouthEastNorth = [lng1, lat1];
      id = new Date().getTime();
      if (westSouthEastNorth) {
        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
      }
      let polygons = this.viewer.entities.add({
        name: 'rectangle',
        id: id,
        polygon: {
          hierarchy: new Cesium.CallbackProperty(function () {
            return {
              positions: Cesium.Cartesian3.fromDegreesArray(westSouthEastNorth),
            };
          }, false),
          height: 0,
          material: this.config.material,
          fill: true,
          show: true,
        },
        polyline: {
          positions: new Cesium.CallbackProperty(function () { return Cesium.Cartesian3.fromDegreesArray(westSouthEastNorth); }, false),
          material: this.config.borderColor,
          width: this.config.borderWidth,
          zIndex: 1,
        },
      });
      this.handler.setInputAction((move) => {
        let cartesian = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
        let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
        let lng = Cesium.Math.toDegrees(cartographic.longitude);
        let lat = Cesium.Math.toDegrees(cartographic.latitude);
        westSouthEastNorth = [lng1, lat1, lng1, lat, lng, lat, lng, lat1, lng1, lat1];
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    //右键完成绘制
    this.handler.setInputAction(() => {
      if (this.handler) {
        this.isDestroy = true
        this.handler.destroy();
        //重新点击进行绘制
        this.drawRectangle()
      }
      this.infoDetail.rectangle.push({ id: id, position: westSouthEastNorth });
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }
  /******* 
   * @function: function
   * @description: 绘制圆形区域,并计算面积和周长,同时支持拖动
   */
  drawCircle() {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = false;
    this.isDestroy = false;
    let id = null;
    let radius = 0;
    let lngLat = [];
    let circleEntity = null;

    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);

    this.handler.setInputAction((click) => {
      id = new Date().getTime();
      let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);
      lngLat = [lng, lat];

      circleEntity = this.viewer.entities.add({
        position: new Cesium.CallbackProperty(function () { return Cesium.Cartesian3.fromDegrees(...lngLat, 0); }, false),
        name: 'circle',
        id: id,
        ellipse: {
          height: 0,
          outline: true,
          material: this.config.material,
          outlineColor: this.config.borderColor,
          outlineWidth: this.config.borderWidth,
          semiMajorAxis: new Cesium.CallbackProperty(function () { return radius; }, false),
          semiMinorAxis: new Cesium.CallbackProperty(function () { return radius; }, false),
        },
      });

      this.handler.setInputAction((move) => {
        let cartesian2 = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
        radius = Cesium.Cartesian3.distance(cartesian, cartesian2);
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

      // 完成圆形绘制后,允许拖动
      this.handler.setInputAction(() => {
        // 计算面积(πr²)和周长(2πr)
        let area = Math.PI * Math.pow(radius, 2); // 单位:平方米
        let circumference = 2 * Math.PI * radius; // 单位:米

        this.infoDetail.circle.push({
          id: id,
          center: lngLat,
          radius: radius,
          area: area, // 保存面积
          circumference: circumference // 保存周长
        });

        if (this.handler) {
          this.isDestroy = true;
          this.handler.destroy();
          this.drawCircle();
          this.enableCircleDragging(circleEntity, id); // 启用拖动功能
        }
      }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  }

  /******* 
   * @function: function
   * @description: 绘制自定义区域,并计算面积,同时支持拖动
   */
  drawPlane() {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = false;
    this.isDestroy = false;
    let id = new Date().getTime();
    let positions = [];
    let codeInfo = [];
    let polygon = new Cesium.PolygonHierarchy();
    let _polygonEntity = new Cesium.Entity();
    let polyObj = null;

    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);

    this.handler.setInputAction((movement) => {
      let cartesian = this.viewer.camera.pickEllipsoid(movement.position, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);
      if (cartesian && cartesian.x) {
        if (positions.length == 0) {
          positions.push(cartesian.clone());
        }
        codeInfo.push([lng, lat]);
        positions.push(cartesian.clone());
        polygon.positions.push(cartesian.clone());
        if (!polyObj) {
          _polygonEntity.polyline = {
            width: this.config.borderWidth,
            material: this.config.borderColor,
            clampToGround: false,
          };
          _polygonEntity.polyline.positions = new Cesium.CallbackProperty(function () {
            return positions;
          }, false);
          _polygonEntity.polygon = {
            hierarchy: new Cesium.CallbackProperty(function () {
              return polygon;
            }, false),
            material: this.config.material,
            clampToGround: false,
          };
          _polygonEntity.name = 'planeSelf';
          _polygonEntity._id = id;
          polyObj = this.viewer.entities.add(_polygonEntity);
        }
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    this.handler.setInputAction((movement) => {
      let cartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);
      if (positions.length >= 0) {
        if (cartesian && cartesian.x) {
          positions.pop();
          positions.push(cartesian);
          polygon.positions.pop();
          polygon.positions.push(cartesian);
          codeInfo.pop();
          codeInfo.push([lng, lat]);
        }
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    // 右键完成绘制
    this.handler.setInputAction((movement) => {
      // 计算多边形面积
      let area = this.calculatePolygonArea(positions);

      this.infoDetail.planeSelf.push({
        id: id,
        positions: codeInfo,
        area: area // 保存面积
      });

      if (this.handler) {
        this.isDestroy = true;
        this.handler.destroy();
        this.drawPlane();
        this.enablePolygonDragging(polyObj, id); // 启用拖动功能
      }
      positions.push(positions[0]);
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

  /******* 
   * @function: function
   * @description: 绘制线段并计算其长度
   */
  drawLine () {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = false;
    this.isDestroy = false;
    let id = null;
    let positions = [];
    let codeInfo = [];
    let polyObj = null;
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);

    this.handler.setInputAction((movement) => {
      id = new Date().getTime();
      let cartesian = this.viewer.camera.pickEllipsoid(movement.position, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);

      if (cartesian && cartesian.x) {
        if (positions.length == 0) {
          positions.push(cartesian.clone());
        }
        codeInfo.push([lng, lat]);
        positions.push(cartesian.clone());

        if (!polyObj) {
          polyObj = this.viewer.entities.add({
            polyline: {
              positions: new Cesium.CallbackProperty(() => positions, false),
              width: this.config.borderWidth,
              material: this.config.borderColor,
              clampToGround: false,
            },
            name: 'line',
            id: id,
          });
        }
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    this.handler.setInputAction((movement) => {
      let cartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
      let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
      let lng = Cesium.Math.toDegrees(cartographic.longitude);
      let lat = Cesium.Math.toDegrees(cartographic.latitude);

      if (positions.length >= 0) {
        if (cartesian && cartesian.x) {
          positions.pop();
          positions.push(cartesian);
          codeInfo.pop();
          codeInfo.push([lng, lat]);
        }
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    // 右键完成绘制
    this.handler.setInputAction(() => {
      // 计算线段长度
      let length = this.calculateLineLength(positions);

      this.infoDetail.line.push({
        id: id,
        positions: codeInfo,
        length: length // 保存线段长度
      });

      if (this.handler) {
        this.isDestroy = true;
        this.handler.destroy();
        this.drawLine(); // 重新启用绘制功能
      }
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }
  /******* 
   * @function: function
   * @description: 计算扇形的面积
   */
  calculateSectorArea (radius, startAngle, endAngle) {
    let angleDiff = endAngle - startAngle;
    if (angleDiff < 0) {
      angleDiff += 2 * Math.PI;  // 确保角度差为正
    }
    return 0.5 * Math.pow(radius, 2) * angleDiff;
  }

  /******* 
   * @function: function
   * @description: 计算扇形的弧长
   */
  calculateArcLength (radius, startAngle, endAngle) {
    let angleDiff = endAngle - startAngle;
    return radius * angleDiff; // 单位:米
  }

  /******* 
   * @function: function
   * @description: 计算扇形的边界点
   */
  computeSectorPositions (center, radius, startAngle, endAngle) {
    const positions = [];
    const numPoints = 50; // 调整为更平滑或更粗糙的扇形
    const angleDiff = endAngle - startAngle;

    for (let i = 0; i <= numPoints; i++) {
      const angle = startAngle + (i * angleDiff) / numPoints;
      const x = center.x + radius * Math.cos(angle);
      const y = center.y + radius * Math.sin(angle);
      positions.push(new Cesium.Cartesian3(x, y, center.z));
    }

    positions.push(center);
    return positions;
  }

  /******* 
   * @function: function
   * @description: 绘制扇形,并计算面积和弧长
   */
  drawSector () {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = false;
    this.isDestroy = false;
    let id = null;
    let center = null;
    let radius = 0;
    let startAngle = 0;
    let endAngle = 0;
    let positions = [];

    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);

    // 第一次点击确定中心点
    this.handler.setInputAction((click) => {
      id = new Date().getTime();
      center = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
      if (!center) return;
      const cartographic = Cesium.Cartographic.fromCartesian(center);
      const lng = Cesium.Math.toDegrees(cartographic.longitude);
      const lat = Cesium.Math.toDegrees(cartographic.latitude);

      let entity = this.viewer.entities.add({
        id: id,
        position: center,
        polygon: {
          hierarchy: new Cesium.CallbackProperty(() => new Cesium.PolygonHierarchy(positions), false),
          material: this.config.material,
          outline: true,
          outlineColor: this.config.borderColor,
          outlineWidth: this.config.borderWidth,
        },
      });

      this.handler.setInputAction((move) => {
        const movePosition = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
        if (!movePosition) return;
        radius = Cesium.Cartesian3.distance(center, movePosition);
        const cartographicMove = Cesium.Cartographic.fromCartesian(movePosition);
        const lngMove = Cesium.Math.toDegrees(cartographicMove.longitude);
        const latMove = Cesium.Math.toDegrees(cartographicMove.latitude);

        endAngle = Math.atan2(latMove - lat, lngMove - lng);

        // 动态计算扇形边界点
        positions = this.computeSectorPositions(center, radius, startAngle, endAngle);
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

      // 设置起始角度
      this.handler.setInputAction((click) => {
        const startCartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
        if (!startCartesian) return;
        const cartographicStart = Cesium.Cartographic.fromCartesian(startCartesian);
        const lngStart = Cesium.Math.toDegrees(cartographicStart.longitude);
        const latStart = Cesium.Math.toDegrees(cartographicStart.latitude);
        startAngle = Math.atan2(latStart - lat, lngStart - lng);

        this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    // 右键完成绘制
    this.handler.setInputAction(() => {
      // 计算扇形的面积和弧长
      let area = this.calculateSectorArea(radius, startAngle, endAngle);
      let arcLength = this.calculateArcLength(radius, startAngle, endAngle);

      this.infoDetail.sector.push({
        id: id,
        center: Cesium.Cartographic.fromCartesian(center),
        radius: radius,
        startAngle: startAngle,
        endAngle: endAngle,
        area: area,         // 保存面积
        arcLength: arcLength // 保存弧长
      });

      if (this.handler) {
        this.isDestroy = true;
        this.handler.destroy();
        // 重新启用绘制功能
        this.drawSector();
      }
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

  /******* 
   * @function: function
   * @description: 通过坐标数组生成线段并计算长度
   */
  drawLineFromCoordinates (coordinates) {
    let id = new Date().getTime();
    let positions = coordinates.map(coord => {
      return Cesium.Cartesian3.fromDegrees(coord[0], coord[1]);
    });

    // 计算线段长度
    let length = this.calculateLineLength(positions);

    this.infoDetail.line.push({
      id: id,
      positions: coordinates,
      length: length // 保存线段长度
    });

    let _polygonEntity = new Cesium.Entity();
    _polygonEntity.polyline = {
      width: this.config.borderWidth,
      material: this.config.borderColor,
      clampToGround: false,
      positions: positions,
    };
    _polygonEntity.name = 'line';
    _polygonEntity._id = id;
    this.viewer.entities.add(_polygonEntity);
  }  /******* 
   * @function: function
   * @description: 计算线段的总长度
   */
  calculateLineLength (positions) {
    let totalLength = 0;

    for (let i = 0; i < positions.length - 1; i++) {
      let startCartographic = Cesium.Cartographic.fromCartesian(positions[i]);
      let endCartographic = Cesium.Cartographic.fromCartesian(positions[i + 1]);

      let startPos = Cesium.Cartesian3.fromRadians(
        startCartographic.longitude,
        startCartographic.latitude
      );
      let endPos = Cesium.Cartesian3.fromRadians(
        endCartographic.longitude,
        endCartographic.latitude
      );

      let segmentLength = Cesium.Cartesian3.distance(startPos, endPos);
      totalLength += segmentLength;
    }

    return totalLength; // 返回线段总长度,单位:米
  }
  /******* 
  /******* 
   * @function: function
   * @description: 通过坐标数组生成自定义区域,并计算面积
   */
  drawPlaneFromCoordinates (coordinates) {
    let id = new Date().getTime();
    let positions = coordinates.map(coord => {
      return Cesium.Cartesian3.fromDegrees(coord[0], coord[1]);
    });

    // 关闭区域,用第一个点作为最后一个点
    positions.push(positions[0]);

    // 计算多边形面积
    let area = this.calculatePolygonArea(positions);

    this.infoDetail.planeSelf.push({
      id: id,
      positions: coordinates,
      area: area // 保存面积
    });

    let polygonHierarchy = new Cesium.PolygonHierarchy(positions);
    let _polygonEntity = new Cesium.Entity();

    _polygonEntity.polyline = {
      width: this.config.borderWidth,
      material: this.config.borderColor,
      clampToGround: false,
      positions: positions,
    };

    _polygonEntity.polygon = {
      hierarchy: polygonHierarchy,
      material: this.config.material,
      clampToGround: false,
    };

    _polygonEntity.name = 'planeSelf';
    _polygonEntity._id = id;

    this.viewer.entities.add(_polygonEntity);
    this.enablePolygonDragging(_polygonEntity, id); // 启用拖动功能
  }
  /******* 
   * @function: function
   * @description: 启用多边形的拖动功能
   */
  enablePolygonDragging(polygonEntity, id) {
    this.dragHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);

    let isDragging = false;
    let originalPositions = null;

    this.dragHandler.setInputAction((click) => {
      const pickedObject = this.viewer.scene.pick(click.position);
      if (Cesium.defined(pickedObject) && pickedObject.id === polygonEntity) {
        isDragging = true;
        originalPositions = pickedObject.id.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
      }
    }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

    this.dragHandler.setInputAction((movement) => {
      if (isDragging && polygonEntity) {
        const newCartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
        if (newCartesian) {
          const deltaX = newCartesian.x - originalPositions[0].x;
          const deltaY = newCartesian.y - originalPositions[0].y;
          const deltaZ = newCartesian.z - originalPositions[0].z;

          const newPositions = originalPositions.map((position) => {
            return new Cesium.Cartesian3(
              position.x + deltaX,
              position.y + deltaY,
              position.z + deltaZ
            );
          });

          // 更新多边形和折线的位置信息
          polygonEntity.polygon.hierarchy = new Cesium.CallbackProperty(() => {
            return new Cesium.PolygonHierarchy(newPositions);
          }, false);

          polygonEntity.polyline.positions = new Cesium.CallbackProperty(() => {
            return newPositions;
          }, false);

          // 实时更新 infoDetail 中多边形的位置
          const polygonInfo = this.infoDetail.planeSelf.find(plane => plane.id === id);
          if (polygonInfo) {
            polygonInfo.positions = newPositions.map(pos => {
              const cartographic = Cesium.Cartographic.fromCartesian(pos);
              return [
                Cesium.Math.toDegrees(cartographic.longitude),
                Cesium.Math.toDegrees(cartographic.latitude)
              ];
            });
          }
        }
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    this.dragHandler.setInputAction(() => {
      isDragging = false;
    }, Cesium.ScreenSpaceEventType.LEFT_UP);
  }

  /******* 
   * @function: function
   * @description: 启用圆形的拖动功能
   */
  enableCircleDragging(circleEntity, id) {
    this.dragHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);

    let isDragging = false;
    let originalPosition = null;

    this.dragHandler.setInputAction((click) => {
      const pickedObject = this.viewer.scene.pick(click.position);
      if (Cesium.defined(pickedObject) && pickedObject.id === circleEntity) {
        isDragging = true;
        originalPosition = pickedObject.id.position.getValue(Cesium.JulianDate.now());
      }
    }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

    this.dragHandler.setInputAction((movement) => {
      if (isDragging && circleEntity) {
        const newCartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
        if (newCartesian) {
          circleEntity.position = new Cesium.CallbackProperty(() => newCartesian, false);

          const newCartographic = Cesium.Cartographic.fromCartesian(newCartesian);
          const newLngLat = [
            Cesium.Math.toDegrees(newCartographic.longitude),
            Cesium.Math.toDegrees(newCartographic.latitude)
          ];

          // 实时更新 infoDetail 中圆形的位置
          const circleInfo = this.infoDetail.circle.find(circle => circle.id === id);
          if (circleInfo) {
            circleInfo.center = newLngLat;
          }
        }
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    this.dragHandler.setInputAction(() => {
      if (isDragging && circleEntity) {
        const finalPosition = circleEntity.position.getValue(Cesium.JulianDate.now());
        const finalCartographic = Cesium.Cartographic.fromCartesian(finalPosition);
        const finalLngLat = [
          Cesium.Math.toDegrees(finalCartographic.longitude),
          Cesium.Math.toDegrees(finalCartographic.latitude)
        ];

        // 结束拖动后,更新 infoDetail 中的圆心坐标
        const circleInfo = this.infoDetail.circle.find(circle => circle.id === id);
        if (circleInfo) {
          circleInfo.center = finalLngLat;
        }
      }
      isDragging = false;
    }, Cesium.ScreenSpaceEventType.LEFT_UP);
  }
  /******* 
   * @function: function
   * @description: 通过坐标和半径生成圆形区域,并计算面积和周长
   */
  drawCircleFromCoordinates (center, radius) {
    let id = new Date().getTime();

    // 将中心点转换为 Cartesian3 坐标
    let centerCartesian = Cesium.Cartesian3.fromDegrees(center[0], center[1]);

    // 计算面积(πr²)和周长(2πr)
    let area = Math.PI * Math.pow(radius, 2); // 单位:平方米
    let circumference = 2 * Math.PI * radius; // 单位:米

    this.infoDetail.circle.push({
      id: id,
      center: center,
      radius: radius,
      area: area, // 保存面积
      circumference: circumference // 保存周长
    });

    let entity = this.viewer.entities.add({
      position: centerCartesian,
      name: 'circle',
      id: id,
      ellipse: {
        semiMajorAxis: radius,
        semiMinorAxis: radius,
        height: 0,
        outline: true,
        material: this.config.material,
        outlineColor: this.config.borderColor,
        outlineWidth: this.config.borderWidth,
      },
    });
      // 启用拖动功能
      this.enableCircleDragging(entity, id);
  }
  /******* 
      * @function: function
      * @description: 通过坐标、半径、起始角度和结束角度生成扇形,并计算面积和弧长
      */
  drawSectorFromCoordinates (center, radius, startAngle, endAngle) {
    let id = new Date().getTime();

    // 将中心点转换为 Cartesian3 坐标
    let centerCartesian = Cesium.Cartesian3.fromDegrees(center[0], center[1]);

    // 计算扇形的边界点
    let positions = this.computeSectorPositions(centerCartesian, radius, startAngle, endAngle);

    // 计算扇形的面积和弧长
    let area = this.calculateSectorArea(radius, startAngle, endAngle);
    let arcLength = this.calculateArcLength(radius, startAngle, endAngle);

    this.infoDetail.sector.push({
      id: id,
      center: center,
      radius: radius,
      startAngle: startAngle,
      endAngle: endAngle,
      area: area,         // 保存面积
      arcLength: arcLength // 保存弧长
    });

    let entity = this.viewer.entities.add({
      polygon: {
        hierarchy: new Cesium.PolygonHierarchy(positions),
        material: this.config.material,
        outline: true,
        outlineColor: this.config.borderColor,
        outlineWidth: this.config.borderWidth,
      },
      name: 'sector',
      id: id,
    });
  }
  /******* 
     * @function: function
     * @description: 移除绘制对象
     */
  removeEntity () {
    if (this.handler && !this.isDestroy) {
      this.handler.destroy();
    }
    this.isRemove = true
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    this.handler.setInputAction((move) => {
      let pick = this.viewer.scene.pick(move.endPosition);
      if (pick && pick.id && pick.id.id && this.isRemove) {
        document.body.style.cursor = 'pointer';
        this.handler.setInputAction((click) => {
          let newPoint;
          switch (pick.id.name) {
            case 'point':
              newPoint = this.infoDetail.point.filter((item) => item.id != pick.id._id);
              this.infoDetail.point = newPoint;
              break;
            case 'line':
              newPoint = this.infoDetail.line.filter((item) => item.id != pick.id._id);
              this.infoDetail.line = newPoint;
              break;
            case 'rectangle':
              newPoint = this.infoDetail.rectangle.filter((item) => item.id != pick.id._id);
              this.infoDetail.rectangle = newPoint;
              break;
            case 'planeSelf':
              newPoint = this.infoDetail.planeSelf.filter((item) => item.id != pick.id._id);
              this.infoDetail.planeSelf = newPoint;
              break;
            case 'circle':
              newPoint = this.infoDetail.circle.filter((item) => item.id != pick.id._id);
              this.infoDetail.circle = newPoint;
              break;
            case 'sector':
              newPoint = this.infoDetail.sector.filter((item) => item.id != pick.id._id);
              this.infoDetail.sector = newPoint;
              break;
            default:
              break;
          }
          this.viewer.entities.remove(pick.id);
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
      } else {
        document.body.style = 'cursor: default;';
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  }

  backInfoDetail () {
    return this.infoDetail;
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值