Cesium-通视分析(附源码)

Cesium-通视分析(附源码)

Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品。它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精度,渲染质量以及多平台,易用性上都有高质量的保证。

通视分析


通视分析是指以某一点为观察点,研究某一区域通视情况的地形分析。利用DEM判断地形上任意两点之间是否可以互相可见的技术方法,分为视线通视分析和可视域分析,前者判断任意两点之间或者多点之间能否通视,后者对于给定的观察点,分析观察所覆盖的区域。

其中可视域是从一个或者多个观察的可以看见的地表范围。可视域分析是在栅格数据数据集上,对于给定的一个观察点,基于一定的相对高度,查找给定的范围内观察点所能通视覆盖的区域,也就是给定点的通视区域范围,分析结果是得到一个栅格数据集。在确定发射塔的位置、雷达扫描的区域、以及建立森林防火瞭望塔的时候,都会用到可视域分析。可视域分析在航海、航空以及军事方面有较为广泛的应用。

本章节首先处理这两种分析中的较为简单的通视分析,即给定的任意两点之间是否可见。

效果图

具体做法


原理:采用射线法来判别两点之间是否有其他物体所遮挡(比如Entity、Primitive、Terrain、3DTiles等)

接口API

  • Cesium.Cesium3DTileset
  • Cesium.Cartesian3
  • Cesium.Ray
  • Cesium.Scene

具体步骤
1.设置你自己的AccessToken,如果不用Ion上的网络资源可省略这步

Cesium.Ion.defaultAccessToken = 'xxxx';

2.初始化容器

var viewer = new Cesium.Viewer('cesiumContainer', {
    imageryProvider: new Cesium.UrlTemplateImageryProvider({
        url: 'http://www.google.cn/maps/vt?lyrs=s@716&x={x}&y={y}&z={z}'
    })
});

3.加载3dtiles模型,作为通视的模型

var tileset = new Cesium.Cesium3DTileset({
    url: 'http://localhost/demo/tileset.json',
});
viewer.scene.primitives.add(tileset);
tileset.readyPromise.then(function (argument) {
    viewer.camera.flyToBoundingSphere(tileset.boundingSphere);
});

4.指定观察点

// 视域点 设置观察点所在的视点高度为100m
var viewPoint = Cesium.Cartesian3.fromDegrees(x, y, 100);

5.计算目标点

本例计算距离观察点距离1km外 45°至135°之间每间隔1°共计90个点的通视情况

// 目标点集合
var destPoints = [];
// 视域点和目标点的距离
var radius = 1000; // 视距1000米
// 计算45°和135°之间的目标点
for (var i = 45; i <= 135; i++) {
    // 度数转弧度
    var radians = Cesium.Math.toRadians(i);
    // 计算目标点
    var toPoint = new Cesium.Cartesian3(viewPointWebMercator.x + radius * Math.cos(radians), viewPointWebMercator.y + radius * Math.sin(radians), 30);
    destPoints.push(Cesium.Cartographic.toCartesian(toPoint.clone()));
}

其中viewPointWebMercator对象是Cesium.WebMercatorProjection对象的实例,用于经纬度坐标和大地坐标转换

6.创建Ray对象,进行射线交互(注:待3dtiles模型加载完毕后执行,比如在页面上自己定义一个按钮,当模型加载完了后,按钮触发一个点击事件,调用一下pickFromRay方法,或者在console控制台直接执行pickFromRay方法。

for (var i = 0; i < destPoints.length; i++) {
    // 计算射线的方向,目标点left 视域点right
    var direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(destPoints[i], viewPoint, new Cesium.Cartesian3()), new Cesium.Cartesian3());
    // 建立射线
    var ray = new Cesium.Ray(viewPoint, direction);
    var result = viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
    showIntersection(result, destPoints[i], viewPoint);
}

7.处理射线交互结果

function showIntersection(result, destPoint, viewPoint) {
    // 如果是场景模型的交互点,排除交互点是地球表面
    if (Cesium.defined(result) && Cesium.defined(result.object)) {
        drawLine(result.position, viewPoint, Cesium.Color.GREEN); // 可视区域
        drawLine(result.position, destPoint, Cesium.Color.RED); // 不可视区域
    } else {
        drawLine(viewPoint, destPoint, Cesium.Color.GREEN);
    }
}

需要了解的


1.Cesium.Ray 对象

origin 是观察点 direction为方向,计算方向要注意的是从目标点朝观察点的方向

// 计算射线的方向,目标点left 视域点right
var direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(destPoints[i], viewPoint, new Cesium.Cartesian3()), new Cesium.Cartesian3());

2.Scene.prototype.pickFromRay 方法的定义(API文档中没有,需要查看cesium源码)

其中ray为上述的ray对象,objectsToExclude为可以过滤掉不用参与射线计算的对象,是primitives, entities, 或者 features的集合,比如说Scene中有一些辅助性的对象,比如坐标轴,目标点entity和观察点模型等等。

