Files
sdk4.0/src/Obj/Analysis/CircleViewShed/index.js
2025-07-03 18:18:08 +08:00

381 lines
11 KiB
JavaScript

// 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
YJ.Analysis.AnalysesResults.push(this)
CircleViewShed.edit(this)
// 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 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
})
}
static create(that) {
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)
Draw.start(async (a, options) => {
// that.center = options.center
if(!options) {
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