跟随公司项目学习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'] = []
}
}
参考链接(非常感谢各位大佬)
- cesium中的坐标系统与转换:blog.csdn.net/NOSaac/arti…
- 【WebGIS实例】(11)Cesium自定义区域裁剪(挖除&挖出):blog.csdn.net/ReBeX/artic…
- Cesium杂谈一:Cesium中的remove:blog.csdn.net/qq_48607161…
- 【Cesium】绘制线、面、体:blog.csdn.net/qq_35093027…
- Cesium 标绘线面防止被模型遮挡-面线标记覆盖模型:zhuanlan.zhihu.com/p/683329576
- 【Cesium】创建形状基础---点、线、面、矩形、柱体、连廊、墙等:blog.csdn.net/qq_27814951…
- cesium 地形开挖:blog.csdn.net/renkangever…
- Cesium 常用标绘线、面、矩形、圆、曲面、曲线、攻击箭头、钳击箭头,标绘与修改。:blog.csdn.net/qq_41400354…
- cesium中的坐标系统与转换:blog.csdn.net/NOSaac/arti…
- 23.cesuim分析之地形开挖:blog.csdn.net/yaojindou0/…