全部源码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta name="viewport"
          content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <title>通视分析</title>
    <script src="https://cesiumjs.org/releases/1.57/Build/Cesium/Cesium.js"></script>
    <link href="https://cesiumjs.org/releases/1.57/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
    <style>
        html,
        body,
        #cesiumContainer {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
            background-color: #000000;
        }
 
    </style>
 
</head>
 
<body>
<div id="cesiumContainer">
</div>
<script>
 
    Cesium.Ion.defaultAccessToken = 'xxxxxxxx(换成你自己的key)';
 
    // 初始化容器
    var viewer = new Cesium.Viewer('cesiumContainer', {
        imageryProvider: new Cesium.UrlTemplateImageryProvider({
            url: 'http://www.google.cn/maps/vt?lyrs=s@716&x={x}&y={y}&z={z}'
        })
    });
 
    // 开启地形深度监测
    viewer.scene.globe.depthTestAgainstTerrain = true;
 
    // 加载3dtile模型
    var tileset = new Cesium.Cesium3DTileset({
        url: 'xxxxx(换成你自己的3dtile服务地址)',
    });
    viewer.scene.primitives.add(tileset);
    tileset.readyPromise.then(function (argument) {
        // 更改相机状态
        viewer.camera.flyToBoundingSphere(tileset.boundingSphere);
    });
 
    // entity集合
    var parentEntity = viewer.entities.add(new Cesium.Entity());
 
    // 视域点
    var viewPoint = Cesium.Cartesian3.fromDegrees(lng, lat, height); // (自己的经纬度)
 
    var viewPointEntity = viewer.entities.add({
        parent: parentEntity,
        position: viewPoint,
        ellipsoid: {
            radii: new Cesium.Cartesian3(5, 5, 5),
            material: Cesium.Color.GREEN
        }
    });
 
    // 加载坐标轴,便于测试
    var transform = Cesium.Transforms.eastNorthUpToFixedFrame(viewPoint);
    var modelMatrixPrimitive = viewer.scene.primitives.add(new Cesium.DebugModelMatrixPrimitive({
        modelMatrix: transform,
        length: 1000.0
    }));
 
    // 世界坐标转换为投影坐标
    var webMercatorProjection = new Cesium.WebMercatorProjection(viewer.scene.globe.ellipsoid);
    var viewPointWebMercator = webMercatorProjection.project(Cesium.Cartographic.fromCartesian(viewPoint));
 
    // 排除碰撞监测的对象
    var objectsToExclude = [viewPointEntity, modelMatrixPrimitive];
 
    // 目标点集合
    var destPoints = [];
 
    // 视域点和目标点的距离
    var radius = 1000; // 视距1000米
 
    // 计算一圈
    for (var i = 0; i <= 360; i++) {
        // 度数转弧度
        var radians = Cesium.Math.toRadians(i);
        // 计算目标点
        var toPoint = new Cesium.Cartesian3(viewPointWebMercator.x + radius * Math.cos(radians), viewPointWebMercator.y + radius * Math.sin(radians), 30);
        // 投影坐标转世界坐标
        toPoint = webMercatorProjection.unproject(toPoint);
        destPoints.push(Cesium.Cartographic.toCartesian(toPoint.clone()));
 
        // 添加排除的辅助对象
        objectsToExclude.push(viewer.entities.add({
            parent: parentEntity,
            name: i,
            position: Cesium.Cartesian3.fromDegrees(Cesium.Math.toDegrees(toPoint.longitude), Cesium.Math.toDegrees(toPoint.latitude), 30),
            ellipsoid: {
                radii: new Cesium.Cartesian3(5, 5, 5),
                material: Cesium.Color.RED
            }
        }));
    }
 
    // 绘制线
    function drawLine(leftPoint, secPoint, color) {
        viewer.entities.add({
            polyline: {
                positions: [leftPoint, secPoint],
                arcType: Cesium.ArcType.NONE,
                width: 5,
                material: color,
                depthFailMaterial: color
            }
        })
    }
 
    function pickFromRay() {
        for (var i = 0; i < destPoints.length; i++) {
            // 计算射线的方向,目标点left 视域点right
            var direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(destPoints[i], viewPoint, new Cesium.Cartesian3()), new Cesium.Cartesian3());
            // 建立射线
            var ray = new Cesium.Ray(viewPoint, direction);
            var result = viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
            showIntersection(result, destPoints[i], viewPoint);
        }
    }
 
    // 处理交互点
    function showIntersection(result, destPoint, viewPoint) {
        // 如果是场景模型的交互点,排除交互点是地球表面
        if (Cesium.defined(result) && Cesium.defined(result.object)) {
            drawLine(result.position, viewPoint, Cesium.Color.GREEN); // 可视区域
            drawLine(result.position, destPoint, Cesium.Color.RED); // 不可视区域
        } else {
            drawLine(viewPoint, destPoint, Cesium.Color.GREEN);
        }
    }
 
</script>
</body>
 
</html>

3.7 3 投票数
文章评分
订阅评论
提醒
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x