cesium 测量距离

效果

源码 

// 辅助函数:计算两点间距离(米)
function getDistanceBetweenPoints(point1: Cesium.Cartesian3, point2: Cesium.Cartesian3): number {
    return Cesium.Cartesian3.distance(point1, point2);
}

// 辅助函数:计算多点总距离(米)
function spaceDistance(points: Cesium.Cartesian3[]): string {
    if (points.length < 2) return '0.000';
    let totalDistance = 0;
    for (let i = 0; i < points.length - 1; i++) {
        totalDistance += getDistanceBetweenPoints(points[i], points[i + 1]);
    }
    return totalDistance.toFixed(3);
}

export default class CesiumMap {
    // ... 原有属性省略 ...

    // 距离测量相关属性
    private isMeasuringDistance = false; // 是否正在距离测量
    private measureDistancePositions: Cesium.Cartesian3[] = []; // 测量点坐标
    private tempMeasurePositions: Cesium.Cartesian3[] = []; // 临时点(用于鼠标移动预览)
    private distanceVertexEntities: Cesium.Entity[] = []; // 测量顶点实体
    private distanceLineEntity: Cesium.Entity | null = null; // 测量线实体
    private distanceLabelEntity: Cesium.Entity | null = null; // 距离标签实体
    private measureTotalDistance = 0; // 总距离测量结果
    private moveVertexEntity: Cesium.Entity | null = null; // 移动时的临时点

    // ... 原有方法省略 ...


    // ---------------------- 距离测量相关方法 ----------------------

    /**
     * 启动距离测量
     */
    public startDistanceMeasure() {
        this.clearDistanceMeasureEntities(); // 清除历史数据
        this.isMeasuringDistance = true;
        this.measureDistancePositions = [];
        this.tempMeasurePositions = [];
        this.viewer._element.style.cursor = 'crosshair'; // 测量时显示十字光标
        console.log('开始距离测量:左键点击添加点,右键结束测量');
    }

    /**
     * 处理距离测量的左键点击
     */
    private handleDistanceMeasureLeftClick(e: Cesium.ScreenSpaceEventHandler.PositionedEvent) {
        // 获取点击位置坐标
        let position = this.viewer.scene.pickPosition(e.position);
        if (!position) {
            const ellipsoid = this.viewer.scene.globe.ellipsoid;
            position = this.viewer.scene.camera.pickEllipsoid(e.position, ellipsoid);
        }
        if (!position) return;

        this.measureDistancePositions.push(position);

        // 第一个点:创建线和起点标识
        if (this.measureDistancePositions.length === 1) {
            this.createDistanceLineEntity();
            this.createDistanceStartEntity();
            return;
        }

        // 后续点:创建顶点和分段距离标签
        this.createDistanceVertexEntity();
    }

    /**
     * 处理距离测量的鼠标移动(实时预览)
     */
    private handleDistanceMeasureMouseMove(e: Cesium.ScreenSpaceEventHandler.MotionEvent) {
        if (!this.isMeasuringDistance || this.measureDistancePositions.length === 0) return;

        // 获取鼠标移动位置坐标
        let position = this.viewer.scene.pickPosition(e.endPosition);
        if (!position) {
            position = this.viewer.scene.camera.pickEllipsoid(
                e.endPosition,
                this.viewer.scene.globe.ellipsoid
            );
        }
        if (!position) return;

        // 更新临时点数组(用于预览线)
        this.tempMeasurePositions = [...this.measureDistancePositions, position];
    }

    /**
     * 结束距离测量(右键触发)
     */
    private finishDistanceMeasure() {
        if (this.measureDistancePositions.length < 2) {
            // 少于2个点时视为取消
            this.cancelDistanceMeasure();
            return;
        }

        // 创建终点标识和总距离标签
        this.createDistanceEndEntity();
        this.isMeasuringDistance = false;
        this.viewer._element.style.cursor = 'default';
        this.measureTotalDistance = parseFloat(spaceDistance(this.measureDistancePositions));
        console.log(`距离测量完成,总距离:${this.measureTotalDistance.toFixed(3)} 米`);
    }

    /**
     * 取消距离测量
     */
    private cancelDistanceMeasure() {
        this.isMeasuringDistance = false;
        this.clearDistanceMeasureEntities();
        this.viewer._element.style.cursor = 'default';
        console.log('已取消距离测量');
    }

    /**
     * 清除距离测量相关实体
     */
    private clearDistanceMeasureEntities() {
        // 清除线实体
        if (this.distanceLineEntity) {
            this.viewer.entities.remove(this.distanceLineEntity);
            this.distanceLineEntity = null;
        }

        // 清除所有顶点实体
        this.distanceVertexEntities.forEach(entity => {
            this.viewer.entities.remove(entity);
        });
        this.distanceVertexEntities = [];

        // 清除临时移动点
        if (this.moveVertexEntity) {
            this.viewer.entities.remove(this.moveVertexEntity);
            this.moveVertexEntity = null;
        }

        // 重置测量数据
        this.measureDistancePositions = [];
        this.tempMeasurePositions = [];
        this.measureTotalDistance = 0;
    }

