最近一直在研究Cesium相关,想做一个井下展示,就需要将地形挖出一块区域,然后显示模型,但碍于技术太菜,就疯狂百度,然未果...终搜索到一篇文章,微微修改后成功运行,分享给大家~
同时特别感谢源文大佬的奉献:
基于cesium的地形开挖地形剖切
// TerrainClipPlan.js
class TerrainClipPlan {
#positions;
#height = 0;
#show = false;
constructor(viewer, options) {
this.viewer = viewer;
this.options = options || {};
this.#positions = options.positions;
this.#height = options.height || 0;
this.bottomImg = options.bottomImg;
this.wallImg = options.wallImg;
this.splitNum = Cesium.defaultValue(options.splitNum, 50);
if (this.#positions && this.#positions.length > 0) {
this.updateData(this.#positions)
}
}
// 定义height属性的getter和setter
get show() {
return this.#show;
}
set show(e) {
this.#show = e;
if (this.viewer.scene.globe.clippingPlanes) {
this.viewer.scene.globe.clippingPlanes.enabled = e;
this.#switchExcavate(e);
}
}
get height() {
return this.#height;
}
set height(e) {
this.#height = e;
this.#updateExcavateDepth(e);
}
#prepareWell(e) {
var t = this.splitNum,
i = e.length;
if (i != 0) {
for (var a = this.excavateMinHeight - this.height, n = [], r = [], s = [], l = 0; l < i; l++) {
var u = l == i - 1 ? 0 : l + 1,
c = Cesium.Cartographic.fromCartesian(e[l]),
d = Cesium.Cartographic.fromCartesian(e[u]),
h = [c.longitude, c.latitude],
f = [d.longitude, d.latitude];
if (l == 0) {
s.push(new Cesium.Cartographic(h[0], h[1]));
r.push(Cesium.Cartesian3.fromRadians(h[0], h[1], a));
n.push(Cesium.Cartesian3.fromRadians(h[0], h[1], 0));
}
for (var p = 1; p <= t; p++) {
var m = Cesium.Math.lerp(h[0], f[0], p / t),
g = Cesium.Math.lerp(h[1], f[1], p / t);
l == i - 1 && p == t || (
s.push(new Cesium.Cartographic(m, g)),
r.push(Cesium.Cartesian3.fromRadians(m, g, a)),
n.push(Cesium.Cartesian3.fromRadians(m, g, 0)))
}
}
this.wellData = {
lerp_pos: s,
bottom_pos: r,
height_top: n
}
}
}
async #createWell(e) {
if (this.viewer.terrainProvider._layers) {
this.#createBottomSurface(e.bottom_pos);
var i = await Cesium.sampleTerrainMostDetailed(this.viewer.terrainProvider, e.lerp_pos);
for (var a = i.length, n = [], r = 0; r < a; r++) {
var s = Cesium.Cartesian3.fromRadians(i[r].longitude, i[r].latitude, i[r].height);
n.push(s)
}
this.#createWellWall(e.bottom_pos, n)
} else {
this.#createBottomSurface(e.bottom_pos);
this.#createWellWall(e.bottom_pos, e.height_top)
}
}
#createWellWall(e, t) {
let minHeight = this.#getMinHeight(e);
let maxHeights = [];
let minHeights = [];
for (let i = 0; i < t.length; i++) {
maxHeights.push(this.#ellipsoidToLonLat(t[i]).altitude);
minHeights.push(minHeight);
}
let wall = new Cesium.WallGeometry({
positions: t,
maximumHeights: maxHeights,
minimumHeights: minHeights,
});
let geometry = Cesium.WallGeometry.createGeometry(wall);
var a = new Cesium.Material({
fabric: {
type: "Image",
uniforms: {
image: this.wallImg
}
}
}),
n = new Cesium.MaterialAppearance({
translucent: !1,
flat: !0,
material: a
});
this.wellWall = new Cesium.Primitive({
geometryInstances: new Cesium.GeometryInstance({
geometry: geometry,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.GREY)
},
id: "PitWall"
}),
appearance: n,
asynchronous: false
}), this.viewer.scene.primitives.add(this.wellWall)
}
#createBottomSurface(e) {
if (e.length) {
let minHeight = this.#getMinHeight(e);
let positions = [];
for (let i = 0; i < e.length; i++) {
let p = this.#ellipsoidToLonLat(e[i]);
positions.push(p.longitude);
positions.push(p.latitude);
positions.push(minHeight);
}
let polygon = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArrayHeights(positions)
),
perPositionHeight: true,
closeBottom: false
});
let geometry = Cesium.PolygonGeometry.createGeometry(polygon);
let i = new Cesium.Material({
fabric: {
type: "Image",
uniforms: {
image: this.bottomImg
}
}
});
let a = new Cesium.MaterialAppearance({
translucent: !1,
flat: !0,
material: i
});
this.bottomSurface = new Cesium.Primitive({
geometryInstances: new Cesium.GeometryInstance({
geometry: geometry
}),
appearance: a,
asynchronous: false
}), this.viewer.scene.primitives.add(this.bottomSurface)
}
}
#getMinHeight(e) {
let minHeight = 5000000;
let minPoint = null;
for (let i = 0; i < e.length; i++) {
let height = e[i]['z'];
if (height < minHeight) {
minHeight = height;
minPoint = this.#ellipsoidToLonLat(e[i]);
}
}
return minPoint.altitude;
}
#ellipsoidToLonLat(c) {
let ellipsoid = this.viewer.scene.globe.ellipsoid;
let cartesian3 = new Cesium.Cartesian3(c.x, c.y, c.z);
let cartographic = ellipsoid.cartesianToCartographic(cartesian3);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let alt = cartographic.height;
return {
longitude: lng,
latitude: lat,
altitude: alt
}
}
#switchExcavate(e) {
if (e) {
this.wellWall.show = true;
this.bottomSurface.show = true;
} else {
this.wellWall.show = false;
this.bottomSurface.show = false;
}
}
#updateExcavateDepth(e) {
this.bottomSurface && this.viewer.scene.primitives.remove(this.bottomSurface);
this.wellWall && this.viewer.scene.primitives.remove(this.wellWall);
for (var t = this.wellData.lerp_pos, i = [], a = t.length, n = 0; n < a; n++) {
i.push(Cesium.Cartesian3.fromRadians(t[n].longitude, t[n].latitude, this.excavateMinHeight - e));
}
this.wellData.bottom_pos = i;
this.#createWell(this.wellData);
this.viewer.scene.primitives.add(this.bottomSurface);
this.viewer.scene.primitives.add(this.wellWall)
}
clear() {
if (this.viewer.scene.globe.clippingPlanes) {
this.viewer.scene.globe.clippingPlanes.enabled = !1;
this.viewer.scene.globe.clippingPlanes.removeAll();
this.viewer.scene.globe.clippingPlanes.isDestroyed() || this.viewer.scene.globe
.clippingPlanes.destroy()
}
this.viewer.scene.globe.clippingPlanes = void 0;
this.bottomSurface && this.viewer.scene.primitives.remove(this.bottomSurface);
this.wellWall && this.viewer.scene.primitives.remove(this.wellWall);
delete this.bottomSurface;
delete this.wellWall;
this.viewer.scene.render()
}
updateData(e) {
this.clear();
var t = [],
i = e.length,
a = new Cesium.Cartesian3,
n = Cesium.Cartesian3.subtract(e[0], e[1], a);
n = n.x > 0, this.excavateMinHeight = 9999;
for (var r = 0; r < i; ++r) {
var s = (r + 1) % i,
u = Cesium.Cartographic.fromCartesian(e[r]),
c = this.viewer.scene.globe.getHeight(u) || u.height;
c < this.excavateMinHeight && (this.excavateMinHeight = c);
var midpoint = Cesium.Cartesian3.add(e[r], e[s], new Cesium.Cartesian3());
midpoint = Cesium.Cartesian3.multiplyByScalar(midpoint, 0.5, midpoint);
var up = Cesium.Cartesian3.normalize(midpoint, new Cesium.Cartesian3());
var right = Cesium.Cartesian3.subtract(e[s], midpoint, new Cesium.Cartesian3());
right = Cesium.Cartesian3.normalize(right, right);
var normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3());
normal = Cesium.Cartesian3.normalize(normal, normal);
var originCenteredPlane = new Cesium.Plane(normal, 0.0);
var distance = Cesium.Plane.getPointDistance(originCenteredPlane, midpoint);
t.push(new Cesium.ClippingPlane(normal, distance));
}
this.viewer.scene.globe.clippingPlanes = new Cesium.ClippingPlaneCollection({
planes: t,
edgeWidth: 1,
edgeColor: Cesium.Color.WHITE,
enabled: !0
}), this.#prepareWell(e), this.#createWell(this.wellData)
}
}
export default TerrainClipPlan
调用方式
// 逆时针绘制面才会被裁剪,因此选择的点也要逆时针开始
terrainClipPlan() {
const data = [
[112.44100429433882, 37.90537618241738],
[112.44254330645052, 37.90554777851232],
[112.4424642277896, 37.90624724562377],
[112.44102050879226, 37.90617882399433],
];
const earthPositionList = data.map((item) =>
Cesium.Cartesian3.fromDegrees(...item)
);
let terrainClipPlan = new TerrainClipPlan(this.viewer, {
height: 10,
splitNum: 1000,
bottomImg: "/image/floor_map.jpg",
wallImg: "/image/wall_map.jpg",
// 可不传,传值则会默认调用updateData
positions: earthPositionList,
});
// 修改深度
terrainClipPlan.height = 30;
// 修改显隐
terrainClipPlan.show= true;
terrainClipPlan.show= false;
// 清除
terrainClipPlan.clear();
// 更新地形开挖区域
const data2 = [
[112.44100429433882, 37.90537618241738],
[112.44254330645052, 37.90554777851232],
[112.4424642277896, 37.90624724562377],
[112.44102050879226, 37.90617882399433],
];
const updatePoints= data2.map((item) =>
Cesium.Cartesian3.fromDegrees(...item)
);
// 可配合自定义绘制区域后,调用updateData
terrainClipPlan.updateData(updatePoints);
},