cesium学习记录09-turf.js的使用(画矩形结合地形生成三角网)

上一篇是绘制多边形,这一篇来说绘制矩形,但又因为只说绘制矩形太短了,所以就结合一下turf.js,生成一下地形三角网
Turf.js中文网

最终效果:

cesium学习记录09-turf.js的使用(画矩形结合地形生成三角网)_第1张图片
cesium学习记录09-turf.js的使用(画矩形结合地形生成三角网)_第2张图片
cesium学习记录09-turf.js的使用(画矩形结合地形生成三角网)_第3张图片

一、引入Turf.js

1,下载

npm install @turf/turf

2,引入

import * as turf from "@turf/turf";

二、鼠标绘制矩形区域

代码:

 //画矩形
  DrawRectangle() {
    var allPoints = [];

    // 设置返回值
    return new Promise((resolve, reject) => {
        let viewer = this.viewer;
        let topLeftPoint = null;
        let bottomRightPoint = null;

        let drawingRectangle = viewer.entities.add({
            id: "drawingRectangle",
            name: "画矩形",
            rectangle: {
                coordinates: new Cesium.CallbackProperty(() => {
                    if (topLeftPoint === null || bottomRightPoint === null) {
                        return;
                    }
                    let west = topLeftPoint.longitude;
                    let north = topLeftPoint.latitude;
                    let east = bottomRightPoint.longitude;
                    let south = bottomRightPoint.latitude;
                    return new Cesium.Rectangle(west, south, east, north);
                }, false),
                material: Cesium.Color.BLUE.withAlpha(0.2),
                closeTop: true,
                closeBottom: false
            }
        });

        let handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);

        handler.setInputAction(event => {
            var cartesian = this.getCatesian3FromPX(event.position);
            if (cartesian) {
                if (topLeftPoint === null) {
                    topLeftPoint = Cesium.Cartographic.fromCartesian(cartesian);
                }

                viewer.entities.add({
                    position: cartesian,
                    point: {
                        color: Cesium.Color.RED,
                        pixelSize: 10
                    }
                });
            }
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

        handler.setInputAction(event => {
            if (topLeftPoint) {
                bottomRightPoint = Cesium.Cartographic.fromCartesian(this.getCatesian3FromPX(event.endPosition));
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

        handler.setInputAction(() => {
            if (topLeftPoint !== null && bottomRightPoint !== null) {
                handler.destroy(); // 关闭鼠标事件监听,结束绘制

                let west = Cesium.Math.toDegrees(topLeftPoint.longitude);
                let north = Cesium.Math.toDegrees(topLeftPoint.latitude);
                let east = Cesium.Math.toDegrees(bottomRightPoint.longitude);
                let south = Cesium.Math.toDegrees(bottomRightPoint.latitude);

                allPoints.push({ lng: west, lat: north });
                allPoints.push({ lng: east, lat: north });
                allPoints.push({ lng: east, lat: south });
                allPoints.push({ lng: west, lat: south });
                allPoints.push(allPoints[0]); // 闭合
                resolve(allPoints);
            }
        }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
    });
},

(getCatesian3FromPX方法在上一篇)

效果:

cesium学习记录09-turf.js的使用(画矩形结合地形生成三角网)_第4张图片

代码说明:

函数定义和参数:

DrawRectangle() { … }: 这是定义DrawRectangle方法的部分

初始化变量:

var allPoints = [];: 初始化一个空数组,用于保存绘制的矩形的顶点坐标。
let topLeftPoint = null;: 初始化一个变量,用于存储矩形的左上角点。
let bottomRightPoint = null;: 初始化一个变量,用于存储矩形的右下角点。

Promise构造函数:

return new Promise((resolve, reject) => { … });: 返回一个新的Promise对象,用于处理异步操作。resolve和reject是两个回调函数,分别表示异步操作成功和失败的处理。

创建矩形Entity:

let drawingRectangle = viewer.entities.add({ … });: 用于在Cesium Viewer中添加一个新的矩形Entity。它使用CallbackProperty动态计算矩形的坐标。

创建事件处理器:

let handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);: 初始化一个新的Cesium屏幕空间事件处理器。

监听鼠标单击事件:

当用户在Cesium Viewer上单击鼠标左键时,这个事件会被触发。
if (topLeftPoint === null) { … }: 判断是否已经设置了矩形的左上角点。如果没有,则设置。

监听鼠标移动事件:

当用户在Cesium Viewer上移动鼠标时,这个事件会被触发。
if (topLeftPoint) { … }: 当左上角点设置后,根据鼠标的当前位置设置矩形的右下角点。
监听鼠标双击事件:

当用户在Cesium Viewer上双击鼠标左键时,这个事件会被触发。
当左上角和右下角点都被设置后,结束绘制,关闭鼠标事件监听。
handler.destroy(); 用于关闭鼠标事件监听,结束绘制。
计算出矩形的四个顶点的经纬度坐标,并将这些坐标点保存到allPoints数组中。
resolve(allPoints);: 最后,通过resolve函数将allPoints数组传递出去,表示异步操作成功完成。

总结:

这个DrawRectangle方法允许用户通过以下三个步骤在Cesium地图上绘制一个矩形:

单击地图以设置矩形的左上角点。
移动鼠标以实时预览矩形的形状。
双击地图以确认矩形的右下角点,并完成矩形的绘制。
方法返回一个Promise对象,当用户完成矩形的绘制后,这个Promise对象将被resolve,返回矩形的顶点坐标。

三、根据矩形区域生成点集

(resultPoints即上面矩形的顶点坐标返回值,num是手动穿的参数,当然数值越大点就越多了)

// 1. 创建一个矩形区域
// var rectangle = Cesium.Rectangle.fromDegrees(117.09649937089316, 36.20673458245797, 117.11797117691083, 36.230040948473906)
var rectangle = Cesium.Rectangle.fromDegrees(resultPoints[0].lng,resultPoints[2].lat,resultPoints[2].lng,resultPoints[0].lat)
// 2. 在这个矩形区域内生成点集
var width = num;  // 横向点数
var height = num; // 纵向点数
var terrainProvider =viewer.terrainProvider
var positions = [];
for (var y = 0; y < height; y++) {
  for (var x = 0; x < width; x++) {
    var longitude = Cesium.Math.lerp(rectangle.west, rectangle.east, x / (width - 1));
    var latitude = Cesium.Math.lerp(rectangle.south, rectangle.north, y / (height - 1));
    positions.push(Cesium.Cartographic.fromRadians(longitude, latitude));
  }
}

四、根据地形和区域为上述点集赋高度值

(这里的terrainProvider即地图加载的地形,我这里使用了cesium默认带的地形:viewer.terrainProvider = Cesium.createWorldTerrain({ requestVertexNormals: true, requestWaterMask: true });,参数terrainProvider即这里的viewer.terrainProvider。positions即上面的positions)

Cesium.sampleTerrainMostDetailed(terrainProvider, positions).then(function(samples) {
   var points = samples.map(function(sample) {
    return turf.point([Cesium.Math.toDegrees(sample.longitude), Cesium.Math.toDegrees(sample.latitude), sample.height],
    { z: sample.height })  // 将高度值保存到名为 'z' 的属性中);
  });
//显示点集  
var cartesianPoints = points.map(function(point) {
  var coord = point.geometry.coordinates;
  var cartographic = Cesium.Cartographic.fromDegrees(coord[0], coord[1], coord[2]);
  return Cesium.Cartographic.toCartesian(cartographic);
});
var pointCollection = new Cesium.PointPrimitiveCollection();

cartesianPoints.forEach(function(position) {
pointCollection.add({
  position: position,
  color: Cesium.Color.RED,
  pixelSize: 5
});
});
viewer.scene.primitives.add(pointCollection);

})

(需要注意的是,//显示点集后面的添加点的代码在生成三角网是不必要的,只是我觉得看着好看一点 )
cesium学习记录09-turf.js的使用(画矩形结合地形生成三角网)_第5张图片

五、使用turf.js生成三角形网格

即将Cesium.sampleTerrainMostDetailed(terrainProvider, positions)方法里面改为:

代码:

Cesium.sampleTerrainMostDetailed(terrainProvider, positions).then(function(samples) {
   var points = samples.map(function(sample) {
    return turf.point([Cesium.Math.toDegrees(sample.longitude), Cesium.Math.toDegrees(sample.latitude), sample.height],
    { z: sample.height })  // 将高度值保存到名为 'z' 的属性中);
  });
  // 创建一个FeatureCollection
var featureCollection = turf.featureCollection(points);
var tin = turf.tin(featureCollection, 'z');
var geometryInstances = [];  // 用于存放所有的三角形GeometryInstance
var instances = [];
// 遍历每个TIN三角形
tin.features.forEach(function(feature,i) {
    var coordinates = feature.geometry.coordinates[0];
    var heights = [feature.properties.a, feature.properties.b, feature.properties.c];
    var positions = coordinates.map(function(coord, index) {
        return Cesium.Cartesian3.fromDegrees(coord[0], coord[1], heights[index]);
    });
  viewer.entities.add({
    name: "三角面",
    id: "triangle"+i,
    polygon: {
      hierarchy: [positions[0], positions[1], positions[2]],
      heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
      perPositionHeight: true,
      material: Cesium.Color.fromCssColorString("#23B8BA").withAlpha(
        1.0
      ),
      //  extrudedHeight: 0,
      outline: true,
      outlineColor:  Cesium.Color.GREEN,
    }
  });
});

  var cartesianPoints = points.map(function(point) {
    var coord = point.geometry.coordinates;
    var cartographic = Cesium.Cartographic.fromDegrees(coord[0], coord[1], coord[2]);
    return Cesium.Cartographic.toCartesian(cartographic);
  });
  var pointCollection = new Cesium.PointPrimitiveCollection();

cartesianPoints.forEach(function(position) {
  pointCollection.add({
    position: position,
    color: Cesium.Color.RED,
    pixelSize: 5
  });
});
viewer.scene.primitives.add(pointCollection);
})

cesium学习记录09-turf.js的使用(画矩形结合地形生成三角网)_第6张图片

代码说明:

函数定义和参数:

GeneratingTriangulation(resultPoints,num) { … }: 这是定义GeneratingTriangulation方法的部分,它接受两个参数:resultPoints是一个包含矩形四个顶点坐标的数组;num是横向和纵向的点数。

创建一个矩形区域:

var rectangle = Cesium.Rectangle.fromDegrees(…):根据resultPoints中提供的坐标,创建一个矩形区域。

在这个矩形区域内生成点集:

var positions = [];: 初始化一个空数组,用于保存生成的点集的坐标。
通过两个嵌套循环在矩形区域内均匀地生成点集。

获取点集的高程信息:

Cesium.sampleTerrainMostDetailed(terrainProvider, positions).then(function(samples) { … }:使用Cesium的sampleTerrainMostDetailed函数获取每个点的高程信息。

转换高程采样点为Turf.js点:

将Cesium高程采样点转换为Turf.js点格式,并保存每个点的高程(Z值)。

生成TIN三角网:

var tin = turf.tin(featureCollection, ‘z’);: 使用Turf.js的tin函数,基于点集和它们的Z值生成TIN三角网。

在Cesium地图上绘制TIN三角网:

通过遍历每个TIN三角形,并为每个三角形创建一个Cesium的polygon实体。这些多边形实体有高度信息,因此它们会按照地形进行渲染。

在Cesium地图上绘制点集:

遍历cartesianPoints(点集的笛卡尔坐标),并将它们作为点实体添加到地图上,这样可以在地图上可视化这些点。

注解:

Cesium.Rectangle.fromDegrees(…):这个函数创建一个矩形,参数是矩形的西、南、东、北边界的经纬度。
Cesium.sampleTerrainMostDetailed(terrainProvider, positions):这个函数从给定的地形服务提供商中采样最详细级别的地形数据,以获取指定位置的地形高程。
turf.tin(…):这个函数是Turf.js库中的一个函数,用于根据输入的点集生成TIN三角网。Turf.js是一个JavaScript库,用于地理空间分析。
这个方法最终的结果是在Cesium地图上绘制了一个TIN三角网,该三角网是根据指定的矩形区域和点数生成的。每个三角形都是一个单独的Cesium实体,并且这些实体的顶点高度是基于地形数据的。

六、计算坡度

顺便计算一下坡度

tin.features.forEach中,添加如下代码:

 const point1 = positions[0];
    const point2 = positions[1];
    const point3 = positions[2];
    //使用我们新定义的函数计算每个三角形的坡度
    var analysisResult = that.calculateSlopeAndAspect(point1,point2,point3);

计算坡度的方法:

/**
   * 计算三角形的坡度
   * @param {*} point1 
   * @param {*} point2 
   * @param {*} point3 
   * @returns 
   */
  calculateSlopeAndAspect(point1, point2, point3) {
    // 计算两个向量,它们位于三角形的两边
    var v1 = Cesium.Cartesian3.subtract(point1, point2, new Cesium.Cartesian3());
    var v2 = Cesium.Cartesian3.subtract(point2, point3, new Cesium.Cartesian3());
    // 计算这两个向量的叉积,得到三角形的法向量
    var normal = new Cesium.Cartesian3();
    Cesium.Cartesian3.cross(v1, v2, normal);
    Cesium.Cartesian3.normalize(normal, normal);
    // 计算坡度:法向量和垂直向量(0,0,1)之间的夹角
    var up = new Cesium.Cartesian3(0, 0, 1);
    var slopeRadians = Math.acos(Cesium.Cartesian3.dot(normal, up));
    var slopeDegrees = Cesium.Math.toDegrees(slopeRadians);
    // 如果坡度大于90度,则将其减少到90度以下
    if (slopeDegrees > 90) {
      slopeDegrees = 180 - slopeDegrees;
  }
    // 返回坡度值
    return {slope: slopeDegrees};
  },

使用这个方法得到的坡度值定义三角形面的颜色:

即在 var analysisResult =...后面添加:

var slope = analysisResult.slope;
    var hue = slope / 90.0; // 将坡度从0到90度映射到色调从0到1
    var saturation = 1.0;   // 全饱和度
    var lightness = 0.5;    // 正常亮度
    var alpha = 1.0;        // 完全不透明
    //将HSL颜色转换为RGBA,当坡度为0度时,hue变为0,颜色是红色;当坡度为90度时,hue变为1,颜色是绿色;在0到90度之间的坡度将映射到从红色到绿色之间的颜色。
    var color = Cesium.Color.fromHsl(hue, saturation, lightness).withAlpha(alpha);

三角形的material修改为

material: color,

将三角网高度抬升一下,方便观察,提高个300米

var positions = coordinates.map(function(coord, index) {
        return Cesium.Cartesian3.fromDegrees(coord[0], coord[1], heights[index]+300);
    });

(越红坡度越大)

至于最开始的第三张图,中间的黄色体的实现就不再赘述了,无非是一个个五面体构成的(顶面就是每个三角面,然后三个四边形侧面(两个顶面点加两个底面点),再加上高度值一定的三角底面)

你可能感兴趣的:(cesium学习,javascript,学习,开发语言,gis,cesium)