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以及域内所有 ...
随机推荐
- OpenCV笔记(6) Bitwise
源码: BitwiseAnd //dst = src1 & src2 public static void BitwiseAnd(InputArray src1, InputArray s ...
- 浅谈ChatGPT模型中的惩罚机制
本文由ChatMoney团队出品 在探讨ChatGPT模型的文本生成能力时,除了采样算法,惩罚机制同样扮演着至关重要的角色.这些机制不仅影响生成文本的多样性和创意性,还为我们提供了调整文本风格和质量的 ...
- Chapter1 p1 Output Image
由于本文章是对TinyRenderer的模仿,所以并不打算引入外部库. 那么我们第一步需要解决的就是图形输出的问题,毕竟,如果连渲染的结果都看不到,那还叫什么Renderer嘛. 由于不引入外部库,所 ...
- 流程控制之case
1.case语句作用 case和if一样,都是用于处理多分支的条件判断 但是在条件较多的情况,if嵌套太多就不够简洁了 case语句就更简洁和规范了 2.case用法参考 常见用法就是如根据用户输入的 ...
- Java中创建对象的5种方式总结
引言 作为Java开发人员,我们每天都会代码中创建对象,但我们通常使用依赖管理系统,比如Spring框架,然后,这里有很多种创建对象的方式,本文就对Java创建对象的几种方式进行总结 五种创建方式 创 ...
- 增补博客 第一篇 python 简易带参计算器
设计一个简易的参数计算器.[输入格式]第一行输入待计算的带变量参数的计算式第二行输入各变量参数的赋值序列[输出格式]输出带变量参数的计算式的计算结果[输入样例]a+ba=1,b=10[输出样例]11 ...
- Postman使用记录,通过表格动态赋值循环调用接口 - Using CSV and JSON data files in the Postman Collection Runner
1.GET请求,通过导入csv文件来处理 GET http://localhost:8080/web/addstudent?sno={{sno}}&name={{name}}&sex= ...
- HTTP长连接、短连接、Linux网络优化
无连接 含义:每次传输完数据后就断开连接. 因为早期互联网规模小,并且http具有瞬时性,突发性,服务器同时处理着多个请求.所以采用无连接的方式.以便于腾出资源处理其他请求. 无状态 顺便说一说无状态 ...
- python基础-集合set { }
集合的定义和操作 集合的特性: 元素数量 支持多个 元素类型 任意 下标索引 支持 重复元素 不支持 可修改性 支持 数据有序 否 使用场景 不可重复的数据记录场景 # 定义集合 my_set = { ...
- 详解Web应用安全系列(6)安全配置错误
Web攻击中的安全配置错误漏洞是一个重要的安全问题,它涉及到对应用程序.框架.应用程序服务器.Web服务器.数据库服务器等组件的安全配置不当.这类漏洞往往由于配置过程中的疏忽或错误,使得攻击者能够未经 ...