    /**
     * 创建测量线实体(支持实时预览)
     */
    private createDistanceLineEntity() {
        this.distanceLineEntity = this.viewer.entities.add({
            polyline: {
                positions: new Cesium.CallbackProperty(() => {
                    // 优先显示临时点(鼠标移动预览),否则显示已选点
                    return this.tempMeasurePositions.length > 0 
                        ? this.tempMeasurePositions 
                        : this.measureDistancePositions;
                }, false),
                width: 2,
                material: Cesium.Color.YELLOW,
                depthFailMaterial: Cesium.Color.YELLOW // 被遮挡时仍显示黄色
            }
        });
    }

    /**
     * 创建起点实体(第一个点的标识)
     */
    private createDistanceStartEntity() {
        const startPoint = this.measureDistancePositions[0];
        const entity = this.viewer.entities.add({
            position: startPoint,
            type: "MeasureDistanceStart",
            billboard: {
                image: "../../static/images/start.png", // 起点图标
                scaleByDistance: new Cesium.NearFarScalar(300, 1, 1200, 0.4),
                distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 10000),
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM
            },
            point: {
                color: Cesium.Color.FUCHSIA,
                pixelSize: 6
            }
        });
        this.distanceVertexEntities.push(entity);
    }

    /**
     * 创建测量顶点实体(中间点和距离标签)
     */
    private createDistanceVertexEntity() {
        const currentIndex = this.measureDistancePositions.length - 1;
        const currentPoint = this.measureDistancePositions[currentIndex];
        const prevPoint = this.measureDistancePositions[currentIndex - 1];

        // 计算当前段距离
        const segmentDistance = getDistanceBetweenPoints(prevPoint, currentPoint);

        // 创建顶点
        const entity = this.viewer.entities.add({
            position: currentPoint,
            id: `MeasureDistanceVertex_${currentIndex}`,
            type: "MeasureDistanceVertex",
            point: {
                color: Cesium.Color.FUCHSIA,
                pixelSize: 8,
                disableDepthTestDistance: 500
            },
            label: {
                text: `${segmentDistance.toFixed(3)} 米`, // 显示当前段距离
                scale: 0.5,
                font: 'normal 24px Microsoft YaHei',
                distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 5000),
                scaleByDistance: new Cesium.NearFarScalar(1000, 1, 3000, 0.4),
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                pixelOffset: new Cesium.Cartesian2(0, -30),
                outlineWidth: 2,
                outlineColor: Cesium.Color.WHITE
            }
        });
        this.distanceVertexEntities.push(entity);
    }

    /**
     * 创建终点实体(包含总距离标签)
     */
    private createDistanceEndEntity() {
        const endPoint = this.measureDistancePositions[this.measureDistancePositions.length - 1];
        
        // 移除最后一个临时顶点的标签(替换为终点标签)
        const lastVertexId = `MeasureDistanceVertex_${this.measureDistancePositions.length - 1}`;
        const lastVertex = this.viewer.entities.getById(lastVertexId);
        if (lastVertex) {
            this.viewer.entities.remove(lastVertex);
        }

        // 清除移动时的临时点
        if (this.moveVertexEntity) {
            this.viewer.entities.remove(this.moveVertexEntity);
            this.moveVertexEntity = null;
        }

        // 创建终点实体
        const entity = this.viewer.entities.add({
            position: endPoint,
            type: "MeasureDistanceEnd",
            billboard: {
                image: "../../static/images/end.png", // 终点图标
                scaleByDistance: new Cesium.NearFarScalar(300, 1, 1200, 0.4),
                distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 10000),
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM
            },
            point: {
                color: Cesium.Color.FUCHSIA,
                pixelSize: 6
            },
            label: {
                text: `总距离:${spaceDistance(this.measureDistancePositions)} 米`, // 总距离
                scale: 0.5,
                font: 'normal 26px Microsoft YaHei',
                distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 5000),
                scaleByDistance: new Cesium.NearFarScalar(1000, 1, 3000, 0.4),
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                pixelOffset: new Cesium.Cartesian2(0, -50),
                outlineWidth: 2,
                outlineColor: Cesium.Color.WHITE,
                eyeOffset: new Cesium.Cartesian3(0, 0, -10)
            }
        });
        this.distanceVertexEntities.push(entity);
    }


    // ---------------------- 事件绑定整合 ----------------------

    /**
     * 鼠标左键点击事件(整合距离测量逻辑)
     */
    private mapLeftClick(e: Cesium.ScreenSpaceEventHandler.PositionedEvent) {
        const r = this.getPickAndPosition(e.position);
        
        // 优先处理距离测量
        if (this.isMeasuringDistance) {
            this.handleDistanceMeasureLeftClick(e);
            return;
        }

        // 其次处理高度测量(原有逻辑)
        if (this.isMeasuringHeight) {
            this.handleMeasureLeftClick(e);
            return;
        }

        // ... 其他左键逻辑 ...
    }

    /**
     * 鼠标移动事件(整合距离测量逻辑)
     */
    private mouseMoveEvent(e: Cesium.ScreenSpaceEventHandler.MotionEvent) {
        const r = this.getPickAndPosition(e.endPosition);
        
        // 优先处理距离测量
        if (this.isMeasuringDistance) {
            this.handleDistanceMeasureMouseMove(e);
            return;
        }

        // 其次处理高度测量(原有逻辑)
        if (this.isMeasuringHeight) {
            this.handleMeasureMouseMove(e);
            return;
        }

        // ... 其他鼠标移动逻辑 ...
    }

    /**
     * 鼠标右键点击事件(整合距离测量逻辑)
     */
    private mapRightClick(e: Cesium.ScreenSpaceEventHandler.PositionedEvent) {
        const r = this.getPickAndPosition(e.position);
        
        // 优先处理距离测量
        if (this.isMeasuringDistance) {
            this.finishDistanceMeasure();
            return;
        }

        // 其次处理高度测量(原有逻辑)
        if (this.isMeasuringHeight) {
            this.cancelHeightMeasure();
            return;
        }

        // ... 其他右键逻辑 ...
    }
}

