Cesium实战:交互式多边形绘制与编辑功能完全指南(最终修复版)

📋 文章目录

引言

Cesium作为一款强大的3D地理信息可视化库,在WebGIS开发中有着广泛的应用。本文将详细介绍如何在Vue框架下基于Cesium实现交互式多边形绘制与编辑功能,包括颜色自定义、透明度调整以及面积实时计算等实用功能,并解决开发过程中遇到的各种常见问题。

无论是GIS应用、智慧城市还是工程测量系统,多边形绘制都是核心功能之一。通过本文的实现方案,你将能够快速集成专业的多边形编辑工具到自己的Cesium项目中,并掌握Cesium事件处理和状态管理的最佳实践。

功能概述

本文实现的多边形工具具有以下特性:

交互式绘制 :左键点击添加顶点,右键结束绘制 ✅ 顶点编辑 :拖拽顶点实时调整多边形形状 ✅ 样式自定义 :支持颜色选择和透明度调整 ✅ 面积计算 :自动计算并显示多边形面积(平方公里) ✅ 交互控制:绘制/编辑时禁用地图默认交互,完成后恢复

环境准备

在开始之前,请确保你的项目中已安装以下依赖:

复制代码
# 安装Cesium
npm install cesium --save

# 安装Turf.js用于面积计算
npm install @turf/turf --save

核心实现步骤

1. 地图初始化

首先,我们需要初始化Cesium地图实例。在Vue组件的mounted钩子中完成地图的加载:

复制代码
import initMap from '@/config/initMap.js';
import { mapConfig } from '@/config/mapConfig';

export default {
  data() {
    return {
      viewer: null,
      // 绘制状态管理
      isDrawing: false,
      isEditing: false,
      currentPolygon: null,
      polygonPoints: [],
      dynamicPoints: [],
      vertexEntities: [],
      vertexHandlers: [], // 顶点事件处理器数组
      handler: null,
      polygonColor: '#0000FF', // 默认蓝色
      polygonAlpha: 0.5,       // 默认透明度
      areaLabel: null          // 面积标签实体
    };
  },
  mounted() {
    // 初始化地图
    this.viewer = initMap(mapConfig.gaode.url3, false);
    // 初始化绘制处理器
    this.initDrawHandler();
  }
}

2. 多边形绘制

多边形绘制是通过监听鼠标事件实现的,主要分为三个阶段:

  1. 左键点击:添加顶点并更新多边形
  2. 鼠标移动:动态显示多边形轮廓
  3. 右键点击:结束绘制并创建最终多边形

核心代码实现:

