*前言:尝试了网上好多个版本的可视域分析,感觉都有一些问题,我这个也可能不是最完美的,但是我觉得对我来说够用了,实现效果如下*

此示例基于vue3上实现,cesium版本1.101.0vite-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 实现可视域分析的更多相关文章

  1. ARCGIS如何进行可视域分析

    可视域分析在不同的领域有着广泛的应用,如火灾监控点的设定,观察哨所的设定等等.军事领域是可视域分析技术应用最广的领域.例如为了设计巡航导弹的航线,就必须对发射点到目标的地形进行分析,包括地形特征优劣分 ...

  2. ArcGIS API for JavaScript 4.2学习笔记[28] 可视域分析【使用Geoprocessor类】

    想知道可视域分析是什么,就得知道可视域是什么 我们站在某个地方,原地不动转一圈能看到的所有事物就叫可视域.当然平地就没什么所谓的可视域. 如果在山区呢?可视范围就会被山体挡住了.这个分析对军事上有十分 ...

  3. 2.5 Cesium视域分析的实现

    Cesium 视域分析 祝愿周末没事,技术继续分享交流,群685834990

  4. ArcGIS api for javascript——地理处理任务-计算一个可视域

    描述 本例展示了使用一个地理处理计算一个可视域(viewshed) 单击地图上的任意点查看该点5英里内能看见的所有区域.这个模型需要几秒钟来运行并反馈结果. 可视域计算是通过ArcGIS Server ...

  5. Cesium专栏-填挖方分析(附源码下载)

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

  6. ArcGIS进行视域分析及地形图制作

     问题说明 开发商要在本区域建造观景亭,希望在观景亭上能看到优美的景色.根据提供的数据,完成以下要求. 一. 数据说明(见"题目3"文件夹) 1. DEM.tif:研究区域的数据高 ...

  7. 一文带你体验MRS HetuEngine如何实现跨源跨域分析

    摘要: HetuEngine作为MRS服务中交互式分析&多源统一SQL引擎,亲自全程体验其如何实现多数据源的跨源跨域分析能力. 本文分享自华为云社区<MRS HetuEngine体验跨源 ...

  8. cesium billboard跨域问题2

    这篇主要是对上一篇博客cesium billboard出现跨域的原理分析 https://www.cnblogs.com/SmilingEye/p/11363837.html 1.源码位置 从Bill ...

  9. cesium billboard跨域问题1

    群里小伙伴问道使用billboard加载图片时出现跨域问题,一般认为在服务器端设置 Access-Control-Allow-Origin: * 例如用tomcat发布图片服务,可以这样设置:http ...

  10. BloodHound可视化之域分析

    一.简介 BloodHound是一款将域内信息可视化的单页的web应用程序,是一款在域内进行信息收集的免费工具: bloodhound通过图与线的形式,将域内用户.计算机.组.会话.ACL以及域内所有 ...

随机推荐

  1. Cygwin安装及简单说明

    1 简介 官方说明:Cygwin is a Linux-like environment for Windows. It consists of a DLL (cygwin1.dll), which ...

  2. 串口收发UART(Verilog HDL)

    UART(Universal Asynchronous Receiver Transmitter,通用异步收发器)是一种异步串行通信协议,主要用于计算机和嵌入式系统之间的数据交换. 实现UART通信的 ...

  3. Docker入门系列之四:Docker镜像

    在本文中,您将学习如何加快Docker构建周期并创建轻量级镜像.遵循之前的文章中的食物隐喻,我们将沙拉隐喻为Docker镜像,同时减少Docker镜像的数量. 在本系列的第3部分中,我们介绍了十几个D ...

  4. 接口加密传输设计及AES加解密代码DEMO

    接口加密传输设计及AES加解密代码DEMO 接口加密的方案设计:可以将请求的json字符串aes加密,通过params字段传输,接口服务端接收到参数,先解密,然后转换成对象.继续业务逻辑的处理.(另外 ...

  5. RAS非对称加解密-RAS加解密和签名和验签,密钥生成器(java代码)

    RAS非对称加解密-RAS加解密和签名和验签,密钥生成器(java代码)RSA 算法是一种非对称加解密算法.服务方生成一对 RSA 密钥,即公钥 + 私钥,将公钥提供给调用方,调用方使用公钥对数据进行 ...

  6. Python使用Matplotlib画以日期为X轴的图

    Python使用Matplotlib画以日期为X轴的图 步骤: 用pd把字符串格式的日期转成date格式. 使用 AutoDateLocator 设置x轴的属性. 1 from matplotlib ...

  7. Freertos学习:02-FreeRTOSConfig.h

    --- title: rtos-freertos-02-FreeRTOSConfig.h EntryName: rtos-freertos-02-FreeRTOSConfig date: 2020-0 ...

  8. 在centos开启防火墙没启动22/tcp or 22/udp的情况下是如何ssh连上的

    偶尔间查询防火墙的22/tcp or 22/udp ,看到是no的状态,而且此时也是ssh登陆的,然后就反复尝试,关闭22端口,开启,重载,重启.甚至连上另个服务器发现还是一样的情况.在群里问大佬们终 ...

  9. Redis 注册成windows 服务并开机自启动

    进入安装目录 输入命令redis-server --service-install redis.windows.conf   输入启动命令即可 redis-server --service-start ...

  10. Spark3学习【基于Java】5. Spark-Sql联表查询JOIN

    大数据场景下,联表远比微小型关系型数据库中使用的频繁.网上有句话: 传统数据库单机模式做Join的场景毕竟有限,也建议尽量减少使用Join. 然而大数据领域就完全不同,Join是标配,OLAP业务根本 ...