Cesium专栏-地形开挖2-任意多边形开挖

808 阅读1分钟

“任意多边形地形开挖” 是“地形开挖”的补充篇,在这节里,我们介绍关于如何使用任意多边形对地形进行开挖,同时,由于有不少小伙伴也咨询了关于“地形开挖”篇后序内容中的填充地形的效果,之前没放出来,是想让小伙伴有个思考的过程,现在放出来,也是提供一种解决方法。

效果图

直接上代码说明方法

1、使用鼠标交互事件,采集需要开挖的范围

注: 这里要特别注意一点,为了下面的计算 ClippingPlane 方便,采集点顺序最好是 逆时针,如果点集的组织是顺时针,需要首先逆序成逆时针,关于如果判断一个点集是否是顺时针或者是逆时针,可以用向量法求多边形面积的方式,如果为正,则为顺时针,否者为逆时针。或者使用JS插件计算,比如turf.js。

var points = [
    new Cesium.Cartesian3(-1715292.6999753984, 4993153.157628936, 3566663.752912529),
    new Cesium.Cartesian3(-1715285.8150713604, 4993167.072601330,3566647.6921528564),
    new Cesium.Cartesian3(-1715286.5985765400, 4993181.309761941, 3566627.519787549),
    new Cesium.Cartesian3(-1715299.0249209427, 4993191.177501195, 3566607.861264360),
    new Cesium.Cartesian3(-1715349.5762367432, 4993176.675656664, 3566603.878289345),
    new Cesium.Cartesian3(-1715375.5538853381, 4993159.990032478, 3566614.671147202),
    new Cesium.Cartesian3(-1715370.1945772346, 4993141.041835706, 3566643.580587877),
    new Cesium.Cartesian3(-1715359.7019716015, 4993131.063945592, 3566662.468046927),
    new Cesium.Cartesian3(-1715321.9141253997, 4993137.762602262, 3566671.205164391)
];

2、根据点集计算 ClippingPlane (这个计算方式来源于官网示例)

var pointsLength = points.length;
var clippingPlanes = []; // 存储ClippingPlane集合
for (var i = 0; i < pointsLength; ++i) {
  var nextIndex = (i + 1) % pointsLength;
  var midpoint = Cesium.Cartesian3.add(points[i], points[nextIndex], 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(points[nextIndex], 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);

  clippingPlanes.push(new Cesium.ClippingPlane(normal, distance));
}

3、赋值给globe的 clippingPlanes 属性

viewer.scene.globe.clippingPlanes = new Cesium.ClippingPlaneCollection({
  planes: clippingPlanes,
  edgeWidth: 1.0,
  edgeColor: Cesium.Color.WHITE
});

下面就是填充地形的代码(比较简单,思想就是插值计算地形高度,绘制多边形)

// 多边形每个顶点的坐标
var points = [];

// 依次计算顶点与下个顶点之间组成的线段,中间按照精度大小插值n个点,然后计算每个点的地形高度
var promise = calHeight(points[i], points[i+1], n); // 返回promise对象

// 待所有的计算完毕后,处理坐标点,构造多边形
Promise.all([promise1, promise2, ...]).then(data => {
    var positons = Array.prototype.concat.apply([], data); // 数组降维
    var hierarchy = [];
    positons.forEach(element => {
        hierarchy.push(Cesium.Math.toDegrees(element.longitude));
        hierarchy.push(Cesium.Math.toDegrees(element.latitude));
        hierarchy.push(element.height);
    });
    viewer.entities.add({
        polygon: {
            hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(hierarchy),
            material: new Cesium.ImageMaterialProperty({
                image: "../images/box.jpg"
            }),
            closeTop: false, // 这个要设置为false
            extrudedHeight: 0,
            perPositionHeight: true // 这个要设置true
        }
    });
})

插值、计算坐标点高度(如果不加载地形的话,这步可以省略)

function calHeight(fromPoint, endPoint, count) {
    var positions = [];
    for (var i = 0; i <= count; i++) {
        var cart = Cesium.Cartesian3.lerp(fromPoint, endPoint, i / count, new Cesium.Cartesian3());
        positions.push(Cesium.Cartographic.fromCartesian(cart));
    }
    return new Promise((resolve, reject) => {
        var promise = Cesium.sampleTerrainMostDetailed(terrainProvider, positions);
        Cesium.when(promise, function (updatedPositions) {
            resolve(updatedPositions);
        });
    })
}

10/11更新:

经测试,任意范围一定要是凸多边形,如果不符合的话,可能会失败。需要注意!!!!!!

link==>