复制代码
// 开始绘制多边形
startDrawPolygon() {
  if (this.isEditing) this.stopEditPolygon();
  
  this.isDrawing = true;
  this.polygonPoints = [];
  this.dynamicPoints = [];
  this.disableMapInteraction();
  
  // 左键点击添加顶点
  this.handler.setInputAction(this.handleLeftClick, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  // 鼠标移动更新动态轮廓
  this.handler.setInputAction(this.handleMouseMove, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  // 右键结束绘制
  this.handler.setInputAction(this.handleRightClick, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  
  // 创建动态多边形实体
  this.createDynamicPolygon();
},

// 创建动态多边形(绘制过程中的临时多边形)
createDynamicPolygon() {
  // 移除已存在的动态多边形
  if (this.currentPolygon) {
    this.viewer.entities.remove(this.currentPolygon);
  }
  
  this.currentPolygon = this.viewer.entities.add({
    polygon: {
      hierarchy: new Cesium.CallbackProperty(() => {
        return new Cesium.PolygonHierarchy(this.dynamicPoints);
      }, false),
      material: Cesium.Color.RED.withAlpha(0.3),
      outline: true,
      outlineColor: Cesium.Color.RED
    }
  });
}

3. 顶点编辑功能

编辑功能允许用户通过拖拽顶点来调整多边形形状,实现思路是为每个顶点创建可交互的点实体,并监听其拖拽事件:

复制代码
// 为每个顶点添加拖拽事件
addVertexDragHandler(vertexEntity, index) {
  const vertexHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
  
  // 将处理器存储到数组,以便后续销毁
  this.vertexHandlers.push(vertexHandler);
  
  vertexHandler.setInputAction((event) => {
    const pick = this.viewer.scene.pick(event.position);
    if (Cesium.defined(pick) && pick.id === vertexEntity) {
      // 开始拖拽
      const moveHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
      
      moveHandler.setInputAction((moveEvent) => {
        const newCartesian = this.getCartesianFromMouse(moveEvent.endPosition);
        if (newCartesian) {
          // 更新顶点位置
          this.polygonPoints[index] = newCartesian;
          vertexEntity.position.setValue(newCartesian);
          // 更新多边形
          this.currentPolygon.polygon.hierarchy.setValue(
            new Cesium.PolygonHierarchy(this.polygonPoints)
          );
          // 更新面积显示
          this.calculateAndShowArea();
        }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
      
      // 结束拖拽
      vertexHandler.setInputAction(() => {
        moveHandler.destroy();
      }, Cesium.ScreenSpaceEventType.LEFT_UP);
    }
  }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
}

4. 颜色与透明度自定义

通过添加颜色选择器和透明度滑块,允许用户自定义多边形样式:

复制代码
// 更新多边形样式
updatePolygonStyle() {
  if (!this.currentPolygon) return;
  
  try {
    const color = Cesium.Color.fromCssColorString(this.polygonColor)
      .withAlpha(this.polygonAlpha);
    this.currentPolygon.polygon.material = color;
  } catch (e) {
    console.error('颜色解析错误:', e);
    // 使用默认颜色作为fallback
    this.currentPolygon.polygon.material = Cesium.Color.BLUE.withAlpha(0.5);
  }
}

5. 面积计算与显示

使用Turf.js库计算多边形面积,并在多边形中心显示面积标签:

复制代码
// 计算并显示多边形面积
calculateAndShowArea() {
  try {
    if (this.polygonPoints.length < 3) return;
    
    // 将笛卡尔坐标转换为经纬度
    const coordinates = this.polygonPoints.map(cartesian => {
      const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
      return [
        Cesium.Math.toDegrees(cartographic.longitude),
        Cesium.Math.toDegrees(cartographic.latitude)
      ];
    });
    
    // 闭合多边形
    coordinates.push(coordinates[0]);
    
    // 使用Turf.js计算面积
    const polygon = turf.polygon([coordinates]);
    const areaSquareMeters = turf.area(polygon);
    const areaSquareKm = areaSquareMeters / 1000000;
    const areaText = `面积: ${areaSquareKm.toFixed(4)} 平方公里`;
    
    // 显示面积标签
    this.showAreaLabel(areaText);
  } catch (e) {
    console.error('面积计算错误:', e);
    this.showAreaLabel('面积计算失败');
  }
},

// 在多边形中心显示面积标签
showAreaLabel(text) {
  // 移除旧标签
  if (this.areaLabel) {
    this.viewer.entities.remove(this.areaLabel);
  }
  
  // 计算多边形中心点
  const center = this.calculatePolygonCenter();
  
  // 创建新标签
  this.areaLabel = this.viewer.entities.add({
    position: center,
    label: {
      text: text,
      font: '16px sans-serif',
      fillColor: Cesium.Color.YELLOW,
      backgroundColor: Cesium.Color.BLACK.withAlpha(0.7),
      padding: new Cesium.Cartesian2(12, 8),
      horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
      verticalOrigin: Cesium.VerticalOrigin.CENTER,
      disableDepthTestDistance: Number.POSITIVE_INFINITY,
      outline: true,
      outlineColor: Cesium.Color.WHITE,
      outlineWidth: 1
    }
  });
}

常见问题解决方案

在开发过程中,我们可能会遇到以下问题:

1. 多边形颜色显示异常

问题:设置颜色后多边形显示为白色或不生效。

解决方案

  • 确保颜色值是有效的CSS颜色字符串

  • 检查透明度值是否为数字类型

  • 添加错误处理和默认颜色 fallback

    // 安全的颜色设置方法
    try {
    const color = Cesium.Color.fromCssColorString(this.polygonColor)
    .withAlpha(Number(this.polygonAlpha) || 0.5); // 确保alpha为数字
    this.currentPolygon.polygon.material = color;
    } catch (e) {
    console.error('颜色解析错误:', e);
    // 使用默认颜色作为fallback
    this.currentPolygon.polygon.material = Cesium.Color.BLUE.withAlpha(0.5);
    }

2. 面积标签不可见

问题:面积标签不显示或被地形遮挡。

解决方案

  • 提升标签高度,避免被地形遮挡

  • 使用醒目颜色和背景提高可见性

  • 设置disableDepthTestDistance确保标签始终显示在最前面

    // 计算中心点时提升高度
    calculatePolygonCenter() {
    // ... 经纬度计算逻辑 ...

    复制代码
    // 将中心点转换回笛卡尔坐标,并提升高度
    return Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 50); // 提升50米高度

    }

3. 控制台alpha类型错误

问题Expected alpha to be typeof number, actual typeof was string

解决方案

  • 确保透明度值为数字类型

  • 在Vue中使用.number修饰符或手动转换类型

4. 地图交互无法恢复

问题:结束绘制或编辑后,地图依然不能缩放或移动。

解决方案

  • 增强交互恢复方法,确保所有控制参数被正确设置
  • 管理所有事件处理器的生命周期,确保完全销毁
  • 在所有退出路径调用交互恢复方法
复制代码
复制代码
// 恢复地图交互(增强版)
enableMapInteraction() {
  const controller = this.viewer.scene.screenSpaceCameraController;
  
  // 确保所有输入被启用
  controller.enableInputs = true;
  
  // 恢复所有相机控制
  controller.enableRotate = true;
  controller.enableZoom = true;
  controller.enableTranslate = true;
  controller.enableTilt = true;
  controller.enableLook = true;
  
  console.log("地图交互已完全恢复");
},

// 停止编辑多边形(修复版)
stopEditPolygon() {
  this.isEditing = false;
  
  // 销毁所有顶点拖拽事件处理器
  this.vertexHandlers.forEach(handler => {
    if (handler && !handler.isDestroyed()) {
      handler.destroy();
    }
  });
  this.vertexHandlers = []; // 清空处理器数组
  
  this.enableMapInteraction();
}

完整代码

Vue组件代码

复制代码
<template>
  <div id="cesiumContainer" style="width: 100%; height: 100vh">
    <div class="polygon-controls">
      <label>多边形颜色:</label>
      <input type="color" v-model="polygonColor" @input="updatePolygonStyle" />

      <label>透明度:</label>
      <input
        type="range"
        min="0"
        max="1"
        step="0.1"
        v-model.number="polygonAlpha"
        @input="updatePolygonStyle"
      />
    </div>
    <div class="">结束绘制</div>
  </div>
</template>

<script>
import initMap from '@/config/initMap.js';
import { mapConfig } from '@/config/mapConfig';
// import * as Cesium from 'cesium';
import * as turf from '@turf/turf';

export default {
  data() {
    return {
      viewer: null,
      // 绘制状态管理
      isDrawing: false,
      isEditing: false,
      currentPolygon: null,
      polygonPoints: [],
      dynamicPoints: [],
      vertexEntities: [],
      handler: null,
      polygonColor: '#0000FF', // 默认蓝色
      polygonAlpha: 0.5, // 默认透明度
      areaLabel: null, // 面积标签实体
      vertexHandlers: [], // 新增:存储顶点拖拽事件处理器
    };
  },
  mounted() {
    this.viewer = initMap(mapConfig.gaode.url3, false);
    this.initDrawHandler();
  },
  methods: {
    // 初始化绘制事件处理器
    initDrawHandler() {
      this.handler = new Cesium.ScreenSpaceEventHandler(
        this.viewer.scene.canvas
      );
      this.startDrawPolygon();
    },

    // 开始绘制多边形
    startDrawPolygon() {
      console.log('开始绘制多边形');
      if (this.isEditing) this.stopEditPolygon();

      this.isDrawing = true;
      this.polygonPoints = [];
      this.dynamicPoints = [];
      this.disableMapInteraction();

      // 左键点击添加顶点
      this.handler.setInputAction(
        this.handleLeftClick,
        Cesium.ScreenSpaceEventType.LEFT_CLICK
      );
      // 鼠标移动更新动态轮廓
      this.handler.setInputAction(
        this.handleMouseMove,
        Cesium.ScreenSpaceEventType.MOUSE_MOVE
      );
      // 右键结束绘制
      this.handler.setInputAction(
        this.handleRightClick,
        Cesium.ScreenSpaceEventType.RIGHT_CLICK
      );

      // 创建动态多边形实体
      this.createDynamicPolygon();
    },

    // 处理左键点击添加顶点
    handleLeftClick(event) {
      const cartesian = this.getCartesianFromMouse(event.position);
      if (!cartesian) return;

      this.polygonPoints.push(cartesian);
      this.dynamicPoints.push(cartesian);

      // 添加顶点标记
      this.addVertexMarker(cartesian);
    },

    // 处理鼠标移动更新轮廓
    handleMouseMove(event) {
      if (this.polygonPoints.length === 0 || !this.isDrawing) return;

      const cartesian = this.getCartesianFromMouse(event.endPosition);
      if (!cartesian) return;

      // 更新动态点
      if (this.dynamicPoints.length > this.polygonPoints.length) {
        this.dynamicPoints.pop();
      }
      this.dynamicPoints.push(cartesian);
    },

    // 处理右键结束绘制
    handleRightClick() {
      if (this.polygonPoints.length < 3) {
        alert('至少需要3个顶点才能形成多边形');
        this.clearDrawing();
        return;
      }

      // 移除动态点
      this.dynamicPoints.pop();
      this.isDrawing = false;

      // 保存最终多边形
      this.saveFinalPolygon();

      // 清除临时事件
      this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
      this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
      this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);

      // 开启编辑模式
      this.startEditPolygon();

      // 确保交互已恢复(双重保险)
      setTimeout(() => {
        this.enableMapInteraction();
      }, 100);
    },

    // 开始编辑多边形
    startEditPolygon() {
      this.isEditing = true;
      this.disableMapInteraction();

      // 为每个顶点添加拖拽事件
      this.vertexEntities.forEach((vertex, index) => {
        this.addVertexDragHandler(vertex, index);
      });
    },

    // 添加顶点拖拽事件
    addVertexDragHandler(vertexEntity, index) {
      const vertexHandler = new Cesium.ScreenSpaceEventHandler(
        this.viewer.scene.canvas
      );

      // 将处理器存储到数组,以便后续销毁
      this.vertexHandlers.push(vertexHandler);

      vertexHandler.setInputAction((event) => {
        const pick = this.viewer.scene.pick(event.position);
        if (Cesium.defined(pick) && pick.id === vertexEntity) {
          // 开始拖拽
          const moveHandler = new Cesium.ScreenSpaceEventHandler(
            this.viewer.scene.canvas
          );

          moveHandler.setInputAction((moveEvent) => {
            const newCartesian = this.getCartesianFromMouse(
              moveEvent.endPosition
            );
            if (newCartesian) {
              // 更新顶点位置
              this.polygonPoints[index] = newCartesian;
              vertexEntity.position.setValue(newCartesian);
              // 更新多边形
              this.currentPolygon.polygon.hierarchy.setValue(
                new Cesium.PolygonHierarchy(this.polygonPoints)
              );
              // 更新面积显示
              this.calculateAndShowArea();
            }
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

          // 结束拖拽
          vertexHandler.setInputAction(() => {
            moveHandler.destroy();
          }, Cesium.ScreenSpaceEventType.LEFT_UP);
        }
      }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
    },

    // 停止编辑多边形
    stopEditPolygon() {
      this.isEditing = false;
      // 销毁所有顶点拖拽事件处理器
      this.vertexHandlers.forEach((handler) => {
        if (handler && !handler.isDestroyed()) {
          handler.destroy();
        }
      });
      this.vertexHandlers = []; // 清空处理器数组

      this.enableMapInteraction();
    },

    // 创建动态多边形
    createDynamicPolygon() {
      // 移除已存在的动态多边形
      if (this.currentPolygon) {
        this.viewer.entities.remove(this.currentPolygon);
      }

      this.currentPolygon = this.viewer.entities.add({
        polygon: {
          hierarchy: new Cesium.CallbackProperty(() => {
            return new Cesium.PolygonHierarchy(this.dynamicPoints);
          }, false),
          material: Cesium.Color.RED.withAlpha(0.3), // 绘制过程中使用红色
          outline: true,
          outlineColor: Cesium.Color.RED,
        },
      });
    },

    // 保存最终多边形(修复重复定义问题)
    saveFinalPolygon() {
      // 移除动态多边形
      this.viewer.entities.remove(this.currentPolygon);

      try {
        // 验证颜色和透明度
        console.log(
          '当前颜色值:',
          this.polygonColor,
          '透明度:',
          this.polygonAlpha
        );
        const color = Cesium.Color.fromCssColorString(
          this.polygonColor
        ).withAlpha(Number(this.polygonAlpha) || 0.5);

        // 创建最终多边形,应用自定义颜色和透明度
        this.currentPolygon = this.viewer.entities.add({
          polygon: {
            hierarchy: new Cesium.PolygonHierarchy(this.polygonPoints),
            material: color,
            outline: true,
            outlineColor: Cesium.Color.BLACK,
            outlineWidth: 2,
          },
        });

        // 计算并显示面积
        this.calculateAndShowArea();
      } catch (e) {
        console.error('创建多边形失败:', e);
        // 使用默认颜色作为fallback
        this.currentPolygon = this.viewer.entities.add({
          polygon: {
            hierarchy: new Cesium.PolygonHierarchy(this.polygonPoints),
            material: Cesium.Color.BLUE.withAlpha(0.5),
            outline: true,
            outlineColor: Cesium.Color.BLACK,
          },
        });
      }
    },

    // 添加顶点标记
    addVertexMarker(position) {
      const vertexEntity = this.viewer.entities.add({
        position: position,
        point: {
          pixelSize: 10,
          color: Cesium.Color.YELLOW,
          outlineColor: Cesium.Color.BLACK,
          outlineWidth: 2,
          disableDepthTestDistance: Number.POSITIVE_INFINITY,
        },
      });
      this.vertexEntities.push(vertexEntity);
    },

    // 从鼠标位置获取笛卡尔坐标
    getCartesianFromMouse(position) {
      const ray = this.viewer.camera.getPickRay(position);
      if (!ray) return null;
      return this.viewer.scene.globe.pick(ray, this.viewer.scene);
    },

    // 清除绘制状态
    clearDrawing() {
      this.isDrawing = false;
      this.polygonPoints = [];
      this.dynamicPoints = [];

      if (this.currentPolygon) {
        this.viewer.entities.remove(this.currentPolygon);
      }

      this.vertexEntities.forEach((vertex) =>
        this.viewer.entities.remove(vertex)
      );
      this.vertexEntities = [];

      if (this.areaLabel) {
        this.viewer.entities.remove(this.areaLabel);
        this.areaLabel = null;
      }

      this.enableMapInteraction();
    },

    // 更新多边形样式
    updatePolygonStyle() {
      if (!this.currentPolygon) return;

      try {
        const color = Cesium.Color.fromCssColorString(
          this.polygonColor
        ).withAlpha(Number(this.polygonAlpha) || 0.5);

        this.currentPolygon.polygon.material = color;
        console.log('样式更新成功:', color);
      } catch (e) {
        console.error('颜色解析错误:', e);
        this.currentPolygon.polygon.material = Cesium.Color.BLUE.withAlpha(0.5);
      }
    },

    // 计算并显示多边形面积
    calculateAndShowArea() {
      try {
        if (this.polygonPoints.length < 3) return;

        // 将笛卡尔坐标转换为经纬度
        const coordinates = this.polygonPoints.map((cartesian) => {
          const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
          return [
            Cesium.Math.toDegrees(cartographic.longitude),
            Cesium.Math.toDegrees(cartographic.latitude),
          ];
        });

        // 确保多边形闭合
        if (
          coordinates.length > 0 &&
          !(
            coordinates[0][0] === coordinates[coordinates.length - 1][0] &&
            coordinates[0][1] === coordinates[coordinates.length - 1][1]
          )
        ) {
          coordinates.push([...coordinates[0]]);
        }

        // 使用Turf.js计算面积
        const polygon = turf.polygon([coordinates]);
        const areaSquareMeters = turf.area(polygon);
        const areaSquareKm = areaSquareMeters / 1000000;
        const areaText = `面积: ${areaSquareKm.toFixed(4)} 平方公里`;
        console.log('计算面积:', areaText);

        // 显示面积标签
        this.showAreaLabel(areaText);
      } catch (e) {
        console.error('面积计算错误:', e);
        this.showAreaLabel('面积计算失败');
      }
    },

    // 在多边形中心显示面积标签(优化版本)
    showAreaLabel(text) {
      // 移除旧标签
      if (this.areaLabel) {
        this.viewer.entities.remove(this.areaLabel);
      }

      // 计算多边形中心点(优化算法)
      const center = this.calculatePolygonCenter();

      // 创建新标签,增强可见性
      this.areaLabel = this.viewer.entities.add({
        position: center,
        label: {
          text: text,
          font: '16px sans-serif',
          fillColor: Cesium.Color.YELLOW, // 使用醒目颜色
          backgroundColor: Cesium.Color.BLACK.withAlpha(0.8), // 增强对比度
          padding: new Cesium.Cartesian2(12, 8),
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
          verticalOrigin: Cesium.VerticalOrigin.CENTER,
          disableDepthTestDistance: Number.POSITIVE_INFINITY, // 始终显示在最前面
          pixelOffset: new Cesium.Cartesian2(0, 0),
          outline: true,
          outlineColor: Cesium.Color.WHITE,
          outlineWidth: 1,
        },
      });
    },

    // 计算多边形中心点(更可靠的方法)
    calculatePolygonCenter() {
      if (this.polygonPoints.length === 0) return Cesium.Cartesian3.ZERO;

      // 计算经纬度平均值
      let totalLon = 0,
        totalLat = 0;
      const cartographics = this.polygonPoints.map((cartesian) =>
        Cesium.Cartographic.fromCartesian(cartesian)
      );

      cartographics.forEach((cartographic) => {
        totalLon += Cesium.Math.toDegrees(cartographic.longitude);
        totalLat += Cesium.Math.toDegrees(cartographic.latitude);
      });

      const centerLon = totalLon / cartographics.length;
      const centerLat = totalLat / cartographics.length;

      // 将中心点转换回笛卡尔坐标,并提升高度避免被地形遮挡
      return Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 50);
    },

    // 禁用地图交互
    disableMapInteraction() {
      const controller = this.viewer.scene.screenSpaceCameraController;
      controller.enableRotate = false;
      controller.enableZoom = false;
      controller.enableTranslate = false;
      controller.enableTilt = false;
      controller.enableLook = false;
    },

    // 恢复地图交互
    enableMapInteraction() {
      const controller = this.viewer.scene.screenSpaceCameraController;

      // 确保所有输入被启用
      controller.enableInputs = true;

      // 恢复所有相机控制
      controller.enableRotate = true;
      controller.enableZoom = true;
      controller.enableTranslate = true;
      controller.enableTilt = true;
      controller.enableLook = true;

      // 重置鼠标事件处理
      if (this.handler) {
        this.handler.destroy();
        this.handler = new Cesium.ScreenSpaceEventHandler(
          this.viewer.scene.canvas
        );
      }

      console.log('地图交互已完全恢复');
    },
  },
  beforeDestroy() {
    if (this.viewer) {
      this.viewer.destroy();
    }
    if (this.handler) {
      this.handler.destroy();
    }
    // 移除控制面板
    const controlPanel = document.querySelector('.polygon-controls');
    if (controlPanel) {
      controlPanel.remove();
    }
  },
};
</script>

CSS样式

复制代码
<style lang="scss" scoped>
#cesiumContainer {
  width: 100%;
  height: 100vh;
  touch-action: none;
  .polygon-controls {
    position: absolute;
    top: 20px;
    right: 20px;
    z-index: 1000;
    background: white;
    padding: 15px;
    border-radius: 5px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
  }

  .polygon-controls label {
    display: block;
    margin: 10px 0 5px;
    font-weight: bold;
  }

  .polygon-controls input {
    width: 100%;
    margin-bottom: 10px;
  }
}
</style>

总结与扩展

本文详细介绍了如何基于Cesium和Vue实现交互式多边形绘制与编辑功能,包括核心功能实现、样式自定义和面积计算等关键技术点。特别解决了开发过程中常见的颜色显示异常、标签不可见、类型错误和交互无法恢复等问题。

通过事件监听、动态属性更新和地理空间计算,我们构建了一个功能完善的多边形编辑工具。重点强调了事件处理器生命周期管理和状态控制的最佳实践,这些经验对于开发复杂Cesium交互功能具有普遍参考价值。

功能扩展方向

  1. 添加删除功能:允许用户删除多边形或单个顶点
  2. 支持多个多边形:管理多个多边形图层
  3. 导入导出:支持GeoJSON格式导入导出
  4. 测量工具:添加距离测量、角度测量等功能
  5. 样式库:预设多种多边形样式供选择
  6. 撤销/重做:实现操作历史记录功能

希望本文能帮助你快速掌握Cesium多边形绘制技术,如果有任何问题或建议,欢迎在评论区留言讨论!

原创不易,转载请注明出处
如果本文对你有帮助,别忘了点赞和关注哦!

相关推荐
前端 贾公子13 分钟前
pnpm 的 resolution-mode 配置 ( pnpm 的版本解析)
前端
伍哥的传说1 小时前
React 自定义Hook——页面或元素滚动到底部监听 Hook
前端·react.js·前端框架
麦兜*3 小时前
Spring Boot 集成Reactive Web 性能优化全栈技术方案,包含底层原理、压测方法论、参数调优
java·前端·spring boot·spring·spring cloud·性能优化·maven
Jinkxs3 小时前
JavaScript性能优化实战技术
开发语言·javascript·性能优化
知了一笑3 小时前
独立开发第二周:构建、执行、规划
java·前端·后端
UI前端开发工作室3 小时前
数字孪生技术为UI前端提供新视角:产品性能的实时模拟与预测
大数据·前端
Sapphire~3 小时前
重学前端004 --- html 表单
前端·html
Maybyy4 小时前
力扣242.有效的字母异位词
java·javascript·leetcode
遇到困难睡大觉哈哈4 小时前
CSS中的Element语法
前端·css
Real_man4 小时前
新物种与新法则:AI重塑开发与产品未来
前端·后端·面试