地形开挖

最近一直在研究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);
},
效果图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容