381 lines
11 KiB
JavaScript
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
|