Source: modules/measure/MeasurePosition.js

import * as Cesium from "cesium";

/**
 * MeasurePosition class for measuring position in a Cesium viewer.
 * @class MeasurePosition
 * @param {Object} viewer - The Cesium viewer instance.
 * @param {Object} [options] - Options for the measurement tool.
 * @param {string} [options.textFormat] - The format of the measurement text. e.g. "({lon}, {lat}, {height})".
 * @param {Cesium.Color} [options.color] - The color of the measurement line and points.
 * @example
 * const measurePosition = new MeasurePosition(viewer, { color: Cesium.Color.RED });
 * measurePosition.on();
 * // To disable the measurement tool and clear entities:
 * measurePosition.off();
 */
export class MeasurePosition {
    constructor(viewer, options = {}) {
        this.viewer = viewer;
        this.scene = viewer.scene;
        this.handler = new Cesium.ScreenSpaceEventHandler();
        this.color = options.color || Cesium.Color.LIGHTGRAY;
        this.textFormat = options.textFormat || "({lon}, {lat}, {height})";
        this.startHeight = undefined;
        this.startCartographic = undefined;
        this.startCartesian = undefined;
        this.endHeight = undefined;
        this.endCartographic = undefined;
        this.endCartesian = undefined;
        this.startEntity = undefined;
        this.endEntity = undefined;
        this.entities = [];
    }

