Source: commands/CesiumMeasure.js

; if (typeof Cesium !== 'undefined')
  /**
  * Cesium.Measure对象构造,暂不能直接引用
  * @param viewer  {object} 三维对象
  * @param options {object} 初始化参数
  * @constructor
  * @class
  */
  Cesium.Measure = (function (Cesium) {

    /**
     * 绘制对象
     * @param viewer 
     * @param options 
     * @constructor
     */
    function _(options = {}) {

      if (viewer && viewer instanceof Cesium.Viewer) {

        this._drawLayer = undefined;
        var cc = viewer.dataSources.getByName("mymeasure");
        if (cc && cc.length >= 1) {
          this._drawLayer = cc[0];
        } else {
          this._drawLayer = new Cesium.CustomDataSource('mymeasure');
          viewer && viewer.dataSources.add(this._drawLayer)
        }

        this._basePath = options.basePath || ''

        this._viewer = viewer;
        this._handler = null;
        this._isdone = false;
        //临时点对象
        this._tempPoints = [];
      }
    }
    _.prototype = {
      /***
       * 坐标转换 84转笛卡尔
       * 
       * @param {Object} {lng,lat,alt} 地理坐标
       * 
       * @return {Object} Cartesian3 三维位置坐标
       */
      transformWGS84ToCartesian: function (position, alt) {
        if (this._viewer) {
          return position
            ? Cesium.Cartesian3.fromDegrees(
              position.lng || position.lon,
              position.lat,
              position.alt = alt || position.alt,
              Cesium.Ellipsoid.WGS84
            )
            : Cesium.Cartesian3.ZERO
        }
      },
      /***
      * 坐标数组转换 笛卡尔转84
      * 
      * @param {Array} WSG84Arr {lng,lat,alt} 地理坐标数组
      * @param {Number} alt 拔高
      * @return {Array} Cartesian3 三维位置坐标数组
      */
      transformWGS84ArrayToCartesianArray: function (WSG84Arr, alt) {
        if (this._viewer && WSG84Arr) {
          var $this = this
          return WSG84Arr
            ? WSG84Arr.map(function (item) { return $this.transformWGS84ToCartesian(item, alt) })
            : []
        }
      },
      /***
       * 坐标转换 笛卡尔转84
       * 
       * @param {Object} Cartesian3 三维位置坐标
       * 
       * @return {Object} {lng,lat,alt} 地理坐标
       */
      transformCartesianToWGS84: function (cartesian) {
        if (this._viewer && cartesian) {
          var ellipsoid = Cesium.Ellipsoid.WGS84
          var cartographic = ellipsoid.cartesianToCartographic(cartesian)
          return {
            lng: Cesium.Math.toDegrees(cartographic.longitude),
            lat: Cesium.Math.toDegrees(cartographic.latitude),
            alt: cartographic.height
          }
        }
      },
      /***
      * 坐标数组转换 笛卡尔转86
      * 
      * @param {Array} cartesianArr 三维位置坐标数组
      * 
      * @return {Array} {lng,lat,alt} 地理坐标数组
      */
      transformCartesianArrayToWGS84Array: function (cartesianArr) {
        if (this._viewer) {
          var $this = this
          return cartesianArr
            ? cartesianArr.map(function (item) { return $this.transformCartesianToWGS84(item) })
            : []
        }
      },
      /**
       * 84坐标转弧度坐标
       * @param {Object} position wgs84
       * @return {Object} Cartographic 弧度坐标
       * 
       */
      transformWGS84ToCartographic: function (position) {
        return position
          ? Cesium.Cartographic.fromDegrees(
            position.lng || position.lon,
            position.lat,
            position.alt
          )
          : Cesium.Cartographic.ZERO
      },
      /**
     * 拾取位置点
     * 
     * @param {Object} px 屏幕坐标
     * 
     * @return {Object} Cartesian3 三维坐标
     */
      getCatesian3FromPX: function (px) {

        if (this._viewer && px) {

          var picks = this._viewer.scene.drillPick(px)
          var cartesian = null;
          var isOn3dtiles = false, isOnTerrain = false;
          // drillPick
          for (let i in picks) {
            let pick = picks[i]

            if (pick &&
              pick.primitive instanceof Cesium.Cesium3DTileFeature
              || pick && pick.primitive instanceof Cesium.Cesium3DTileset
              || pick && pick.primitive instanceof Cesium.Model) { //模型上拾取
              isOn3dtiles = true;
            }
            // 3dtilset
            if (isOn3dtiles) {
              this._viewer.scene.pick(px) // pick
              cartesian = this._viewer.scene.pickPosition(px);
              if (cartesian) {
                let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
                if (cartographic.height < 0) cartographic.height = 0;
                let lon = Cesium.Math.toDegrees(cartographic.longitude)
                  , lat = Cesium.Math.toDegrees(cartographic.latitude)
                  , height = cartographic.height;
                cartesian = this.transformWGS84ToCartesian({ lng: lon, lat: lat, alt: height })

              }
            }
          }
          // 地形
          let boolTerrain = this._viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider;
          // Terrain
          if (!isOn3dtiles && !boolTerrain) {
            var ray = this._viewer.scene.camera.getPickRay(px);
            if (!ray) return null;
            cartesian = this._viewer.scene.globe.pick(ray, this._viewer.scene);
            isOnTerrain = true
          }
          // 地球
          if (!isOn3dtiles && !isOnTerrain && boolTerrain) {

            cartesian = this._viewer.scene.camera.pickEllipsoid(px, this._viewer.scene.globe.ellipsoid);
          }
          if (cartesian) {
            let position = this.transformCartesianToWGS84(cartesian)
            if (position.alt < 0) {
              cartesian = this.transformWGS84ToCartesian(position, 0.1)
            }
            return cartesian;
          }
          return false;
        }

      },



      /**
       * 获取84坐标的距离
       * @param {*} positions 
       */
      getPositionDistance: function (positions) {
        let distance = 0
        for (let i = 0; i < positions.length - 1; i++) {
          let point1cartographic = this.transformWGS84ToCartographic(positions[i])
          let point2cartographic = this.transformWGS84ToCartographic(positions[i + 1])
          let geodesic = new Cesium.EllipsoidGeodesic()
          geodesic.setEndPoints(point1cartographic, point2cartographic)
          let s = geodesic.surfaceDistance
          s = Math.sqrt(
            Math.pow(s, 2) +
            Math.pow(point2cartographic.height - point1cartographic.height, 2)
          )
          distance = distance + s
        }
        return distance.toFixed(3)
      },
      /**
      * 计算一组坐标组成多边形的面积
      * @param {*} positions 
      */
      getPositionsArea: function (positions) {
        let result = 0
        if (positions) {
          let h = 0
          let ellipsoid = Cesium.Ellipsoid.WGS84
          positions.push(positions[0])
          for (let i = 1; i < positions.length; i++) {
            let oel = ellipsoid.cartographicToCartesian(
              this.transformWGS84ToCartographic(positions[i - 1])
            )
            let el = ellipsoid.cartographicToCartesian(
              this.transformWGS84ToCartographic(positions[i])
            )
            h += oel.x * el.y - el.x * oel.y
          }
          result = Math.abs(h).toFixed(2)
        }
        return result >= 10000 ? ((result / 1000000).toFixed(4) + "平方公里") : result + "平方米";
      },
      /**
       * 测距
       * @param {*} options 
       */
      drawLineMeasureGraphics: function (options = {}) {

        var that = this;
        if (this._viewer && options) {

          var positions = [], _lineEntity = new Cesium.Entity(), $this = this, lineObj,
            _handlers = new Cesium.ScreenSpaceEventHandler(this._viewer.scene.canvas);
          let tempPoints = [];


          if (this._handler && this._lineEntity) {
            this._handler.destroy()
            this._handler = null;
            //移除临时对象
            this._drawLayer.entities.remove(this._lineEntity);
            this._lineEntity = undefined;
            //移除临时点
            this._tempPoints.forEach(element => {
              that._drawLayer.entities.remove(element);
            });
            this._tempPoints = [];
          }
          this._handler = _handlers;
          this._lineEntity = _lineEntity;
          this._tempPoints = tempPoints;
          // left
          _handlers.setInputAction(function (movement) {

            var cartesian = $this.getCatesian3FromPX(movement.position);
            if (cartesian && cartesian.x) {
              if (positions.length == 0) {
                positions.push(cartesian.clone());
              }
              // 添加量测信息点
              var tempP = _addInfoPoint(cartesian);
              that._tempPoints.push(tempP);
              positions.push(cartesian);
            }
          }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

          _handlers.setInputAction(function (movement) {

            var cartesian = $this.getCatesian3FromPX(movement.endPosition);
            if (positions.length >= 2) {
              if (cartesian && cartesian.x) {
                positions.pop();
                positions.push(cartesian);
              }
            }
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
          // right
          _handlers.setInputAction(function (movement) {

            that._handler.destroy()
            that._handler = null

            let cartesian = $this.getCatesian3FromPX(movement.position);
            _addInfoPoint(cartesian)

            if (typeof options.callback === 'function') {

              options.callback($this.transformCartesianArrayToWGS84Array(positions), lineObj);
            }
            _lineEntity.polyline.positions = positions;
          }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);


          _lineEntity.polyline = {
            width: options.width || 5
            , material: options.material || Cesium.Color.BLUE.withAlpha(0.8)
            , clampToGround: options.clampToGround || false,
            depthFailMaterial: options.material || Cesium.Color.BLUE.withAlpha(0.8)
          }
          _lineEntity.polyline.positions = new Cesium.CallbackProperty(function () {
            return positions
          }, false)

          lineObj = this._drawLayer.entities.add(_lineEntity)

          //添加坐标点
          function _addInfoPoint(position) {
            let _labelEntity = {}
            _labelEntity.position = position
            _labelEntity.point = {
              pixelSize: 10,
              outlineColor: Cesium.Color.BLUE,
              outlineWidth: 5,
              disableDepthTestDistance: Number.POSITIVE_INFINITY,
            }
            _labelEntity.label = {
              text: ($this.getPositionDistance($this.transformCartesianArrayToWGS84Array(positions))) + '米',
              show: true,
              showBackground: true,
              font: '14px monospace',
              horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
              verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
              pixelOffset: new Cesium.Cartesian2(-10, -10), //left top,
              //防止label被遮挡,手动偏倚10m
              eyeOffset: new Cesium.Cartesian3(0, 0, -1)
            }
            var dd = $this._drawLayer.entities.add(_labelEntity)
            return dd;
          }
        }

      },
      /**
       * 测面积
       * @param {*} options 
       */
      drawAreaMeasureGraphics: function (options = {}) {
        if (this._viewer && options) {

          var positions = [], polygon = new Cesium.PolygonHierarchy(), _polygonEntity = new Cesium.Entity(), $this = this, polyObj = null, _label = '',
            _handler = new Cesium.ScreenSpaceEventHandler(this._viewer.scene.canvas);
          // left
          _handler.setInputAction(function (movement) {

            var cartesian = $this.getCatesian3FromPX(movement.position);
            if (cartesian && cartesian.x) {
              if (positions.length == 0) {
                polygon.positions.push(cartesian.clone())
                positions.push(cartesian.clone());
              }
              positions.push(cartesian.clone());
              polygon.positions.push(cartesian.clone())

              if (!polyObj) create()
            }
          }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
          // mouse
          _handler.setInputAction(function (movement) {

            var cartesian = $this.getCatesian3FromPX(movement.endPosition);
            // var cartesian = $this._viewer.scene.camera.pickEllipsoid(movement.endPosition, $this._viewer.scene.globe.ellipsoid);
            if (positions.length >= 2) {
              if (cartesian && cartesian.x) {
                positions.pop()
                positions.push(cartesian);
                polygon.positions.pop()
                polygon.positions.push(cartesian);
              }
            }
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

          // right
          _handler.setInputAction(function (movement) {
            let cartesian = $this.getCatesian3FromPX(movement.endPosition);

            _handler.destroy();

            positions.push(positions[0]);

            // 添加信息点
            _addInfoPoint(positions[0])
            if (typeof options.callback === 'function') {

              options.callback($this.transformCartesianArrayToWGS84Array(positions), polyObj);
            }
          }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);

          function create() {

            _polygonEntity.polyline = {
              width: 3
              , material: Cesium.Color.BLUE.withAlpha(0.8)
              , clampToGround: options.clampToGround || false
            }

            _polygonEntity.polyline.positions = new Cesium.CallbackProperty(function () {
              return positions
            }, false)

            _polygonEntity.polygon = {

              hierarchy: new Cesium.CallbackProperty(function () {
                return polygon
              }, false),

              material: Cesium.Color.YELLOW.withAlpha(0.1)
              , clampToGround: options.clampToGround || false
            }

            polyObj = $this._drawLayer.entities.add(_polygonEntity)
          }

          function _addInfoPoint(position) {
            var _labelEntity = {}
            _labelEntity.position = position
            _labelEntity.point = {
              pixelSize: 10,
              outlineColor: Cesium.Color.BLUE,
              outlineWidth: 5,
              disableDepthTestDistance: 100000,
            }
            _labelEntity.label = {
              text: ($this.getPositionsArea($this.transformCartesianArrayToWGS84Array(positions))),
              show: true,
              showBackground: true,
              font: '14px monospace',
              horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
              verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
              pixelOffset: new Cesium.Cartesian2(0, 0) //left top
            }
            $this._drawLayer.entities.add(_labelEntity)
          }
        }

      },


      /**
       * 释放绘制对象,暂未实现,可自定义修改
       */
      destory(){

      },


      /**
       * 画三角量测
       * @param {*} options 
       */
      drawTrianglesMeasureGraphics: function (options = {}) {
        options.style = options.style ||
        {
          width: 3
          , material: Cesium.Color.BLUE.withAlpha(0.5)
        }
        if (this._viewer && options) {

          var _trianglesEntity = new Cesium.Entity(), _tempLineEntity = new Cesium.Entity(), _tempLineEntity2 = new Cesium.Entity(),
            _positions = [], _tempPoints = [], _tempPoints2 = [], $this = this,
            _handler = new Cesium.ScreenSpaceEventHandler(this._viewer.scene.canvas);
          // 高度
          function _getHeading(startPosition, endPosition) {
            if (!startPosition && !endPosition) return 0
            if (Cesium.Cartesian3.equals(startPosition, endPosition)) return 0
            let cartographic = Cesium.Cartographic.fromCartesian(startPosition);
            let cartographic2 = Cesium.Cartographic.fromCartesian(endPosition);
            return (cartographic2.height - cartographic.height).toFixed(2)
          }
          // 偏移点
          function _computesHorizontalLine(positions) {
            let cartographic = Cesium.Cartographic.fromCartesian(positions[0]);
            let cartographic2 = Cesium.Cartographic.fromCartesian(positions[1]);
            return Cesium.Cartesian3.fromDegrees(
              Cesium.Math.toDegrees(cartographic.longitude),
              Cesium.Math.toDegrees(cartographic.latitude),
              cartographic2.height
            )
          }
          // left
          _handler.setInputAction(function (movement) {

            var position = $this.getCatesian3FromPX(movement.position);
            if (!position && !position.z) return false
            if (_positions.length == 0) {
              _positions.push(position.clone())
              _positions.push(position.clone())
              _tempPoints.push(position.clone())
              _tempPoints.push(position.clone())
            } else {
              _handler.destroy();
              if (typeof options.callback === 'function') {

                options.callback({ e: _trianglesEntity, e2: _tempLineEntity, e3: _tempLineEntity2 });
              }
            }
          }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
          // mouse
          _handler.setInputAction(function (movement) {

            var position = $this.getCatesian3FromPX(movement.endPosition);
            if (position && _positions.length > 0) {
              //直线
              _positions.pop()
              _positions.push(position.clone());
              let horizontalPosition = _computesHorizontalLine(_positions)
              //高度
              _tempPoints.pop()
              _tempPoints.push(horizontalPosition.clone())
              //水平线
              _tempPoints2.pop(), _tempPoints2.pop()
              _tempPoints2.push(position.clone())
              _tempPoints2.push(horizontalPosition.clone())
            }
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)

          // create entity

          //直线
          _trianglesEntity.polyline = {
            positions: new Cesium.CallbackProperty(function () {
              return _positions
            }, false),
            ...options.style
          }
          _trianglesEntity.position = new Cesium.CallbackProperty(function () {
            return _positions[0]
          }, false)
          _trianglesEntity.point = {
            pixelSize: 5,
            outlineColor: Cesium.Color.BLUE,
            outlineWidth: 5
          }
          _trianglesEntity.label = {
            text: new Cesium.CallbackProperty(function () {
              return '直线:' + $this.getPositionDistance($this.transformCartesianArrayToWGS84Array(_positions)) + '米'
            }, false),
            show: true,
            showBackground: true,
            font: '14px monospace',
            horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            pixelOffset: new Cesium.Cartesian2(50, -100) //left top
          }
          //高度
          _tempLineEntity.polyline = {
            positions: new Cesium.CallbackProperty(function () {
              return _tempPoints
            }, false),
            ...options.style
          }
          _tempLineEntity.position = new Cesium.CallbackProperty(function () {
            return _tempPoints2[1]
          }, false)
          _tempLineEntity.point = {
            pixelSize: 5,
            outlineColor: Cesium.Color.BLUE,
            outlineWidth: 5
          }
          _tempLineEntity.label = {
            text: new Cesium.CallbackProperty(function () {
              return '高度:' + _getHeading(_tempPoints[0], _tempPoints[1]) + '米'
            }, false),
            show: true,
            showBackground: true,
            font: '14px monospace',
            horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            pixelOffset: new Cesium.Cartesian2(-20, 100) //left top
          }
          //水平
          _tempLineEntity2.polyline = {
            positions: new Cesium.CallbackProperty(function () {
              return _tempPoints2
            }, false),
            ...options.style
          }
          _tempLineEntity2.position = new Cesium.CallbackProperty(function () {
            return _positions[1]
          }, false)
          _tempLineEntity2.point = {
            pixelSize: 5,
            outlineColor: Cesium.Color.BLUE,
            outlineWidth: 5
          }
          _tempLineEntity2.label = {
            text: new Cesium.CallbackProperty(function () {
              return '水平距离:' + $this.getPositionDistance($this.transformCartesianArrayToWGS84Array(_tempPoints2)) + '米'
            }, false),
            show: true,
            showBackground: true,
            font: '14px monospace',
            horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            pixelOffset: new Cesium.Cartesian2(-150, -20) //left top
          }
          this._drawLayer.entities.add(_tempLineEntity2)
          this._drawLayer.entities.add(_tempLineEntity)
          this._drawLayer.entities.add(_trianglesEntity)
        }
      }
    }
    return _
  })(Cesium);