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>