CESIUM跟随项目学习1

92 阅读7分钟

跟随公司项目学习cesium

html部分

    <div class="CesiumLx">
       <div class="btnlist">
          <button @click="spotExcavationFun.listenDrawPolygons">点选开始</button>
          <button @click="boxSelectMethod.frameSelectionStart">框选开始1</button>
          <button @click="frameSelectionExcavation">框选开始2</button>
          <button @click="clearMouseListen">结束</button>
       </div>
       <div class="cesiumCon whv100" :id="props['cid']"></div>
    </div>

js部分

初始化cesium

// 初始化Cesium
async function initCesium() {
    Cesium.Ion.defaultAccessToken = 进入cesium官网:https://cesium.com/learn/, 登录,点击Access Tokens, 复制token到这里;
    // props['cid']是cesium的id,因为是以组件形式
    viewer.value = new Cesium.Viewer(props['cid'], {
       animation: false, // 动画控件
       shouldAnimate: false, // 初始时刻运动
       homeButton: false, // Home按钮
       fullscreenButton: false, // 全屏按钮
       baseLayerPicker: false, // 图层选择控件
       geocoder: false, // 地名查找控件
       timeline: false, // 时间线控件
       shadows: false, // 阴影内容
       navigationHelpButton: false, // 帮助信息控件
       infoBox: false, // 点击要素之后显示的信息 信息框小部件
       requestRenderMode: false, // true启用请求渲染模式:更新实体需拖动地图 视图才更新[true 加载完entity后requestRender一下]
       scene3DOnly: false, // 几何图形以3D模式绘制以节约GPU资源
       sceneMode: Cesium.SceneMode.SCENE3D, // 初始场景模式  3d 球
       maximumRenderTimeChange: 1,
       sceneModePicker: false, // 切换展示模式控件
       selectionIndicator: false,
       // 设置渲染
       orderIndependentTranslucency: false,
       contextOptions: {
          webgl: {
             alpha: true,
          },
       }
    })
    viewer.value.scene.globe.depthTestAgainstTerrain = true; // 开启深度渲染
    // 将相机移动到你指定的经纬度位置,height是相机距离地面的高度 默认值:lng: 120,  lat: 30, height: 300, 都是数字类型
    viewer.value.camera.flyTo({
       destination: Cesium.Cartesian3.fromDegrees(lng, lat, height),
    });
}

设置裁剪地图,设置贴图,经纬度转换等等开挖所需方法

// 裁剪地图
async function cropMap(dataSource, type = false) {
    let clippingPlanes = [] // 存储ClippingPlane集合
    const pointsCoor = dataSource.map(({x, y, z}) => new Cesium.Cartesian3(x, y, z))
    let sum = 0
    for (let i = 0; i < pointsCoor.length; i++) {
       const pointA = pointsCoor[i]
       const pointB = pointsCoor[(i + 1) % pointsCoor.length]
       const crossProduct = Cesium.Cartesian3.cross(pointA, pointB, new Cesium.Cartesian3()) // 计算pointA和pointB两个向量的叉乘
       sum += crossProduct.z
    }

    if (sum > 0 && type) { // 逆时针
       dataSource.reverse()
    } else if (sum < 0 && !type) { // 顺时针
       dataSource.reverse()
    }

    for (let i = 0; i < dataSource.length; ++i) {
       const nextIndex = (i + 1) % dataSource.length

       // 计算两个坐标点的分量和,取中点。
       const midpoint = Cesium.Cartesian3.add(dataSource[i], dataSource[nextIndex], new Cesium.Cartesian3())
       Cesium.Cartesian3.multiplyByScalar(midpoint, 0.5, midpoint)

       // up 是指从地球中心(原点)到 midpoint 的向量,即一个指向地球正上方的单位向量。
       const up = Cesium.Cartesian3.normalize(midpoint, new Cesium.Cartesian3())

       // dataSource[nextIndex]和midpoint的差值,得到一个表示从 dataSource[nextIndex] 指向 midpoint 的向量
       const right = Cesium.Cartesian3.subtract(dataSource[nextIndex], midpoint, new Cesium.Cartesian3())
       Cesium.Cartesian3.normalize(right, right) // 计算单位向量

       // 通过叉乘及归一化得到单位法向量
       const normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3())
       Cesium.Cartesian3.normalize(normal, normal) // 计算单位向量

       const originCenteredPlane = new Cesium.Plane(normal, 0.0)
       const distance = Cesium.Plane.getPointDistance(originCenteredPlane, midpoint) // 计算平面到中点的距离(开挖地形高度或者深度)

       // 最后,我们得到一个平面,这个平面垂直于地球表面
       clippingPlanes.push(new Cesium.ClippingPlane(normal, distance))
    }

    // 为地球添加裁剪面
    viewer.value['scene'].globe.clippingPlanes = new Cesium.ClippingPlaneCollection({
       planes: clippingPlanes,
       enabled: true,
       modelMatrix: Cesium.Matrix4.IDENTITY,
       unionClippingRegions: type, // 内 || 外
       edgeColor: Cesium.Color.YELLOW,
       edgeWidth: 1.0,
       edgeHeight: -100
    })

    viewer.value['scene'].globe.backFaceCulling = false
    viewer.value['scene'].globe.showSkirts = false
}