居于上一篇笔记高度测量的基础上,写的距离测量,一样使用

整合说明

  1. 属性隔离
    距离测量使用独立的属性(如 isMeasuringDistancemeasureDistancePositions),与高度测量属性完全隔离,避免状态冲突。

  2. 功能完善

    • 支持多点连续测量(左键点击添加点,右键结束)
    • 实时预览测量线(鼠标移动时动态更新)
    • 显示分段距离和总距离标签
    • 起点 / 终点使用图标标识,增强视觉区分
  3. 事件优先级
    在 mapLeftClickmouseMoveEventmapRightClick 中,优先判断距离测量状态,再处理高度测量,确保多测量模式不冲突。

  4. 潜在问题修复

    • 修复原 MeasureDistance 类中 mouseMoveEvent 事件参数类型错误(使用 e.endPosition 而非 e.startPosition
    • 完善资源清理逻辑,避免实体残留
    • 统一鼠标样式控制(测量时为十字光标,结束后恢复默认)
使用方法 
// 启动距离测量(绑定到按钮点击)
document.getElementById('startDistanceMeasureBtn').addEventListener('click', () => {
    cesiumMap.startDistanceMeasure();
});

 

使用Cesium测量距离的完整代码如下: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Measure Distance using Cesium</title> <script src="https://cesium.com/downloads/cesiumjs/releases/1.81/Build/Cesium/Cesium.js"></script> <style> @import url(https://cesium.com/downloads/cesiumjs/releases/1.81/Build/Cesium/Widgets/widgets.css); #cesiumContainer { width: 100%; height: 800px; } </style> </head> <body> <div id="cesiumContainer"></div> <script> Cesium.Ion.defaultAccessToken = "YOUR_CESIUM_ION_ACCESS_TOKEN"; var viewer = new Cesium.Viewer("cesiumContainer"); // 创建测量工具 var measureHandler = new Cesium.MeasureHandler(viewer, Cesium.MeasureMode.Distance); // 监听测量结束事件 measureHandler.measureEvent.addEventListener(function(result) { console.log("测量结果:", result.distance + " " + result.unit); }); // 开始测量 measureHandler.activate(); // 清除测量结果 function clearMeasurements() { measureHandler.clear(); } // 添加清除按钮 var toolbar = document.getElementById("toolbar"); var clearButton = document.createElement("button"); clearButton.textContent = "清除测量结果"; clearButton.onclick = clearMeasurements; toolbar.appendChild(clearButton); </script> </body> </html> ``` 上述代码使用Cesium库实现测量距离的功能。首先,需要将`<script>`标签中的`YOUR_CESIUM_ION_ACCESS_TOKEN`替换为您的Cesium Ion访问令牌。然后,创建一个Cesium.Viewer实例,并指定在页面上显示地图的div元素的id为"cesiumContainer"。 然后,创建一个Cesium.MeasureHandler实例,将其模式设置为距离测量模式(MeasureMode.Distance)。通过监听`measureEvent`事件,可以获取测量结果,并在控制台中打印出来。 通过调用`measureHandler.activate()`方法,测量工具被激活,用户可以在地图上点击和拖动来测量距离。 最后,通过定义`clearMeasurements`函数和添加一个清除按钮,可以清除测量结果。 请注意,上述代码中需要引入Cesium库和相关样式表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值