Cesium 实现可视域分析
*前言:尝试了网上好多个版本的可视域分析,感觉都有一些问题,我这个也可能不是最完美的,但是我觉得对我来说够用了,实现效果如下*
此示例基于vue3上实现,cesium
版本1.101.0
,vite-plugin-cesium
版本1.2.22
新建一个名为ViewshedAnalysis.js
的JS文件
import glsl from './glsl2'
/**
* @param {Cesium.Viewer} viewer Cesium三维视窗。
* @param {Object} options 选项。
* @param {Cesium.Cartesian3} options.viewPosition 观测点位置。
* @param {Cesium.Cartesian3} options.viewPositionEnd 最远观测点位置(如果设置了观测距离,这个属性可以不设置)。
* @param {Number} options.viewDistance 观测距离(单位`米`,默认值100)。
* @param {Number} options.viewHeading 航向角(单位`度`,默认值0)。
* @param {Number} options.viewPitch 俯仰角(单位`度`,默认值0)。
* @param {Number} options.horizontalViewAngle 可视域水平夹角(单位`度`,默认值90)。
* @param {Number} options.verticalViewAngle 可视域垂直夹角(单位`度`,默认值60)。
* @param {Cesium.Color} options.visibleAreaColor 可视区域颜色(默认值`绿色`)。
* @param {Cesium.Color} options.invisibleAreaColor 不可视区域颜色(默认值`红色`)。
* @param {Boolean} options.enabled 阴影贴图是否可用。
* @param {Boolean} options.softShadows 是否启用柔和阴影。
* @param {Boolean} options.size 每个阴影贴图的大小。
*/
class ViewshedAnalysis {
constructor(viewer, options) {
this.viewer = viewer
this.viewPosition = options.viewPosition //开始坐标
this.viewPositionEnd = options.viewPositionEnd //结束坐标
this.viewDistance = this.viewPositionEnd
? Cesium.Cartesian3.distance(this.viewPosition, this.viewPositionEnd)
: options.viewDistance || 100.0 //观测距离
this.viewHeading = this.viewPositionEnd
? this.getHeading(this.viewPosition, this.viewPositionEnd)
: options.viewHeading || 0.0
this.viewPitch = this.viewPositionEnd
? this.getPitch(this.viewPosition, this.viewPositionEnd)
: options.viewPitch || 0.0
this.horizontalViewAngle = options.horizontalViewAngle || 90.0 //可视域的水平夹角
this.verticalViewAngle = options.verticalViewAngle || 60.0 //可视域的垂直夹角
this.visibleAreaColor = options.visibleAreaColor || Cesium.Color.GREEN
this.invisibleAreaColor = options.invisibleAreaColor || Cesium.Color.RED
this.enabled = typeof options.enabled === 'boolean' ? options.enabled : true
this.softShadows = typeof options.softShadows === 'boolean' ? options.softShadows : true
this.size = options.size || 2048
}
add() {
this.createLightCamera()
this.createShadowMap()
this.drawFrustumOutline(); //视锥线
this.drawSketch()
this.createPostStage()
}
update() {
this.clear()
this.add()
}
/**
* @method 更新终点坐标,从而实时更新绘制的实体的方向和半径
*
*/
updatePosition(viewPositionEnd) {
this.viewPositionEnd = viewPositionEnd
this.viewDistance = Cesium.Cartesian3.distance(this.viewPosition, this.viewPositionEnd) //观测距离
this.viewHeading = this.getHeading(this.viewPosition, this.viewPositionEnd)
this.viewPitch = this.getPitch(this.viewPosition, this.viewPositionEnd)
}
clear() {
if (this.sketch) {
this.viewer.entities.remove(this.sketch)
this.sketch = null
}
/* if (this.frustumOutline) {
this.viewer.scene.primitives.destroy();
this.frustumOutline = null;
} */
if (this.postStage) {
this.viewer.scene.postProcessStages.remove(this.postStage)
this.postStage = null
}
}
/**
* @method 创建相机
*/
createLightCamera() {
this.lightCamera = new Cesium.Camera(this.viewer.scene)
this.lightCamera.position = this.viewPosition
this.lightCamera.frustum.near = this.viewDistance * 0.001
this.lightCamera.frustum.far = this.viewDistance
const hr = Cesium.Math.toRadians(this.horizontalViewAngle)
const vr = Cesium.Math.toRadians(this.verticalViewAngle)
const aspectRatio =
(this.viewDistance * Math.tan(hr / 2) * 2) / (this.viewDistance * Math.tan(vr / 2) * 2)
this.lightCamera.frustum.aspectRatio = aspectRatio
if (hr > vr) {
this.lightCamera.frustum.fov = hr
} else {
this.lightCamera.frustum.fov = vr
}
this.lightCamera.setView({
destination: this.viewPosition,
orientation: {
heading: Cesium.Math.toRadians(this.viewHeading || 0),
pitch: Cesium.Math.toRadians(this.viewPitch || 0),
roll: 0
}
})
}
/**
* @method 创建阴影贴图
*/
createShadowMap() {
this.shadowMap = new Cesium.ShadowMap({
context: this.viewer.scene.context,
lightCamera: this.lightCamera,
enabled: this.enabled,
isPointLight: true,
pointLightRadius: this.viewDistance,
cascadesEnabled: false,
size: this.size,
softShadows: this.softShadows,
normalOffset: false,
fromLightSource: false
})
this.viewer.scene.shadowMap = this.shadowMap
}
/**
* @method 创建PostStage
* 导入的glsl是做片元着色的
*/
createPostStage() {
const fs = glsl
const postStage = new Cesium.PostProcessStage({
fragmentShader: fs,
uniforms: {
shadowMap_textureCube: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, '_frameState'))
return Reflect.get(this.shadowMap, '_shadowMapTexture')
},
shadowMap_matrix: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, '_frameState'))
return Reflect.get(this.shadowMap, '_shadowMapMatrix')
},
shadowMap_lightPositionEC: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, '_frameState'))
return Reflect.get(this.shadowMap, '_lightPositionEC')
},
shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, '_frameState'))
const bias = this.shadowMap._pointBias
return Cesium.Cartesian4.fromElements(
bias.normalOffsetScale,
this.shadowMap._distance,
this.shadowMap.maximumDistance,
0.0,
new Cesium.Cartesian4()
)
},
shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: () => {
this.shadowMap.update(Reflect.get(this.viewer.scene, '_frameState'))
const bias = this.shadowMap._pointBias
const scratchTexelStepSize = new Cesium.Cartesian2()
const texelStepSize = scratchTexelStepSize
texelStepSize.x = 1.0 / this.shadowMap._textureSize.x
texelStepSize.y = 1.0 / this.shadowMap._textureSize.y
return Cesium.Cartesian4.fromElements(
texelStepSize.x,
texelStepSize.y,
bias.depthBias,
bias.normalShadingSmooth,
new Cesium.Cartesian4()
)
},
camera_projection_matrix: this.lightCamera.frustum.projectionMatrix,
camera_view_matrix: this.lightCamera.viewMatrix,
helsing_viewDistance: () => {
return this.viewDistance
},
helsing_visibleAreaColor: this.visibleAreaColor,
helsing_invisibleAreaColor: this.invisibleAreaColor
}
})
this.postStage = this.viewer.scene.postProcessStages.add(postStage)
}
/**
* @method 创建视锥线
*/
drawFrustumOutline() {
const scratchRight = new Cesium.Cartesian3()
const scratchRotation = new Cesium.Matrix3()
const scratchOrientation = new Cesium.Quaternion()
const position = this.lightCamera.positionWC
const direction = this.lightCamera.directionWC
const up = this.lightCamera.upWC
let right = this.lightCamera.rightWC
right = Cesium.Cartesian3.negate(right, scratchRight)
let rotation = scratchRotation
Cesium.Matrix3.setColumn(rotation, 0, right, rotation)
Cesium.Matrix3.setColumn(rotation, 1, up, rotation)
Cesium.Matrix3.setColumn(rotation, 2, direction, rotation)
let orientation = Cesium.Quaternion.fromRotationMatrix(rotation, scratchOrientation)
let instance = new Cesium.GeometryInstance({
geometry: new Cesium.FrustumOutlineGeometry({
frustum: this.lightCamera.frustum,
origin: this.viewPosition,
orientation: orientation
}),
id: Math.random().toString(36).substr(2),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.YELLOWGREEN),
show: new Cesium.ShowGeometryInstanceAttribute(true)
}
})
this.frustumOutline = this.viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: [instance],
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
translucent: false
})
})
)
}
/**
* @method 创建视网
* 在实时绘制椭球实体时,其实不是一直创建entity,而是改变实体的方向(orientation)和改变椭球的半径(radii)
*/
drawSketch() {
this.sketch = this.viewer.entities.add({
name: 'sketch',
position: this.viewPosition,
orientation: new Cesium.CallbackProperty(() => {
return Cesium.Transforms.headingPitchRollQuaternion(
this.viewPosition,
Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - this.horizontalViewAngle, this.viewPitch, 0.5)
)
}, false),
ellipsoid: {
//椭球的半径
radii: new Cesium.CallbackProperty(() => {
return new Cesium.Cartesian3(this.viewDistance,
this.viewDistance,
this.viewDistance)
}, false),
innerRadii: new Cesium.Cartesian3(2.0, 2.0, 2.0), //椭球内部的半径
minimumClock: Cesium.Math.toRadians(-this.horizontalViewAngle / 2), //椭圆形的最小时钟角度
maximumClock: Cesium.Math.toRadians(this.horizontalViewAngle / 2), //椭球的最大时钟角度
minimumCone: Cesium.Math.toRadians(this.verticalViewAngle + 7.75), //椭圆形的最小圆锥角
maximumCone: Cesium.Math.toRadians(180 - this.verticalViewAngle - 7.75), //椭球的最大圆锥角
fill: false, //椭圆是否填充所提供的的材料
outline: true, //是否勾勒出椭圆形
subdivisions: 256, //每个轮廓环的样本数,确定曲率的粒度
stackPartitions: 64, //堆栈数的属性
slicePartitions: 64, //径向切片数量的属性
outlineColor: Cesium.Color.YELLOWGREEN //轮廓的颜色
}
})
}
/**
* @method 获取偏航角
*/
getHeading(fromPosition, toPosition) {
let finalPosition = new Cesium.Cartesian3()
let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition)
Cesium.Matrix4.inverse(matrix4, matrix4)
Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition)
Cesium.Cartesian3.normalize(finalPosition, finalPosition)
return Cesium.Math.toDegrees(Math.atan2(finalPosition.x, finalPosition.y))
}
/**
* @method 获取俯仰角
*/
getPitch(fromPosition, toPosition) {
let finalPosition = new Cesium.Cartesian3()
let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition)
Cesium.Matrix4.inverse(matrix4, matrix4)
Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition)
Cesium.Cartesian3.normalize(finalPosition, finalPosition)
return Cesium.Math.toDegrees(Math.asin(finalPosition.z))
}
}
export default ViewshedAnalysis
引入js:
import ViewShed from '@/utils/analysis/visibility/ViewshedAnalysis.js'
调用方法:
const shootAreaAnalysis = (type) => {
store.setSelected('shootArea')
let i = 0
var horizontalViewAngle = 90 //视角水平张角
var verticalViewAngle = 60 //视角垂直张角
var endPosition = null
var viewShed = null
var handler = new Cesium.ScreenSpaceEventHandler(window.Viewer.scene.canvas)
handler.setInputAction(movement => {
i++
if (i === 1) {
var startPosition = window.Viewer.scene.pickPosition(movement.position) //鼠标点击一次获取开始坐标
if (!startPosition) return
viewShed = new ViewShed(window.Viewer, {
viewPosition: startPosition,
viewPositionEnd: startPosition,
horizontalViewAngle: horizontalViewAngle,
verticalViewAngle: verticalViewAngle
})
// 鼠标移动的事件
handler.setInputAction(movement => {
endPosition = window.Viewer.scene.pickPosition(movement.endPosition)
if (!endPosition) return
viewShed.updatePosition(endPosition)
if (!viewShed.sketch) {
viewShed.drawSketch()
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
}
// 鼠标点击两次获取结束坐标
if (i === 2) {
i = 0
endPosition = window.Viewer.scene.pickPosition(movement.position)
viewShed.updatePosition(endPosition)
viewShed.update()
handler && handler.destroy() //销毁鼠标事件
store.setSelected(null)
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
}
glsl2.js 文件我找了两个, 我测试都是可以实现的,只是效果问题,可以切换了尝试一下
下载地址
Cesium 实现可视域分析的更多相关文章
- ARCGIS如何进行可视域分析
可视域分析在不同的领域有着广泛的应用,如火灾监控点的设定,观察哨所的设定等等.军事领域是可视域分析技术应用最广的领域.例如为了设计巡航导弹的航线,就必须对发射点到目标的地形进行分析,包括地形特征优劣分 ...
- ArcGIS API for JavaScript 4.2学习笔记[28] 可视域分析【使用Geoprocessor类】
想知道可视域分析是什么,就得知道可视域是什么 我们站在某个地方,原地不动转一圈能看到的所有事物就叫可视域.当然平地就没什么所谓的可视域. 如果在山区呢?可视范围就会被山体挡住了.这个分析对军事上有十分 ...
- 2.5 Cesium视域分析的实现
Cesium 视域分析 祝愿周末没事,技术继续分享交流,群685834990
- ArcGIS api for javascript——地理处理任务-计算一个可视域
描述 本例展示了使用一个地理处理计算一个可视域(viewshed) 单击地图上的任意点查看该点5英里内能看见的所有区域.这个模型需要几秒钟来运行并反馈结果. 可视域计算是通过ArcGIS Server ...
- Cesium专栏-填挖方分析(附源码下载)
Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精度,渲染质量以 ...
- ArcGIS进行视域分析及地形图制作
问题说明 开发商要在本区域建造观景亭,希望在观景亭上能看到优美的景色.根据提供的数据,完成以下要求. 一. 数据说明(见"题目3"文件夹) 1. DEM.tif:研究区域的数据高 ...
- 一文带你体验MRS HetuEngine如何实现跨源跨域分析
摘要: HetuEngine作为MRS服务中交互式分析&多源统一SQL引擎,亲自全程体验其如何实现多数据源的跨源跨域分析能力. 本文分享自华为云社区<MRS HetuEngine体验跨源 ...
- cesium billboard跨域问题2
这篇主要是对上一篇博客cesium billboard出现跨域的原理分析 https://www.cnblogs.com/SmilingEye/p/11363837.html 1.源码位置 从Bill ...
- cesium billboard跨域问题1
群里小伙伴问道使用billboard加载图片时出现跨域问题,一般认为在服务器端设置 Access-Control-Allow-Origin: * 例如用tomcat发布图片服务,可以这样设置:http ...
- BloodHound可视化之域分析
一.简介 BloodHound是一款将域内信息可视化的单页的web应用程序,是一款在域内进行信息收集的免费工具: bloodhound通过图与线的形式,将域内用户.计算机.组.会话.ACL以及域内所有 ...
随机推荐
- Vue TypeScript 实战:掌握静态类型编程
title: Vue TypeScript 实战:掌握静态类型编程 date: 2024/6/10 updated: 2024/6/10 excerpt: 这篇文章介绍了如何在TypeScript环境 ...
- ftl生成模板并从前台下载
1.生成模板的工具类 package com.jesims.busfundcallnew.util; import freemarker.template.Configuration; import ...
- java并发编程——CompletableFuture
简介 Java的java.util.concurrent包中提供了并发相关的接口和类,本文将重点介绍CompletableFuture并发操作类 JDK1.8新增CompletableFuture该类 ...
- ConvertLatOrLonFilter-经纬度格式转换-保留6位
ConvertLatOrLonFilter-经纬度格式转换-保留6位 /** * 转换经纬度 * 小数点最后最多为6位 * @param latOrLon * @return */ private S ...
- VIP视频解析
效果图 新建窗口 import tkinter as tk# 创建一个窗口 root = tk.Tk() # 设置窗口大小 root.geometry('700x250+200+200') # 设置标 ...
- textFieldShouldReturn: 方法无效化!
问题描述 不管如何在键盘上点击return,textFieldShouldReturn:方法一直没有调用. 问题代码 @interface ViewController : UIViewControl ...
- 【路径规划】 The Dynamic Window Approach to Collision Avoidance (附python代码实例)
引用与前言 参考链接 引用参考如下: 博客园解释:https://www.cnblogs.com/dlutjwh/p/11158233.html 这篇博客园写的贼棒!我当时就是一边对着论文一边对着他这 ...
- Ubuntu20.04中 ORBSLAM3的安装和测试
ORBSLAM3 安装以及测试教程(Ubuntu20.04) 1.前期准备工作 1.1安装相关依赖 sudo apt install git cmake gcc g++ mlocate 1.2下载OR ...
- Java 对象转XML xStream 别名的使用 附下载方式
下载方式 Maven方式 pom.xml中 <dependency> <groupId>xstream</groupId> <artifactId>xs ...
- MongoDB安装、基础操作和聚合实例详解
虽然MongoDB这些年很流行,但笔者之前没研究过,现在有需求研究这类NoSQL的数据库,是为了验证其是否可被替换. MongoDB是很轻量的文档数据库,简单测试也懒得专门准备虚拟机环境了,直接在ma ...