// 设置开挖贴图 arrData为数据源 height为开挖后的位置
async function setExcavationMap(arrData, height = -10) {
    let polygonData = []
    let wallData = []
    arrData.push(arrData[0])
    arrData.map(item => {
       wallData.push(item)
       polygonData.push([item[0], item[1]])
    })
    wallData.map(item => {
       item[2] = height
    })
    viewer.value['entities'].add({
       id: 'excavationMap',
       polygon: {
          height: height,
          hierarchy: Cesium.Cartesian3.fromDegreesArray(polygonData.flat()),
          extrudedHeight: undefined,
          material: new Cesium.ImageMaterialProperty() // image的值为图片地址。ImageMaterialProperty默认图片不重复 // {image: data.url},不需要额外设置
       },
       wall: {
          positions: Cesium.Cartesian3.fromDegreesArrayHeights(wallData.flat()),
          material: new Cesium.ImageMaterialProperty() // image的值为图片地址。ImageMaterialProperty默认图片不重复 // {image: data.url},不需要额外设置
       }
    })
}

// 清除鼠标监听
async function clearMouseListen() {
    handler.value = new Cesium.ScreenSpaceEventHandler(viewer.value.scene.canvas)
    viewer.value['entities'].removeAll()
    viewer.value['scene'].globe.clippingPlanes.removeAll()
}

// 获取Cartesian3转换为经纬度
function convertLatAndLon(data) {
    let position = viewer.value['scene'].pickPosition(data);
    let cartographic = Cesium.Cartographic.fromCartesian(position);
    let lon = Cesium.Math.toDegrees(cartographic.longitude).toFixed(5);
    let lat = Cesium.Math.toDegrees(cartographic.latitude).toFixed(5);
    let height = cartographic.height.toFixed(2);
    return [Number(lon), Number(lat), Number(height)]
}

// 经纬度转换为Cartesian3
function convertLonLatCartesian3(data) {
    return Cesium.Cartesian3.fromDegrees(data[0], data[1])
}

// 禁止cesium平移缩放旋转等
async function openAndCloseCon(type=false) {
    // 禁止旋转
    viewer.value['scene'].screenSpaceCameraController.enableRotate = type;
    // 禁止平移
    viewer.value['scene'].screenSpaceCameraController.enableTranslate = type;
    // 禁止缩放
    viewer.value['scene'].screenSpaceCameraController.enableZoom = type;
}

点选开挖内容

