Files
sdk4.0_new/src/Obj/Analysis/CircleViewShed/index.js
2025-12-10 14:59:18 +08:00

425 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ViewShed.js
import Event from '../../../Event'
import MouseTip from '../../../MouseTip'
import Tools from '../../../Tools'
import EventBinding from '../../Element/Dialog/eventBinding'
import Dialog from '../../../BaseDialog'
import { html } from './_element'
/**
* @constructor
* @description 可视域分析
* @param sdk
* @param {Object} options 选项。
* @param {Number} options.viewPointHeight=1.8 视点高度(m)。
* @param {Number} options.precision=20 精度。
* @param {String} options.visibleAreaColor=#008000 可视区域颜色。
* @param {String} options.invisibleAreaColor=#FF0000 不可视区域颜色。
*/
class CircleViewShed extends Tools {
#intervalEvents = new Map()
constructor(sdk, options = {}, _Dialog = {}) {
super(sdk, options)
this.viewer = sdk.viewer
this.options = {}
this.options.visibleAreaColor = options.visibleAreaColor || '#008000'
this.options.invisibleAreaColor = options.invisibleAreaColor || '#FF0000'
this.ids = []
this.primitives = []
this.viewpointPrimitive = null
this._elms = {}
this.precision = options.precision
this.viewPointHeight = options.viewPointHeight
this.Dialog = _Dialog
this._EventBinding = new EventBinding()
this.html = null
this.tools = new Tools(sdk)
YJ.Analysis.AnalysesResults.push(this)
// CircleViewShed.edit(this)
// CircleViewShed.create(this)
}
draw() {
let terrainAvailability = this.viewer.terrainProvider.availability;
if (!terrainAvailability) {
return '未加载地形数据!'
}
CircleViewShed.create(this)
}
get viewPointHeight() {
return this.options.viewPointHeight
}
set viewPointHeight(v) {
let viewPointHeight = Math.floor(Number(v) * 10) / 10
if (isNaN(viewPointHeight)) {
viewPointHeight = 1.8
}
if (viewPointHeight < 0) {
viewPointHeight = 0
}
this.options.viewPointHeight = viewPointHeight
this._elms.viewPointHeight &&
this._elms.viewPointHeight.forEach(item => {
item.value = viewPointHeight
})
}
get viewPointHeights() {
return this.viewPointHeight
}
set viewPointHeights(v) {
let viewPointHeight = Math.floor(Number(v) * 10) / 10
if (isNaN(viewPointHeight)) {
viewPointHeight = 1.8
}
if (viewPointHeight < 0) {
viewPointHeight = 0
}
this.viewPointHeight = viewPointHeight
}
get precision() {
return this.options.precision
}
set precision(v) {
let precision = Math.floor(Number(v))
if (isNaN(precision)) {
precision = 20
} else if (precision < 1) {
precision = 1
}
this.options.precision = precision
this._elms.precision &&
this._elms.precision.forEach(item => {
item.value = precision
})
}
get precisions() {
return this.precision
}
set precisions(val) {
this.precision = val
}
static create(that) {
let terrainAvailability = that.viewer.terrainProvider.availability;
if (!terrainAvailability) {
return '未加载地形数据!'
}
let count = 0
if (!YJ.Measure.GetMeasureStatus()) {
if (that._DialogObject && that._DialogObject.close) {
that._DialogObject.close()
that._DialogObject = null
}
let Draw = new YJ.Draw.DrawCircle(that.sdk, {
tipText: '左键单击确定中心点位置右键单击取消区域绘制CTRL+右键单击撤销'
})
Draw.start(async (a, options) => {
// that.center = options.center
if (!options) {
switch (a) {
case 0:
Draw.tipText = '左键单击确定中心点位置右键单击取消区域绘制CTRL+右键单击撤销'
break;
case 1:
Draw.tipText = '左键单击确定圆形视域半径,完成圆形视域分析!'
break;
}
return
}
that.radius = options.radius
let positions = await Cesium.sampleTerrainMostDetailed(
that.sdk.viewer.terrainProvider,
[Cesium.Cartographic.fromDegrees(options.center.lng, options.center.lat)]
);
that.center = {
lng: options.center.lng,
lat: options.center.lat,
alt: positions[0].height
}
await that.analyse()
})
} else {
console.log('上一次测量未结束')
}
}
// static async edit(that) {
// if (that._DialogObject && that._DialogObject.close) {
// that._DialogObject.close()
// that._DialogObject = null
// }
// that._DialogObject = await new Dialog(that.sdk.viewer._container, {
// title: '圆形视域分析',
// left: '180px',
// top: '100px',
// closeCallBack: () => {
// that.Dialog.closeCallBack && that.Dialog.closeCallBack()
// YJ.Measure.SetMeasureStatus(false)
// }
// })
// await that._DialogObject.init()
// that._DialogObject._element.body.className =
// that._DialogObject._element.body.className + ' circle-view-shed'
// let contentElm = document.createElement('div')
// contentElm.innerHTML = html()
// that._DialogObject.contentAppChild(contentElm)
// let drawElm = document.createElement('button')
// drawElm.innerHTML = '绘制'
// drawElm.addEventListener('click', () => {
// let terrainAvailability = that.viewer.terrainProvider.availability;
// if (!terrainAvailability) {
// window.ELEMENT && window.ELEMENT.Message({
// message: '未加载地形数据!',
// type: 'warning',
// duration: 1500
// });
// return
// }
// CircleViewShed.create(that)
// })
// that._DialogObject.footAppChild(drawElm)
// let all_elm = contentElm.getElementsByTagName('*')
// that._EventBinding.on(that, all_elm)
// that._elms = that._EventBinding.element
// }
analyse() {
// this.destroy()
let center = [this.center.lng, this.center.lat]
let radius = this.radius / 1000
let circle = turf.circle(center, radius, {
steps: 180,
units: 'kilometers',
properties: { foo: 'bar' }
})
if (!this.viewpointPrimitive) {
this.viewpointPrimitive = this.viewer.scene.primitives.add(
new Cesium.PointPrimitiveCollection()
)
}
if (!this.viewBillboardPrimitive) {
this.viewBillboardPrimitive = this.viewer.scene.primitives.add(
new Cesium.BillboardCollection()
)
}
let array = []
let distance = radius / this.precision
for (let i = 1; i < circle.geometry.coordinates[0].length; i++) {
let line = turf.lineString([center, circle.geometry.coordinates[0][i]])
let array2 = []
for (let j = 1; j <= this.precision; j++) {
let sliced = turf.lineSliceAlong(line, 0, distance * j, {
units: 'kilometers'
})
array2.push([
sliced.geometry.coordinates[1][0],
sliced.geometry.coordinates[1][1]
])
}
array.push(array2)
}
let viewPoint = Cesium.Cartesian3.fromDegrees(
this.center.lng,
this.center.lat,
this.center.alt + this.viewPointHeight
)
let instances = []
CircleViewShed.getcanvas(this).then(canvas =>
this.viewBillboardPrimitive.add({
position: viewPoint,
image: canvas,
width: 200,
height: 140,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: Number.POSITIVE_INFINITY
})
)
this.viewpointPrimitive.add({
position: viewPoint,
color: Cesium.Color.AQUA.withAlpha(1),
pixelSize: 6
})
let m = 0
let _this = this
let key = this.randomString()
let intervalEvent = setInterval(() => {
if (m >= array.length) {
let item = this.#intervalEvents.get(key)
item && clearInterval(item.event)
return
}
InBatches(m)
m += 1
}, 0)
this.#intervalEvents.set(key, { event: intervalEvent })
function InBatches(k) {
let instances = []
let i = k
for (let j = 0; j < array[i].length; j++) {
let pt1 = array[i][j]
let pt2
let pt3
let pt4 = array[i][j - 1]
if (i == array.length - 1) {
pt2 = array[0][j]
pt3 = array[0][j - 1]
} else {
pt2 = array[i + 1][j]
pt3 = array[i + 1][j - 1]
}
if (j == 0) {
pt3 = [...center]
pt4 = []
}
let cpt = [(pt1[0] + pt3[0]) / 2, (pt1[1] + pt3[1]) / 2]
let cartographic = Cesium.Cartographic.fromDegrees(cpt[0], cpt[1])
let height = _this.viewer.scene.globe.getHeight(cartographic)
let targetPoint = Cesium.Cartesian3.fromDegrees(cpt[0], cpt[1], height)
let direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(
targetPoint,
viewPoint,
new Cesium.Cartesian3()
),
new Cesium.Cartesian3()
)
let ray = new Cesium.Ray(viewPoint, direction)
let pickedObjects = _this.viewer.scene.drillPickFromRay(
ray,
_this.primitives
)
let result
for (let i = 0; i < pickedObjects.length; i++) {
if (pickedObjects[i].position) {
result = pickedObjects[i]
break
}
}
let color = Cesium.Color.LIME
if (
result &&
Math.abs(result.position.x - targetPoint.x) > 0.01 &&
Math.abs(result.position.y - targetPoint.y) > 0.01 &&
Math.abs(result.position.z - targetPoint.z) > 0.01
) {
color = Cesium.Color.RED
}
let polyline = new Cesium.GroundPolylineGeometry({
positions: Cesium.Cartesian3.fromDegreesArray([
...pt1,
...pt2,
...pt3,
...pt4,
...pt1
]),
width: 2
})
let polygonInstance = new Cesium.GeometryInstance({
geometry: polyline,
name: 'ViewershedPolygon',
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(color),
show: new Cesium.ShowGeometryInstanceAttribute(true) //显示或者隐藏
}
})
instances.push(polygonInstance)
}
_this.primitives.push(
_this.viewer.scene.primitives.add(
new Cesium.GroundPolylinePrimitive({
geometryInstances: instances,
appearance: new Cesium.PolylineColorAppearance()
})
)
)
}
}
static getcanvas(that) {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = 220
canvas.height = 140
canvas.style.background = '#000000'
let img = new Image()
const data = [
{
images: that.getSourceRootPath() + '/img/bubble/lng.png',
text: '经度:' + parseFloat(that.center.lng.toFixed(10)) + '°'
},
{
images: that.getSourceRootPath() + '/img/bubble/lat.png',
text: '纬度:' + parseFloat(that.center.lat.toFixed(10)) + '°'
},
{
images: that.getSourceRootPath() + '/img/bubble/h.png',
text: '视高:' + that.viewPointHeight + ' m'
},
{
images: that.getSourceRootPath() + '/img/bubble/radius.png',
text: '半径:' + that.radius + ' m'
}
]
img.src = that.getSourceRootPath() + '/img/bubble/bubble.png'
let imagesLoaded = 0
return new Promise(async (resolve, reject) => {
img.onload = () => {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
data.forEach((item, index) => {
const img = new Image()
img.src = item.images
img.onload = () => {
ctx.drawImage(img, 12, 12 + index * 26)
ctx.fillStyle = '#fff'
ctx.font = '12px Arial'
ctx.fillText(item.text, 44, 28 + index * 26)
imagesLoaded++
if (imagesLoaded === data.length) {
resolve(canvas)
}
}
})
}
})
}
destroy() {
for (const [key, value] of this.#intervalEvents) {
clearInterval(value.event)
}
this.#intervalEvents = new Map()
for (let i = 0; i < this.primitives.length; i++) {
this.viewer.scene.primitives.remove(this.primitives[i])
}
this.primitives = []
if (this.viewpointPrimitive) {
this.viewer.scene.primitives.remove(this.viewpointPrimitive)
this.viewpointPrimitive = null
}
if (this.viewBillboardPrimitive) {
this.viewer.scene.primitives.remove(this.viewBillboardPrimitive)
this.viewBillboardPrimitive = null
}
YJ.Measure.SetMeasureStatus(false)
}
}
export default CircleViewShed