    /**
     * Enables the position measurement tool.
     * Click to start measuring
     * @function
     * @returns {void}
     */
    on = () => {
        this.scene.canvas.style.cursor = "crosshair";
        const viewer = this.viewer;
        const scene = viewer.scene;
        const handler = this.handler;

        // Mouse Left Click
        const mouseLeftClickHandler = (event) => {
            let pickedEllipsoidPosition;
            if (scene.pickPositionSupported) {
                pickedEllipsoidPosition = viewer.scene.pickPosition(
                    event.position);
                const cartographic = Cesium.Cartographic.fromCartesian(
                    pickedEllipsoidPosition);
                this.startCartographic = cartographic;
                const height = viewer.scene.globe.getHeight(
                    Cesium.Cartographic.fromRadians(cartographic.longitude,
                        cartographic.latitude, 0));
                this.startHeight = height;
                this.endHeight = height;
            }

            if (!pickedEllipsoidPosition) {
                pickedEllipsoidPosition = viewer.camera.pickEllipsoid(
                    event.position,
                    scene.globe.ellipsoid,
                );
                const cartographic = Cesium.Cartographic.fromCartesian(
                    pickedEllipsoidPosition);
                this.startCartographic = cartographic;
                const height = viewer.scene.globe.getHeight(
                    Cesium.Cartographic.fromRadians(cartographic.longitude,
                        cartographic.latitude, 0));
                this.startHeight = height;
                this.endHeight = height;
                pickedEllipsoidPosition = Cesium.Cartesian3.fromRadians(
                    cartographic.longitude, cartographic.latitude, height);
            }
            this.startCartesian = pickedEllipsoidPosition;
            this.endCartesian = pickedEllipsoidPosition;

            const cartographic = Cesium.Cartographic.fromCartesian(
                pickedEllipsoidPosition);
            const longitude = Cesium.Math.toDegrees(cartographic.longitude).
                toFixed(6);
            const latitude = Cesium.Math.toDegrees(cartographic.latitude).
                toFixed(6);

            this.startEntity = viewer.entities.add({
                position: pickedEllipsoidPosition,
                point: {
                    color: this.color,
                    pixelSize: 4,
                    disableDepthTestDistance: Number.POSITIVE_INFINITY,
                },
                label: {
                    show: true,
                    showBackground: false,
                    font: "14px monospace",
                    fillColor: this.color,
                    horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                    verticalOrigin: Cesium.VerticalOrigin.TOP,
                    pixelOffset: new Cesium.Cartesian2(0, -30),
                    disableDepthTestDistance: Number.POSITIVE_INFINITY,
                    text: new Cesium.CallbackProperty(() => {
                        const height = viewer.scene.globe.getHeight(
                            Cesium.Cartographic.fromRadians(
                                cartographic.longitude,
                                cartographic.latitude, 0));
                        let text = this.textFormat;
                        text = text.replace("{lon}", longitude);
                        text = text.replace("{lat}", latitude);
                        text = text.replace("{height}", height.toFixed(3));
                        return text;
                    }, false),
                },
            });

            this.entities.push(this.startEntity);
        };

        // Mouse Move
        const mouseMoveHandler = (moveEvent) => {
            /* if (!this.status) {
                return;
            }*/
            let pickedEllipsoidPosition;
            if (scene.pickPositionSupported) {
                pickedEllipsoidPosition = viewer.scene.pickPosition(
                    moveEvent.endPosition);
            }
            if (!pickedEllipsoidPosition) {
                pickedEllipsoidPosition = viewer.camera.pickEllipsoid(
                    moveEvent.endPosition,
                    scene.globe.ellipsoid,
                );
                const cartographic = Cesium.Cartographic.fromCartesian(
                    pickedEllipsoidPosition);
                const height = viewer.scene.globe.getHeight(
                    Cesium.Cartographic.fromRadians(cartographic.longitude,
                        cartographic.latitude, 0));
                pickedEllipsoidPosition = Cesium.Cartesian3.fromRadians(
                    cartographic.longitude, cartographic.latitude, height);
            }

            const convertCartographic = Cesium.Cartographic.fromCartesian(
                pickedEllipsoidPosition);
            pickedEllipsoidPosition = Cesium.Cartesian3.fromRadians(
                convertCartographic.longitude, convertCartographic.latitude,
                convertCartographic.height);
            this.endCartesian = pickedEllipsoidPosition;
            this.endHeight = convertCartographic.height;

            if (!this.endEntity) {
                this.endEntity = viewer.entities.add({
                    position: new Cesium.CallbackProperty(() => {
                        return this.endCartesian;
                    }, false),
                    point: {
                        color: this.color,
                        pixelSize: 4,
                        disableDepthTestDistance: Number.POSITIVE_INFINITY,
                    },
                    label: {
                        show: true,
                        showBackground: false,
                        font: "14px monospace",
                        fillColor: this.color,
                        horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                        verticalOrigin: Cesium.VerticalOrigin.TOP,
                        pixelOffset: new Cesium.Cartesian2(0, -30),
                        disableDepthTestDistance: Number.POSITIVE_INFINITY,
                        text: new Cesium.CallbackProperty(() => {
                            const cartographic = Cesium.Cartographic.fromCartesian(
                                this.endCartesian);
                            const longitude = Cesium.Math.toDegrees(
                                cartographic.longitude).
                                toFixed(6);
                            const latitude = Cesium.Math.toDegrees(
                                cartographic.latitude).
                                toFixed(6);
                            const height = this.endHeight;
                            let text = this.textFormat;
                            text = text.replace("{lon}", longitude);
                            text = text.replace("{lat}", latitude);
                            text = text.replace("{height}", height.toFixed(3));
                            return text;
                            // return `${this.endHeight.toFixed(3)}m`;
                        }, false),
                    },
                });
                this.entities.push(this.endEntity);
            }
        };
        handler.setInputAction(mouseLeftClickHandler,
            Cesium.ScreenSpaceEventType.LEFT_CLICK);
        handler.setInputAction(mouseMoveHandler,
            Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    };

    /**
     * Disables the position measurement tool.
     * @function
     * @returns {void}
     */
    off = () => {
        this.scene.canvas.style.cursor = "default";
        // this.status = false;
        this.clearEntities();
        const handler = this.handler;
        if (handler) {
            handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
            handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        }
    };

    clearEntities = () => {
        this.viewer.entities.remove(this.startEntity);
        this.viewer.entities.remove(this.endEntity);
        this.entities.forEach((entity) => {
            this.viewer.entities.remove(entity);
        });
        this.startEntity = undefined;
        this.endEntity = undefined;
    };
}