// 点选开挖参数
const polygonData = reactive({
    pointList: [],
    lineList: []
})

// 点选开挖方法
const spotExcavationFun = {
    // 监听画多边形
    listenDrawPolygons: async () => {
       handler.value = new Cesium.ScreenSpaceEventHandler(viewer.value.scene.canvas)
       // 开始
       handler.value['setInputAction']((event) => {
          if (polygonData.pointList.length === 0) {
             console.log(123)
             viewer.value['entities'].removeAll()
          }
          polygonData.lineList.push(viewer.value['scene'].camera.pickEllipsoid(event.position))
          if (Cesium.defined(event.position)) {
             let curLonLat = convertLatAndLon(event.position)
             spotExcavationFun.drawDots(curLonLat)
             const pointNum = polygonData.pointList.length
             if (pointNum > 1) {
                spotExcavationFun.drawLines([...polygonData.pointList[pointNum - 2], ...polygonData.pointList[pointNum - 1]])
             }
          }
       }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
       // 结束
       handler.value['setInputAction'](async (event) => {
          let cartesian = viewer.value['camera'].pickEllipsoid(event.position, viewer.value['scene'].globe.ellipsoid);
          const pointNum = polygonData.pointList.length
          if (cartesian) {
             if (pointNum < 3) {
                ElMessage.warning('请选择3个以上的点再执行闭合操作命令')
             } else {
                await spotExcavationFun.drawLines([...polygonData.pointList[pointNum - 1], ...polygonData.pointList[0]])
                await cropMap(polygonData.lineList)
                await setExcavationMap(polygonData.pointList)
                polygonData.pointList = []
                polygonData.lineList = []
                handler.value['destroy'](); //关闭事件句柄
             }
          }
       }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
    },

    // 画点
    drawDots: async (lonAndLat) => {
       viewer.value.entities.add({
          position: Cesium.Cartesian3.fromDegrees(lonAndLat[0], lonAndLat[1], 0),
          name: 'point_name',
          point: {
             color: Cesium.Color.fromCssColorString('#fc3d4a'),
             outlineColor: Cesium.Color.fromCssColorString('#fc3d4a'),
             pixelSize: 11,
             disableDepthTestDistance: Number.POSITIVE_INFINITY
          },
       })
       polygonData.pointList.push(lonAndLat)
    },

    // 画线
    drawLines: async (lineArr) => {
       viewer.value.entities.add({
          name: 'lineName',
          polyline: {
             positions: Cesium.Cartesian3.fromDegreesArrayHeights(lineArr),
             width: 5,
             material: Cesium.Color.fromCssColorString('#fcc31f'),
             clampToGround: true,   // 这个是线覆盖模型的关键
             zIndex: 100,   // 这个是层级,但是必须clampToGround: true
             show: true
          }
       })
    }
}

框选开挖内容

// 框选开挖参数
const boxSelect = reactive({
    // 框选1参数
    nowPoint: [],
    pointEntityArr: [],
    isDrawPolyGon: false,
    isDrawLine: false,
    polygonEntityArr: [],
    // 框选2参数
    start: {},
    end: {},
    startPoint: {},
    endPoint: {},
    points: [],
    position: [],
    cropPointPosition: []
})

// 框选开挖
const boxSelectMethod = {
    // 删除实体
    // 删除鼠标移动生成的线
    delDemoEntity: (name) => {
       name === 'point_name' && boxSelect['pointEntityArr'].forEach(item => {
          viewer.value['entities'].remove(item)
       })
    },

    // 画点
    drawPoint: (lonAndLat) => {
       return viewer.value.entities.add({
          position: Cesium.Cartesian3.fromDegrees(lonAndLat[0], lonAndLat[1], lonAndLat[2]),
          name: 'point_name',
          point: {
             color: Cesium.Color.fromCssColorString('#fc3d4a'),
             outlineColor: Cesium.Color.fromCssColorString('#fc3d4a'),
             pixelSize: 0.01
          },
       })
    },

    // 画面
    drawPolyGon: (lonAndLat) => {
       let lonLats = (data) => {
          let arr = []
          for (let d in data) {
             //判断经纬度范围
             if (
                data[d][0] >= -180 &&
                data[d][0] <= 180 &&
                data[d][1] >= -90 &&
                data[d][1] <= 90
             ) {
                arr.push(data[d][0], data[d][1])
             } else {
                console.log('经纬度数值不对:', data[d][0], data[d][1])
             }
          }
          return arr
       }
       return viewer.value['entities'].add({
          // position: Cesium.Cartesian3.fromDegrees(lonAndLat[0], lonAndLat[1], lonAndLat[2]),
          id: "polyGon_name",
          polygon: {
             clampToGround: true, //开启贴地
             hierarchy: new Cesium.PolygonHierarchy(
                Cesium.Cartesian3.fromDegreesArray(lonLats(lonAndLat))
             ),
             material: Cesium.Color.BLUE.withAlpha(0.5),
             // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 300000)
          }
       });
    },

    enableListening: ()=>{
       handler.value = new Cesium.ScreenSpaceEventHandler(viewer.value.scene.canvas)
       // 监听鼠标左击事件
       handler.value['setInputAction']((event) => {
          if(boxSelect['polygonEntityArr'].length === 0) {
             viewer.value['entities'].removeAll()
          }
          if (boxSelect['isDrawLine']) {
             // 判断是否有实体
             // 获取坐标信息
             let nowPosition = convertLatAndLon(event['position'])
             // 当前点数据
             boxSelect['nowPoint'] = [nowPosition[0], nowPosition[1], 0]
             // 画点
             const point = boxSelectMethod['drawPoint']([nowPosition[0], nowPosition[1], 0])
             boxSelect['pointEntityArr'].push(point)
             // 开始形成面
             boxSelect['isDrawPolyGon'] = true
          }
       }, Cesium.ScreenSpaceEventType.LEFT_DOWN)
       // 鼠标移动事件
       handler.value['setInputAction']((event) => {
          // 画框逻辑
          if (boxSelect['isDrawLine'] && (boxSelect['nowPoint'].length > 0)) {
             // 获取坐标信息
             let movePosition = convertLatAndLon(event.startPosition)
             const getRectanglePoint = (startPoint, endPoint) => {
                //点转turf点
                let p1 = turf.point([...startPoint]);
                let p2 = turf.point([...endPoint]);
                // 两点间距离
                let p1p2 = turf.rhumbDistance(p1, p2, { units: 'miles' });
                let p1p2bearing = turf.bearing(p1, p2);
                // console.log('ddd bearing', bearing);
                // p1 ---------- p3
                //    | \      |
                //    |   \    |
                //    |     \  |
                //    |       |
                // p4 ---------- p2
                // 获取生成的两个延长线点
                // 获取延长线点
                const getExtensionPoint = (point, distance, bearing) => {
                   // let startPoint = turf.point(point, { "marker-color": "F00" });
                   const demo = turf.rhumbDestination(point, distance, bearing, { units: 'miles' });
                   return demo.geometry.coordinates
                }

                // 获取生成的两个延长线点
                let p3RBearing = 0
                let p3TBearing = 0
                let p4RBearing = 0
                let p4TBearing = 0
                // turf使用的角度是由北向南
                //    北
                //     |
                //     |
                //     |
                //     |
                //   p |-----------------
                //     |
                //     |
                //     |
                //     |
                //     |
                //    南

                if (p1p2bearing > 0) {
                   if (p1p2bearing < 90) {
                      // 在0 - 90度范围内
                      p3RBearing = 0
                      p3TBearing = -90
                      p4RBearing = 90
                      p4TBearing = -180
                   } else {
                      // 在90 - 180度范围内
                      p3RBearing = 90
                      p3TBearing = 0
                      p4RBearing = -180
                      p4TBearing = -90
                   }
                } else {
                   if (p1p2bearing > -90) {
                      // 在0 - -90度范围内
                      p3RBearing = -90
                      p3TBearing = -180
                      p4RBearing = 0
                      p4TBearing = 90
                   } else {
                      // 在-90 - -180度范围内
                      p3RBearing = -180
                      p3TBearing = 90
                      p4RBearing = -90
                      p4TBearing = 0
                   }
                }
                // 获取p3点
                const p3R = getExtensionPoint(p1, p1p2, p3RBearing)  //起点
                const p3T = getExtensionPoint(p2, p1p2, p3TBearing)  //终点(鼠标当前对应的点)
                // 获取p4点
                const p4R = getExtensionPoint(p1, p1p2, p4RBearing)  //起点
                const p4T = getExtensionPoint(p2, p1p2, p4TBearing)  //终点(鼠标当前对应的点)
                //根据两个延长线获取交点 即p3
                const getLineIntersect = (pp1, pp2) => {
                   let line1 = turf.lineString([[...startPoint], [...pp1]]);
                   let line2 = turf.lineString([[...endPoint], [...pp2]]);
                   let intersects = turf.lineIntersect(line1, line2);
                   return intersects.features.length > 0 ? intersects.features[0].geometry.coordinates : ''
                }
                const p3 = getLineIntersect(p3R, p3T)
                const p4 = getLineIntersect(p4R, p4T)
                if (p3 === '' || p4 === '') {
                   console.log('点位有问题!');
                } else {
                   boxSelect['cropPointPosition'] = [
                      startPoint,
                      p3,
                      endPoint,
                      p4
                   ]
                   if (boxSelect['isDrawPolyGon']) {
                      const entityPolygon = boxSelectMethod['drawPolyGon'](boxSelect['cropPointPosition'])
                      boxSelect['polygonEntityArr'].push(entityPolygon)
                      boxSelect['isDrawPolyGon'] = false
                   } else {
                      const points = []
                      boxSelect['cropPointPosition'].forEach(item => {
                         const demo = Cesium.Cartesian3.fromDegrees(item[0], item[1], 0)
                         points.push(demo)
                      })
                      boxSelect['polygonEntityArr'][0].polygon.hierarchy = points
                   }
                }
             }
             // 根据点生成点位数据
             getRectanglePoint([boxSelect['nowPoint'][0], boxSelect['nowPoint'][1]], [movePosition[0], movePosition[1]])
          }
       }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)

       // 鼠标抬起事件
       handler.value['setInputAction'](async (event) => {
          if (boxSelect['isDrawLine']) {
             let cropData = []
             boxSelect['cropPointPosition'].map(item=>{
                cropData.push(convertLonLatCartesian3(item))
             })
             await cropMap(cropData)
             await setExcavationMap(boxSelect['cropPointPosition'], -10)
             // 结束画多边形
             boxSelect['isDrawLine'] = false
             // 画多边形结束,形成面开始
             // 清空数据
             // 删除画点以及画线
             boxSelectMethod.delDemoEntity('point_name')
             boxSelect['nowPoint'] = []
             boxSelect['polygonEntityArr'] = []
             boxSelect['pointEntityArr'] = []
             await openAndCloseCon(true)
             handler.value['destroy'](); //关闭事件句柄
          }
       }, Cesium.ScreenSpaceEventType.LEFT_UP)
    },

    frameSelectionStart: ()=>{
       openAndCloseCon()
       boxSelectMethod.enableListening()
       boxSelect['isDrawLine'] = true
       boxSelect['isDrawPolyGon'] = false
       boxSelect['nowPoint'] = []
       boxSelect['polygonEntityArr'] = []
       boxSelect['pointEntityArr'] = []
    }
}

参考链接(非常感谢各位大佬)