init
This commit is contained in:
89
src/Obj/AirLine/DrawTakeOff.js
Normal file
89
src/Obj/AirLine/DrawTakeOff.js
Normal file
@ -0,0 +1,89 @@
|
||||
import MouseTip from '../../MouseTip/index'
|
||||
import MouseEvent from '../../Event/index'
|
||||
import Draw from '../../Draw/draw'
|
||||
|
||||
class DrawTakeOff extends Draw {
|
||||
/**
|
||||
* @constructor
|
||||
* @desc 获取坐标点
|
||||
* */
|
||||
constructor(sdk, options = {}) {
|
||||
super(sdk, options)
|
||||
this.options.tipText = options.tipText
|
||||
? options.tipText
|
||||
: '左键确定,右键结束;'
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 开始动态获取坐标点
|
||||
* @method start
|
||||
* @param cb {function} 回调函数
|
||||
* @memberOf DrawPoint
|
||||
* @example draw.start((err,position)=>{
|
||||
*
|
||||
* })
|
||||
* */
|
||||
|
||||
start(cb) {
|
||||
// eslint-disable-next-line no-undef
|
||||
if (YJ.Measure.GetMeasureStatus()) {
|
||||
cb('上一次测量未结束')
|
||||
} else {
|
||||
let car = undefined
|
||||
// eslint-disable-next-line no-undef
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
// this.options
|
||||
this.tip = new MouseTip(this.options.tipText, this.sdk)
|
||||
this.event = new MouseEvent(this.sdk)
|
||||
this.isEntity = false
|
||||
this.event.mouse_left((movement, cartesian) => {
|
||||
this.end()
|
||||
let p = this.cartesian3Towgs84(car || cartesian, this.viewer)
|
||||
var pickedObject = this.viewer.scene.pick(movement.position)
|
||||
if (
|
||||
Cesium.defined(pickedObject) &&
|
||||
Cesium.defined(pickedObject.id) &&
|
||||
pickedObject.id.id === window.airportEntity.options.id
|
||||
) {
|
||||
this.isEntity = true
|
||||
}
|
||||
cb(null, p, this.isEntity)
|
||||
})
|
||||
this.event.mouse_right((movement, cartesian) => {
|
||||
this.end()
|
||||
cb(false)
|
||||
})
|
||||
this.event.mouse_move((movement, cartesian) => {
|
||||
car = cartesian
|
||||
this.tip.setPosition(
|
||||
cartesian,
|
||||
movement.endPosition.x,
|
||||
movement.endPosition.y
|
||||
)
|
||||
})
|
||||
|
||||
this.event.gesture_pinck_start((movement, cartesian) => {
|
||||
let startTime = new Date()
|
||||
this.event.gesture_pinck_end(() => {
|
||||
let endTime = new Date()
|
||||
if (endTime - startTime >= 500) {
|
||||
this.end()
|
||||
cb(false)
|
||||
} else {
|
||||
this.end()
|
||||
let p = this.cartesian3Towgs84(car || cartesian, this.viewer)
|
||||
cb(null, p)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
end() {
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
this.event.destroy()
|
||||
this.tip.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
export default DrawTakeOff
|
||||
112
src/Obj/AirLine/GenerateRoute.js
Normal file
112
src/Obj/AirLine/GenerateRoute.js
Normal file
@ -0,0 +1,112 @@
|
||||
// 根据米生成航线
|
||||
function flyLine(earth, positions, unit) {
|
||||
let arr = []
|
||||
positions.forEach(item => {
|
||||
arr.push([item.lng, item.lat])
|
||||
})
|
||||
arr.push(arr[0])
|
||||
// eslint-disable-next-line no-undef
|
||||
const turfPolygon = turf.polygon([arr])
|
||||
// 获取四至
|
||||
// eslint-disable-next-line no-undef
|
||||
const turfExtent = turf.bbox(turfPolygon)
|
||||
// 在turfPolygon中按网格取样点,网格间距3米
|
||||
// eslint-disable-next-line no-undef
|
||||
const turfSamplePoints = turf.pointGrid(turfExtent, unit / 1000, {
|
||||
units: 'kilometers',
|
||||
mask: turfPolygon
|
||||
})
|
||||
// 将turf取样点转为Cesium的取样点
|
||||
const cesiumSamplePoints = []
|
||||
for (let i = 0; i < turfSamplePoints.features.length; i++) {
|
||||
const coord = turfSamplePoints.features[i].geometry.coordinates
|
||||
cesiumSamplePoints.push([coord[0], coord[1]])
|
||||
}
|
||||
// 计算两点的距离
|
||||
function fromTo(point1, point2) {
|
||||
if (!point2) {
|
||||
return
|
||||
}
|
||||
// eslint-disable-next-line no-undef
|
||||
let from = turf.point(point1)
|
||||
// eslint-disable-next-line no-undef
|
||||
let to = turf.point(point2)
|
||||
// eslint-disable-next-line no-undef
|
||||
return Math.round(turf.distance(from, to) * 1000)
|
||||
}
|
||||
// 进行分组并进行奇数反转
|
||||
let polylinePosition = []
|
||||
let flag = 0
|
||||
let index = -1
|
||||
for (let i = 0; i < cesiumSamplePoints.length; i++) {
|
||||
if (fromTo(cesiumSamplePoints[i], cesiumSamplePoints[i + 1]) > unit) {
|
||||
index++
|
||||
let arr = cesiumSamplePoints.slice(flag, i + 1)
|
||||
if (index % 2 !== 0) {
|
||||
arr.reverse()
|
||||
}
|
||||
polylinePosition.push(arr)
|
||||
flag = i + 1
|
||||
}
|
||||
}
|
||||
let arr2 = cesiumSamplePoints.slice(flag, cesiumSamplePoints.length)
|
||||
if ((index + 1) % 2 !== 0) {
|
||||
arr2.reverse()
|
||||
}
|
||||
polylinePosition.push(arr2)
|
||||
return polylinePosition.flat()
|
||||
}
|
||||
// 获取高程
|
||||
function sampleHeightMostDetailed(earth, positions) {
|
||||
return earth.viewer.scene.sampleHeightMostDetailed([
|
||||
new Cesium.Cartographic.fromDegrees(positions[0], positions[1])
|
||||
])
|
||||
}
|
||||
// 计算高程
|
||||
async function countHeight(earth, item) {
|
||||
return await sampleHeightMostDetailed(earth, item)
|
||||
}
|
||||
function cartesian3Towgs84(earth, cartesian) {
|
||||
const viewer = earth.viewer
|
||||
var ellipsoid = viewer.scene.globe.ellipsoid
|
||||
var cartesian3 = new Cesium.Cartesian3(cartesian.x, cartesian.y, cartesian.z)
|
||||
var cartographic = ellipsoid.cartesianToCartographic(cartesian3)
|
||||
var lat = Cesium.Math.toDegrees(cartographic.latitude)
|
||||
var lng = Cesium.Math.toDegrees(cartographic.longitude)
|
||||
var alt = cartographic.height < 0 ? 0 : cartographic.height
|
||||
return {
|
||||
longitude: lng,
|
||||
latitude: lat,
|
||||
height: alt
|
||||
}
|
||||
}
|
||||
//更新路径高度
|
||||
async function updateHeight(earth, height, positions, cb) {
|
||||
let Cartesian = []
|
||||
let locationList = []
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
const element = positions[i]
|
||||
let Cartographic = await countHeight(earth, element)
|
||||
if (isNaN(Cartographic[0].height)) {
|
||||
Cartographic[0].height = 0
|
||||
}
|
||||
Cartographic[0].height = Cartographic[0].height + height
|
||||
Cartesian.push(Cesium.Cartographic.toCartesian(Cartographic[0]))
|
||||
if (i < positions.length) {
|
||||
cb({
|
||||
cur: i + 1,
|
||||
total: positions.length
|
||||
})
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < Cartesian.length; i++) {
|
||||
locationList.push(cartesian3Towgs84(earth, Cartesian[i]))
|
||||
}
|
||||
let arr = [locationList, Cartesian]
|
||||
cb(arr)
|
||||
}
|
||||
let GenerateRoute = {
|
||||
flyLine,
|
||||
updateHeight
|
||||
}
|
||||
export default GenerateRoute
|
||||
272
src/Obj/AirLine/billord_point_line.js
Normal file
272
src/Obj/AirLine/billord_point_line.js
Normal file
@ -0,0 +1,272 @@
|
||||
export default class BillordPointLine {
|
||||
constructor(options, viewer) {
|
||||
this.options = { ...options }
|
||||
this.pointEntity = null
|
||||
this.billboardEntity = null
|
||||
this.lineEntity = null
|
||||
this.updatedPosition = []
|
||||
this.pointUpdatedPosition = []
|
||||
this.viewer = viewer
|
||||
this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
|
||||
this.pinBuilder = new Cesium.PinBuilder()
|
||||
this.index = null
|
||||
//定义屏幕点击事件处理器
|
||||
BillordPointLine.setDefaultValue(this)
|
||||
BillordPointLine.init(this)
|
||||
}
|
||||
static setDefaultValue(that) {
|
||||
that.options.positions = that.options.positions || {}
|
||||
that.options.normalHeight = that.options.normalHeight || 100
|
||||
that.options.airHeight = that.options.airHeight || 100
|
||||
that.options.image = that.options.image || '/static/img/cluster2.png'
|
||||
that.options.show = that.options.show || true
|
||||
that.options.index = that.options.index || 1
|
||||
that.options.saveFun = that.options.saveFun || null
|
||||
that.options.selectFun = that.options.selectFun || null
|
||||
that.options.keyboard = that.options.keyboard ?? true
|
||||
that.options.updateFrustumFun = that.options.updateFrustumFun || null
|
||||
that.options.frustum = that.options.frustum || null
|
||||
}
|
||||
static init(that) {
|
||||
let positions = that.options.positions
|
||||
// 添加一个Point,稍微偏离地面高度,使其完全可见
|
||||
that.pointEntity = that.viewer.entities.add({
|
||||
show: that.options.show,
|
||||
position: Cesium.Cartesian3.fromDegrees(positions.lng, positions.lat, 0), // 地面上
|
||||
point: {
|
||||
pixelSize: 8,
|
||||
color: Cesium.Color.WITHE,
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, // 使用相对地面或贴地,
|
||||
disableDepthTestDistance: 0
|
||||
}
|
||||
})
|
||||
//
|
||||
let pinIndex = that.pinBuilder
|
||||
.fromText(
|
||||
that.options.index,
|
||||
Cesium.Color.fromCssColorString('#00d590'),
|
||||
36
|
||||
)
|
||||
.toDataURL()
|
||||
let altitude = 0
|
||||
if (positions.altitude) {
|
||||
altitude = positions.altitude
|
||||
} else {
|
||||
altitude = that.options.normalHeight
|
||||
}
|
||||
// 添加一个Billboard
|
||||
that.billboardEntity = that.viewer.entities.add({
|
||||
show: that.options.show,
|
||||
position: Cesium.Cartesian3.fromDegrees(
|
||||
positions.lng,
|
||||
positions.lat,
|
||||
positions.alt + that.options.normalHeight
|
||||
),
|
||||
// 判断altitude是否有值
|
||||
|
||||
label: {
|
||||
text: `Lat: ${positions.lng.toFixed(8)}\nLon: ${positions.lat.toFixed(
|
||||
8
|
||||
)}\nAlt: ${altitude.toFixed(8)}m`,
|
||||
font: '14px sans-serif',
|
||||
fillColor: Cesium.Color.YELLOW,
|
||||
outlineColor: Cesium.Color.BLACK,
|
||||
outlineWidth: 2,
|
||||
pixelOffset: new Cesium.Cartesian2(0, -60), // 标签偏移量,防止重叠
|
||||
heightReference: Cesium.HeightReference.RELATIVE_TO_TERRAIN, // 确保标签与地面贴合
|
||||
show: false
|
||||
},
|
||||
billboard: {
|
||||
image: pinIndex, // 示例图像路径
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 图像底部对齐
|
||||
width: 36,
|
||||
height: 36
|
||||
},
|
||||
index: that.options.index
|
||||
})
|
||||
|
||||
// 创建一个连接Point和Billboard的竖线
|
||||
that.lineEntity = that.viewer.entities.add({
|
||||
show: that.options.show,
|
||||
polyline: {
|
||||
positions: new Cesium.CallbackProperty(() => {
|
||||
return [
|
||||
that.pointEntity.position.getValue(),
|
||||
that.billboardEntity.position.getValue()
|
||||
]
|
||||
}, false),
|
||||
width: 1,
|
||||
material: new Cesium.PolylineOutlineMaterialProperty({
|
||||
outlineColor: Cesium.Color.GAINSBORO,
|
||||
outlineWidth: 1,
|
||||
color: Cesium.Color.WITHE
|
||||
}),
|
||||
zIndex: 99999999
|
||||
}
|
||||
})
|
||||
if (that.options.keyboard) {
|
||||
that.changeAltitude()
|
||||
}
|
||||
that.handler.setInputAction(function(movement) {
|
||||
var pickedObject = that.viewer.scene.pick(movement.position)
|
||||
if (
|
||||
Cesium.defined(pickedObject) &&
|
||||
Cesium.defined(pickedObject.id) &&
|
||||
pickedObject.id === that.billboardEntity
|
||||
) {
|
||||
if (that.options.selectFun) {
|
||||
that.options.selectFun(that.billboardEntity.index - 1)
|
||||
}
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
||||
}
|
||||
// 改变高度
|
||||
changeAltitude() {
|
||||
var isMouseDown = false
|
||||
var startPosition
|
||||
var initialHeight
|
||||
let that = this
|
||||
let HHH = false
|
||||
// 标识Alt键是否被按下
|
||||
document.addEventListener('keydown', function(event) {
|
||||
const key = event.key // 获取按下的键名
|
||||
// 检查特定键是否被按下
|
||||
if (key === 'h') {
|
||||
HHH = true
|
||||
}
|
||||
})
|
||||
document.addEventListener('keyup', function(event) {
|
||||
HHH = false
|
||||
})
|
||||
// 按下鼠标左键
|
||||
this.handler.setInputAction(function(movement) {
|
||||
var pickedObject = that.viewer.scene.pick(movement.position)
|
||||
if (
|
||||
Cesium.defined(pickedObject) &&
|
||||
Cesium.defined(pickedObject.id) &&
|
||||
pickedObject.id === that.billboardEntity
|
||||
) {
|
||||
isMouseDown = true
|
||||
startPosition = movement.position
|
||||
|
||||
// 获取Billboard当前的地理高度
|
||||
var positionCartographic = Cesium.Cartographic.fromCartesian(
|
||||
that.billboardEntity.position.getValue()
|
||||
)
|
||||
initialHeight = positionCartographic.height
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_DOWN)
|
||||
// 移动鼠标
|
||||
this.handler.setInputAction(function(movement) {
|
||||
if (isMouseDown && HHH == false) {
|
||||
// 移动位置
|
||||
var newCartesian = that.viewer.scene.pickPosition(movement.endPosition)
|
||||
that.disableCameraDrag(that.viewer, false)
|
||||
if (newCartesian) {
|
||||
var newCartographic = Cesium.Cartographic.fromCartesian(newCartesian)
|
||||
// 保持高度不变
|
||||
var newLongitude = newCartographic.longitude
|
||||
var newLatitude = newCartographic.latitude
|
||||
var updatedPosition = Cesium.Cartesian3.fromRadians(
|
||||
newLongitude,
|
||||
newLatitude,
|
||||
initialHeight
|
||||
)
|
||||
var pointUpdatedPosition = Cesium.Cartesian3.fromRadians(
|
||||
newLongitude,
|
||||
newLatitude,
|
||||
0
|
||||
)
|
||||
that.billboardEntity.position = new Cesium.CallbackProperty(() => {
|
||||
return updatedPosition
|
||||
}, false)
|
||||
that.billboardEntity.label.text = `Lat: ${Cesium.Math.toDegrees(
|
||||
newLongitude
|
||||
).toFixed(6)}\nLon: ${Cesium.Math.toDegrees(newLatitude).toFixed(
|
||||
6
|
||||
)}\nAlt: ${initialHeight.toFixed(2)}m`
|
||||
that.pointEntity.position = new Cesium.CallbackProperty(() => {
|
||||
return pointUpdatedPosition
|
||||
}, false)
|
||||
}
|
||||
}
|
||||
if (isMouseDown && HHH) {
|
||||
console.log(HHH)
|
||||
// 改变高度
|
||||
var endPosition = movement.endPosition
|
||||
var deltaY = startPosition.y - endPosition.y // 计算Y轴方向上的移动距离
|
||||
// 根据鼠标移动的距离来调整高度
|
||||
var newHeight = initialHeight + deltaY
|
||||
// 更新billboard位置
|
||||
var positionCartographic = Cesium.Cartographic.fromCartesian(
|
||||
that.billboardEntity.position.getValue()
|
||||
)
|
||||
var newPosition = Cesium.Cartesian3.fromDegrees(
|
||||
Cesium.Math.toDegrees(positionCartographic.longitude),
|
||||
Cesium.Math.toDegrees(positionCartographic.latitude),
|
||||
newHeight
|
||||
)
|
||||
// 禁用相机
|
||||
that.disableCameraDrag(that.viewer, false)
|
||||
that.billboardEntity.position = new Cesium.CallbackProperty(() => {
|
||||
return newPosition
|
||||
}, false)
|
||||
that.billboardEntity.label.text = `Lat: ${Cesium.Math.toDegrees(
|
||||
positionCartographic.longitude
|
||||
).toFixed(6)}\nLon: ${Cesium.Math.toDegrees(
|
||||
positionCartographic.latitude
|
||||
).toFixed(6)}\nAlt: ${newHeight.toFixed(2)}m`
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
||||
// 松开鼠标左键
|
||||
this.handler.setInputAction(function(movement) {
|
||||
var pickedObject = that.viewer.scene.pick(movement.position)
|
||||
isMouseDown = false // 禁用相机
|
||||
HHH = false //
|
||||
that.disableCameraDrag(that.viewer, true)
|
||||
// 更新frustum的位置
|
||||
if (
|
||||
Cesium.defined(pickedObject) &&
|
||||
Cesium.defined(pickedObject.id) &&
|
||||
pickedObject.id === that.billboardEntity
|
||||
) {
|
||||
if (that.options.saveFun) {
|
||||
that.options.saveFun(null, false)
|
||||
}
|
||||
if (that.options.selectFun) {
|
||||
that.options.selectFun(that.billboardEntity.index - 1)
|
||||
}
|
||||
that.options.frustum.updatePositionHeight(
|
||||
that.billboardEntity.position.getValue()
|
||||
)
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_UP)
|
||||
}
|
||||
|
||||
disableCameraDrag(viewer, bool) {
|
||||
viewer.scene.screenSpaceCameraController.enableRotate = bool
|
||||
viewer.scene.screenSpaceCameraController.enableTranslate = bool
|
||||
viewer.scene.screenSpaceCameraController.enableZoom = bool
|
||||
viewer.scene.screenSpaceCameraController.enableTilt = bool
|
||||
viewer.scene.screenSpaceCameraController.enableLook = bool
|
||||
}
|
||||
get show() {
|
||||
return this.options.show
|
||||
}
|
||||
/**
|
||||
* @param {boolean} bool
|
||||
*/
|
||||
set show(bool) {
|
||||
if (typeof bool === 'boolean') {
|
||||
this.pointEntity.show = bool
|
||||
this.billboardEntity.show = bool
|
||||
this.lineEntity.show = bool
|
||||
}
|
||||
}
|
||||
|
||||
remove() {
|
||||
this.viewer.entities.remove(this.pointEntity)
|
||||
this.viewer.entities.remove(this.billboardEntity)
|
||||
this.viewer.entities.remove(this.lineEntity)
|
||||
}
|
||||
}
|
||||
673
src/Obj/AirLine/frustum.js
Normal file
673
src/Obj/AirLine/frustum.js
Normal file
@ -0,0 +1,673 @@
|
||||
export default class Frustum {
|
||||
constructor(options, viewer, viewer1) {
|
||||
this.options = { ...options }
|
||||
this.viewer = viewer
|
||||
this.viewer1 = viewer1
|
||||
this.head = 0
|
||||
this.pitch = 90
|
||||
this.po = 0.00001
|
||||
this.position = null
|
||||
this.hpr = null
|
||||
this.currentFrustumOutline = null
|
||||
this.frustum = null
|
||||
this.setInterval1 = null
|
||||
this.webrtc = null
|
||||
|
||||
// 设置默认值
|
||||
Frustum.setDefaultValue(this)
|
||||
this.create()
|
||||
}
|
||||
|
||||
static setDefaultValue(that) {
|
||||
that.options.position = that.options.position || {}
|
||||
that.options.fov = that.options.fov || 30
|
||||
that.options.aspectRatio = that.options.aspectRatio || 1
|
||||
that.options.near = that.options.near || 1
|
||||
that.options.far = that.options.far || 120
|
||||
that.options.heading = that.options.heading || 0
|
||||
that.options.pitch = that.options.pitch || 90
|
||||
that.options.roll = that.options.roll || 0
|
||||
that.options.show = that.options.show ?? true
|
||||
that.options.videoUrl = that.options.videoUrl || ''
|
||||
that.options.index = that.options.index || 0
|
||||
that.options.arr = that.options.arr || []
|
||||
that.options.normalHeight = that.options.normalHeight || 100
|
||||
}
|
||||
|
||||
// 初始化视锥体
|
||||
create() {
|
||||
this.frustum = new Cesium.PerspectiveFrustum({
|
||||
fov: Cesium.Math.toRadians(this.options.fov),
|
||||
aspectRatio: this.options.aspectRatio,
|
||||
near: this.options.near,
|
||||
far: this.options.far
|
||||
})
|
||||
|
||||
const { lng, lat, alt } = this.options.position
|
||||
const { heading, pitch, roll } = this.options
|
||||
this.position = Cesium.Cartesian3.fromDegrees(
|
||||
lng,
|
||||
lat,
|
||||
alt + this.options.normalHeight
|
||||
)
|
||||
this.hpr = new Cesium.HeadingPitchRoll(
|
||||
Cesium.Math.toRadians(heading),
|
||||
Cesium.Math.toRadians(pitch),
|
||||
Cesium.Math.toRadians(roll)
|
||||
)
|
||||
|
||||
this.drawFrustumOutline()
|
||||
this.drawFrustumFilled()
|
||||
this.monitorKeyboard()
|
||||
this.updateFrustumSquareBase(40)
|
||||
this.syncHpr()
|
||||
if (this.options.videoUrl) {
|
||||
this.addVideoToFrustumTop2()
|
||||
}
|
||||
}
|
||||
|
||||
// 监听键盘事件
|
||||
monitorKeyboard() {
|
||||
const keyActions = {
|
||||
KeyQ: () => this.setIntervalhpr(-0.45),
|
||||
KeyE: () => this.setIntervalhpr(0.45),
|
||||
KeyB: () => this.setIntervalhprr(-0.45),
|
||||
KeyN: () => this.setIntervalhprr(0.45),
|
||||
KeyW: () => this.updateFrustumPosition('move', -0.00001),
|
||||
KeyS: () => this.updateFrustumPosition('move', 0.00001),
|
||||
KeyA: () => this.updateFrustumPosition('move', -0.00001, 0),
|
||||
KeyD: () => this.updateFrustumPosition('move', 0.00001, 0),
|
||||
KeyC: () => this.updateFrustumHeight(1), // 增加高度
|
||||
KeyZ: () => this.updateFrustumHeight(-1) // 降低高度
|
||||
}
|
||||
|
||||
this.keydownHandler = event => {
|
||||
if (keyActions[event.code]) keyActions[event.code]()
|
||||
}
|
||||
|
||||
this.keyupHandler = () => this.stopFrustumRotation()
|
||||
|
||||
document.addEventListener('keydown', this.keydownHandler)
|
||||
|
||||
document.addEventListener('keyup', this.keyupHandler)
|
||||
}
|
||||
// 渲染视频
|
||||
addVideoToFrustumTop() {
|
||||
// 创建视频元素
|
||||
const videoElement = document.createElement('video')
|
||||
videoElement.width = 640
|
||||
videoElement.height = 360
|
||||
videoElement.autoplay = true
|
||||
videoElement.loop = true
|
||||
videoElement.muted = true
|
||||
// videoElement.style.display = 'none'; // 隐藏视频元素
|
||||
document.body.appendChild(videoElement)
|
||||
|
||||
// 使用 flv.js 播放 FLV 视频
|
||||
if (flvjs.isSupported()) {
|
||||
const flvPlayer = flvjs.createPlayer({
|
||||
// url: 'http://zmkg.cqet.top:9991/live/2pUbcgTrly3mIDuxsDXN9h3hqcEKU6TlsV_YeIDyqHqXGzXafqWokXdU1q6j_S7hTCP7HynZQIsuNM6KQ5l-ag==.flv',
|
||||
type: 'flv',
|
||||
isLive: true,
|
||||
hasAudio: false,
|
||||
enableStashBuffer: true, //
|
||||
enableWorker: true,
|
||||
autoCleanupSourceBuffer: true, //自动清除缓存
|
||||
url: this.options.videoUrl
|
||||
})
|
||||
flvPlayer.attachMediaElement(videoElement)
|
||||
flvPlayer.load()
|
||||
flvPlayer.play()
|
||||
} else {
|
||||
console.error('FLV.js is not supported in this browser.')
|
||||
}
|
||||
|
||||
const corners = this.computeFrustumCorners(
|
||||
this.frustum,
|
||||
this.position,
|
||||
this.hpr
|
||||
)
|
||||
// 创建 PolygonGeometry 并应用视频作为纹理
|
||||
const polygonHierarchy = new Cesium.PolygonHierarchy([
|
||||
corners.bottomLeft,
|
||||
corners.bottomRight,
|
||||
corners.topRight,
|
||||
corners.topLeft
|
||||
])
|
||||
this.videoEntity = this.viewer.entities.add(
|
||||
new Cesium.Entity({
|
||||
id: '22222222',
|
||||
show: true,
|
||||
polygon: {
|
||||
hierarchy: polygonHierarchy
|
||||
}
|
||||
})
|
||||
)
|
||||
videoElement.addEventListener('loadeddata', () => {
|
||||
this.videoEntity.polygon.material = videoElement // 确保视频纹理加载后再设置
|
||||
})
|
||||
}
|
||||
// 渲染视频
|
||||
async addVideoToFrustumTop2() {
|
||||
// 创建视频元素
|
||||
const videoElement = document.createElement('video')
|
||||
videoElement.width = 640
|
||||
videoElement.height = 360
|
||||
videoElement.autoplay = true
|
||||
videoElement.loop = true
|
||||
videoElement.muted = true
|
||||
// videoElement.style.display = 'none'; // 隐藏视频元素
|
||||
document.body.appendChild(videoElement)
|
||||
await this.startPlay(videoElement, this.options.videoUrl)
|
||||
const corners = this.computeFrustumCorners(
|
||||
this.frustum,
|
||||
this.position,
|
||||
this.hpr
|
||||
)
|
||||
// 创建 PolygonGeometry 并应用视频作为纹理
|
||||
const polygonHierarchy = new Cesium.PolygonHierarchy([
|
||||
corners.bottomLeft,
|
||||
corners.bottomRight,
|
||||
corners.topRight,
|
||||
corners.topLeft
|
||||
])
|
||||
this.videoEntity = this.viewer.entities.add(
|
||||
new Cesium.Entity({
|
||||
id: '22222222',
|
||||
show: true,
|
||||
polygon: {
|
||||
hierarchy: polygonHierarchy
|
||||
}
|
||||
})
|
||||
)
|
||||
videoElement.addEventListener('loadeddata', () => {
|
||||
this.videoEntity.polygon.material = videoElement // 确保视频纹理加载后再设置
|
||||
})
|
||||
}
|
||||
async startPlay(element, url) {
|
||||
// Close existing SDK instance if any
|
||||
if (this.webrtc) {
|
||||
this.webrtc.close()
|
||||
}
|
||||
|
||||
// Initialize a new SDK instance
|
||||
this.webrtc = new SrsRtcWhipWhepAsync()
|
||||
|
||||
// Bind the video player to the SDK stream
|
||||
element.srcObject = this.webrtc.stream
|
||||
|
||||
try {
|
||||
const session = await this.webrtc.play(url)
|
||||
console.log(session)
|
||||
// this.sessionId = session.sessionid
|
||||
// this.simulatorUrl = `${session.simulator}?drop=1&username=${session.sessionid}`
|
||||
} catch (error) {
|
||||
// console.error('Error playing stream:', error)
|
||||
this.webrtc.close()
|
||||
// this.playerVisible = false
|
||||
}
|
||||
}
|
||||
// 计算视锥体远裁剪面(大面)的四个角点
|
||||
computeFrustumCorners(frustum, position, hpr) {
|
||||
const tanFov = Math.tan(frustum.fov * 0.5)
|
||||
const farHeight = frustum.far * tanFov
|
||||
const farWidth = farHeight * frustum.aspectRatio
|
||||
|
||||
const topLeft = new Cesium.Cartesian3(-farWidth, farHeight, -frustum.far)
|
||||
const topRight = new Cesium.Cartesian3(farWidth, farHeight, -frustum.far)
|
||||
const bottomLeft = new Cesium.Cartesian3(
|
||||
-farWidth,
|
||||
-farHeight,
|
||||
-frustum.far
|
||||
)
|
||||
const bottomRight = new Cesium.Cartesian3(
|
||||
farWidth,
|
||||
-farHeight,
|
||||
-frustum.far
|
||||
)
|
||||
const transform = Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
position,
|
||||
hpr
|
||||
)
|
||||
// console.log('transform111111111111111111111111', transform)
|
||||
return {
|
||||
topLeft: Cesium.Matrix4.multiplyByPoint(
|
||||
transform,
|
||||
topLeft,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
topRight: Cesium.Matrix4.multiplyByPoint(
|
||||
transform,
|
||||
topRight,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
bottomLeft: Cesium.Matrix4.multiplyByPoint(
|
||||
transform,
|
||||
bottomLeft,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
bottomRight: Cesium.Matrix4.multiplyByPoint(
|
||||
transform,
|
||||
bottomRight,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
}
|
||||
}
|
||||
// 封装的函数:更新 Polygon 面的位置
|
||||
updatePolygonPosition() {
|
||||
const corners = this.computeFrustumCorners(
|
||||
this.frustum,
|
||||
this.position,
|
||||
this.hpr
|
||||
)
|
||||
|
||||
this.videoEntity.polygon.hierarchy = new Cesium.CallbackProperty(e => {
|
||||
return new Cesium.PolygonHierarchy([
|
||||
corners.bottomLeft,
|
||||
corners.bottomRight,
|
||||
corners.topRight,
|
||||
corners.topLeft
|
||||
])
|
||||
})
|
||||
}
|
||||
// 更新锥体底部为正方形的方法
|
||||
updateFrustumSquareBase(value) {
|
||||
// 将输入值范围从 56 到 1 映射到面积范围 10000 到 100
|
||||
const minArea = 100 // 最小面积
|
||||
const maxArea = 10000 // 最大面积
|
||||
|
||||
// 映射公式(反转映射)
|
||||
const newArea = ((56 - value) / (56 - 1)) * (maxArea - minArea) + minArea
|
||||
|
||||
// 确保aspectRatio保持为1(正方形)
|
||||
this.frustum.aspectRatio = 1
|
||||
|
||||
// 根据面积计算正方形边长
|
||||
const sideLength = Math.sqrt(newArea)
|
||||
|
||||
// 远平面距离
|
||||
const far = this.frustum.far
|
||||
|
||||
// 计算新的fov
|
||||
const fov = 2 * Math.atan(sideLength / (2 * far))
|
||||
|
||||
// 更新视锥体的fov
|
||||
this.frustum.fov = fov
|
||||
|
||||
// 重新绘制视锥体轮廓和填充
|
||||
this.drawFrustumOutline()
|
||||
this.drawFrustumFilled()
|
||||
this.syncHpr()
|
||||
}
|
||||
updateFrustumHeight(deltaHeight) {
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(this.position)
|
||||
cartographic.height += deltaHeight // 更新高度
|
||||
this.position = Cesium.Cartesian3.fromDegrees(
|
||||
Cesium.Math.toDegrees(cartographic.longitude),
|
||||
Cesium.Math.toDegrees(cartographic.latitude),
|
||||
cartographic.height
|
||||
)
|
||||
this.options.position.alt = cartographic.height
|
||||
// this.options.arr[
|
||||
// this.options.index
|
||||
// ] = this.viewer.scene.globe.ellipsoid.cartographicToCartesian(cartographic)
|
||||
this.syncHpr()
|
||||
this.drawFrustumOutline() // 重新绘制视锥体轮廓
|
||||
this.drawFrustumFilled()
|
||||
}
|
||||
// 更新position变化后的视锥体属性
|
||||
updatePositionHeight(p) {
|
||||
this.options.position = this.cartesian3Towgs84(p)
|
||||
this.syncHpr()
|
||||
this.drawFrustumOutline() // 重新绘制视锥体轮廓
|
||||
this.drawFrustumFilled()
|
||||
}
|
||||
cartesian3Towgs84(cartesian) {
|
||||
var ellipsoid = this.viewer.scene.globe.ellipsoid
|
||||
var cartesian3 = new Cesium.Cartesian3(
|
||||
cartesian.x,
|
||||
cartesian.y,
|
||||
cartesian.z
|
||||
)
|
||||
var cartographic = ellipsoid.cartesianToCartographic(cartesian3)
|
||||
var lat = Cesium.Math.toDegrees(cartographic.latitude)
|
||||
var lng = Cesium.Math.toDegrees(cartographic.longitude)
|
||||
var alt = cartographic.height < 0 ? 0 : cartographic.height
|
||||
return {
|
||||
lng: lng,
|
||||
lat: lat,
|
||||
alt: alt
|
||||
}
|
||||
}
|
||||
setIntervalhpr(num) {
|
||||
this.stopFrustumRotation() // 先停止当前的定时器
|
||||
this.setInterval1 = setInterval(() => {
|
||||
this.head += num
|
||||
this.updateFrustumHPR(Cesium.Math.toRadians(this.head), this.pitch)
|
||||
}, 10)
|
||||
}
|
||||
setIntervalhprr(num) {
|
||||
this.stopFrustumRotation() // 先停止当前的定时器
|
||||
this.setInterval1 = setInterval(() => {
|
||||
// 限制 pitch 在 [60, 180] 范围内
|
||||
this.pitch = Math.max(60, Math.min(180, this.pitch + num))
|
||||
this.updateFrustumHPR(this.head, Cesium.Math.toRadians(this.pitch))
|
||||
}, 10)
|
||||
}
|
||||
// 停止视锥体旋转
|
||||
stopFrustumRotation() {
|
||||
if (this.setInterval1) {
|
||||
clearInterval(this.setInterval1)
|
||||
this.setInterval1 = null
|
||||
}
|
||||
}
|
||||
// 新增:绘制填充的视锥体
|
||||
drawFrustumFilled() {
|
||||
let that = this
|
||||
// console.log('that.options.show', that.options.show)
|
||||
|
||||
const transform = Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
this.position,
|
||||
this.hpr
|
||||
)
|
||||
|
||||
const frustumGeometry = new Cesium.FrustumGeometry({
|
||||
frustum: this.frustum,
|
||||
origin: Cesium.Matrix4.getTranslation(transform, new Cesium.Cartesian3()),
|
||||
orientation: Cesium.Quaternion.fromRotationMatrix(
|
||||
Cesium.Matrix4.getRotation(transform, new Cesium.Matrix3())
|
||||
)
|
||||
})
|
||||
|
||||
if (this.currentFrustumFilled) {
|
||||
this.viewer.scene.primitives.remove(this.currentFrustumFilled)
|
||||
}
|
||||
|
||||
this.currentFrustumFilled = new Cesium.Primitive({
|
||||
geometryInstances: new Cesium.GeometryInstance({
|
||||
geometry: frustumGeometry,
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.fromAlpha(Cesium.Color.YELLOW, 0.4) // 半透明黄色填充
|
||||
)
|
||||
}
|
||||
}),
|
||||
appearance: new Cesium.MaterialAppearance({
|
||||
material: Cesium.Material.fromType('Color', {
|
||||
color: Cesium.Color.fromAlpha(Cesium.Color.YELLOW, 0.4) // 填充颜色
|
||||
}),
|
||||
translucent: true
|
||||
}),
|
||||
asynchronous: false,
|
||||
// show: false
|
||||
show: that.options.show
|
||||
})
|
||||
|
||||
this.viewer.scene.primitives.add(this.currentFrustumFilled)
|
||||
}
|
||||
// 绘制视锥体轮廓
|
||||
drawFrustumOutline() {
|
||||
let that = this
|
||||
// console.log('that.options.show', that.options.show)
|
||||
|
||||
const transform = Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
this.position,
|
||||
this.hpr
|
||||
)
|
||||
|
||||
const frustumOutlineGeometry = new Cesium.FrustumOutlineGeometry({
|
||||
frustum: this.frustum,
|
||||
origin: Cesium.Matrix4.getTranslation(transform, new Cesium.Cartesian3()),
|
||||
orientation: Cesium.Quaternion.fromRotationMatrix(
|
||||
Cesium.Matrix4.getRotation(transform, new Cesium.Matrix3())
|
||||
)
|
||||
})
|
||||
|
||||
if (this.currentFrustumOutline) {
|
||||
this.viewer.scene.primitives.remove(this.currentFrustumOutline)
|
||||
}
|
||||
|
||||
this.currentFrustumOutline = new Cesium.Primitive({
|
||||
geometryInstances: new Cesium.GeometryInstance({
|
||||
geometry: frustumOutlineGeometry,
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.YELLOW
|
||||
)
|
||||
}
|
||||
}),
|
||||
appearance: new Cesium.PolylineColorAppearance({
|
||||
translucent: false
|
||||
}),
|
||||
asynchronous: false,
|
||||
show: that.options.show
|
||||
// show: false
|
||||
})
|
||||
|
||||
this.viewer.scene.primitives.add(this.currentFrustumOutline)
|
||||
}
|
||||
// 更新视锥体位置
|
||||
updateFrustumPosition(type = 'move', p, deg = 90, flag = true) {
|
||||
if (type === 'move') {
|
||||
// eslint-disable-next-line no-undef
|
||||
const point = turf.point([
|
||||
this.options.position.lng,
|
||||
this.options.position.lat
|
||||
])
|
||||
const degreesValue = Cesium.Math.toDegrees(this.hpr.heading)
|
||||
const bearing = degreesValue + deg
|
||||
const options = { units: 'degrees' }
|
||||
// eslint-disable-next-line no-undef
|
||||
const destination = turf.destination(point, p, bearing, options).geometry
|
||||
.coordinates
|
||||
|
||||
this.position = Cesium.Cartesian3.fromDegrees(
|
||||
destination[0],
|
||||
destination[1],
|
||||
this.options.position.alt + this.options.normalHeight
|
||||
)
|
||||
this.options.position.lng = destination[0]
|
||||
this.options.position.lat = destination[1]
|
||||
|
||||
this.viewer.camera.setView({
|
||||
destination: Cesium.Cartesian3.fromDegrees(
|
||||
destination[0],
|
||||
destination[1],
|
||||
this.viewer.camera.positionCartographic.height
|
||||
)
|
||||
})
|
||||
}
|
||||
if (type === 'update') {
|
||||
this.position = p
|
||||
this.options.videoUrl && this.updatePolygonPosition()
|
||||
}
|
||||
if (flag) {
|
||||
this.syncHpr()
|
||||
this.updateFrustumAttributes()
|
||||
}
|
||||
}
|
||||
// 同步视角
|
||||
syncHpr() {
|
||||
// console.log('this.viewer1', this.viewer1);
|
||||
if (this.viewer1) {
|
||||
const { lng, lat, alt } = this.options.position
|
||||
let pitch = -this.hpr.pitch - Cesium.Math.toRadians(-90.0)
|
||||
this.viewer1.camera.setView({
|
||||
destination: Cesium.Cartesian3.fromDegrees(
|
||||
lng,
|
||||
lat,
|
||||
alt + this.options.normalHeight
|
||||
),
|
||||
orientation: {
|
||||
heading: this.hpr.heading + Cesium.Math.toRadians(-90.0),
|
||||
pitch,
|
||||
roll: this.hpr.roll
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// 更新视锥体的 HeadingPitchRoll
|
||||
updateFrustumHPR(
|
||||
h = this.head,
|
||||
p = this.pitch,
|
||||
r = 0,
|
||||
flag = true,
|
||||
type = ''
|
||||
) {
|
||||
function degreesToRadians(degrees) {
|
||||
return (degrees * Math.PI) / 180.0
|
||||
}
|
||||
if (type == 'alone') {
|
||||
this.hpr.heading = degreesToRadians(h)
|
||||
this.hpr.pitch = degreesToRadians(p)
|
||||
this.hpr.roll = degreesToRadians(r)
|
||||
} else {
|
||||
this.hpr.heading = Cesium.Math.negativePiToPi(h)
|
||||
this.hpr.pitch = Cesium.Math.negativePiToPi(p)
|
||||
this.hpr.roll = Cesium.Math.negativePiToPi(r)
|
||||
}
|
||||
if (flag) {
|
||||
this.syncHpr()
|
||||
this.updateFrustumAttributes()
|
||||
}
|
||||
}
|
||||
// 用于更新
|
||||
updateFrustumAttributes() {
|
||||
let that = this
|
||||
// 检查 position 和 hpr 是否已初始化
|
||||
if (!this.position || !this.hpr) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Position or HPR is not defined:', this.position, this.hpr)
|
||||
return
|
||||
}
|
||||
|
||||
// 生成变换矩阵
|
||||
const transform = Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
this.position,
|
||||
this.hpr
|
||||
)
|
||||
|
||||
if (!transform) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Transform generation failed.')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 准备轮廓几何体和外观
|
||||
const outlineGeometry = new Cesium.FrustumOutlineGeometry({
|
||||
frustum: this.frustum,
|
||||
origin: Cesium.Matrix4.getTranslation(
|
||||
transform,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
orientation: Cesium.Quaternion.fromRotationMatrix(
|
||||
Cesium.Matrix4.getRotation(transform, new Cesium.Matrix3())
|
||||
)
|
||||
})
|
||||
|
||||
const outlineAppearance = new Cesium.PolylineColorAppearance({
|
||||
translucent: false
|
||||
})
|
||||
const outlineColor = Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.YELLOW
|
||||
)
|
||||
|
||||
// 准备填充几何体和外观
|
||||
const filledGeometry = new Cesium.FrustumGeometry({
|
||||
frustum: this.frustum,
|
||||
origin: Cesium.Matrix4.getTranslation(
|
||||
transform,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
orientation: Cesium.Quaternion.fromRotationMatrix(
|
||||
Cesium.Matrix4.getRotation(transform, new Cesium.Matrix3())
|
||||
)
|
||||
})
|
||||
|
||||
const filledAppearance = new Cesium.MaterialAppearance({
|
||||
material: Cesium.Material.fromType('Color', {
|
||||
color: Cesium.Color.YELLOW.withAlpha(0.5)
|
||||
}),
|
||||
translucent: true
|
||||
})
|
||||
const filledColor = Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.RED.withAlpha(0.5)
|
||||
)
|
||||
|
||||
// 删除旧的 Primitive
|
||||
if (this.currentFrustumOutline) {
|
||||
this.viewer.scene.primitives.remove(this.currentFrustumOutline)
|
||||
}
|
||||
if (this.currentFrustumFilled) {
|
||||
this.viewer.scene.primitives.remove(this.currentFrustumFilled)
|
||||
}
|
||||
|
||||
// 创建并添加新的轮廓 Primitive
|
||||
this.currentFrustumOutline = new Cesium.Primitive({
|
||||
geometryInstances: new Cesium.GeometryInstance({
|
||||
geometry: outlineGeometry,
|
||||
attributes: { color: outlineColor }
|
||||
}),
|
||||
appearance: outlineAppearance,
|
||||
asynchronous: false,
|
||||
show: that.options.show
|
||||
})
|
||||
this.viewer.scene.primitives.add(this.currentFrustumOutline)
|
||||
|
||||
// 创建并添加新的填充 Primitive
|
||||
this.currentFrustumFilled = new Cesium.Primitive({
|
||||
geometryInstances: new Cesium.GeometryInstance({
|
||||
geometry: filledGeometry,
|
||||
attributes: { color: filledColor }
|
||||
}),
|
||||
appearance: filledAppearance,
|
||||
asynchronous: false,
|
||||
show: that.options.show
|
||||
})
|
||||
this.viewer.scene.primitives.add(this.currentFrustumFilled)
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error in drawFrustum:', error)
|
||||
}
|
||||
}
|
||||
// 调整视锥体的 near 和 far 平面
|
||||
updateFrustumNearFar(newNear, newFar) {
|
||||
this.frustum.near = newNear
|
||||
this.frustum.far = newFar
|
||||
this.drawFrustumOutline()
|
||||
this.drawFrustumFilled()
|
||||
}
|
||||
// 调整视锥体的 fov
|
||||
updateFrustumFov(newFov) {
|
||||
this.frustum.fov = Cesium.Math.toRadians(newFov)
|
||||
this.drawFrustumOutline()
|
||||
this.drawFrustumFilled()
|
||||
}
|
||||
|
||||
get show() {
|
||||
return this.options.show
|
||||
}
|
||||
|
||||
set show(bool) {
|
||||
if (typeof bool === 'boolean') {
|
||||
this.options.show = bool
|
||||
this.currentFrustumOutline.show = bool
|
||||
this.currentFrustumFilled.show = bool
|
||||
}
|
||||
}
|
||||
|
||||
remove() {
|
||||
document.removeEventListener('keydown', this.keydownHandler)
|
||||
document.removeEventListener('keyup', this.keyupHandler)
|
||||
if (this.currentFrustumFilled) {
|
||||
this.viewer.scene.primitives.remove(this.currentFrustumFilled)
|
||||
}
|
||||
if (this.currentFrustumOutline) {
|
||||
this.viewer.scene.primitives.remove(this.currentFrustumOutline)
|
||||
}
|
||||
if (this.videoEntity) {
|
||||
this.viewer.entities.remove(this.videoEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
257
src/Obj/AirLine/frustum2.js
Normal file
257
src/Obj/AirLine/frustum2.js
Normal file
@ -0,0 +1,257 @@
|
||||
export default class FRUSTUN {
|
||||
constructor(options, viewer, viewer1) {
|
||||
this.options = { ...options }
|
||||
this.viewer = viewer
|
||||
this.viewer1 = viewer1
|
||||
this.head = 0
|
||||
this.po = 0.00001
|
||||
this.position = null
|
||||
this.hpr = null
|
||||
this.currentFrustumOutline = null
|
||||
this.frustum = null
|
||||
this.setInterval = null
|
||||
FRUSTUN.setDefaultValue(this)
|
||||
this.create()
|
||||
}
|
||||
static setDefaultValue(that) {
|
||||
that.options.position = that.options.position || {}
|
||||
that.options.fov = that.options.fov || 20
|
||||
that.options.aspectRatio = that.options.aspectRatio || 1
|
||||
that.options.near = that.options.near || 1
|
||||
that.options.far = that.options.far || 100
|
||||
that.options.heading = that.options.heading || 0
|
||||
that.options.pitch = that.options.pitch || 90
|
||||
that.options.roll = that.options.roll || 0
|
||||
that.options.show = that.options.show || true
|
||||
}
|
||||
// 初始化视锥体
|
||||
create() {
|
||||
this.frustum = new Cesium.PerspectiveFrustum()
|
||||
this.frustum.fov = Cesium.Math.toRadians(this.options.fov)
|
||||
this.frustum.aspectRatio = 1.0 // 保持 aspectRatio 不变
|
||||
this.frustum.near = 1.0
|
||||
this.frustum.far = 50.0
|
||||
// 设置初始视锥体位置和方向
|
||||
let { lng, lat, alt } = this.options.position
|
||||
let { heading, pitch, roll } = this.options
|
||||
this.position = Cesium.Cartesian3.fromDegrees(lng, lat, alt + 100)
|
||||
this.hpr = new Cesium.HeadingPitchRoll(
|
||||
Cesium.Math.toRadians(heading),
|
||||
Cesium.Math.toRadians(pitch),
|
||||
Cesium.Math.toRadians(roll)
|
||||
)
|
||||
console.log(heading)
|
||||
this.drawFrustumOutline()
|
||||
this.monitorKeyboard()
|
||||
}
|
||||
|
||||
setIntervalhpr(num) {
|
||||
if (!this.setInterval1) {
|
||||
this.setInterval1 = setInterval(() => {
|
||||
this.head += num
|
||||
this.updateFrustumHPR(Cesium.Math.toRadians(this.head))
|
||||
}, 10)
|
||||
}
|
||||
}
|
||||
// 监听 键盘事件
|
||||
// monitorKeyboard() {
|
||||
// let that = this;
|
||||
// document.addEventListener("keydown", function (event) {
|
||||
// switch (event.code) {
|
||||
// case "KeyQ":
|
||||
// that.setIntervalhpr(-0.45);
|
||||
// break;
|
||||
// case "KeyE":
|
||||
// that.setIntervalhpr(0.45);
|
||||
// break;
|
||||
// case "KeyW":
|
||||
// that.updateFrustumPosition("move", -0.00001);
|
||||
// break;
|
||||
// case "KeyS":
|
||||
// that.updateFrustumPosition("move", 0.00001);
|
||||
// break;
|
||||
// case "KeyA":
|
||||
// that.updateFrustumPosition("move", -0.00001, 0);
|
||||
// break;
|
||||
// case "KeyD":
|
||||
// that.updateFrustumPosition("move", 0.00001, 0);
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// });
|
||||
// document.addEventListener("keyup", function (event) {
|
||||
// clearInterval(that.setInterval1);
|
||||
// that.setInterval1 = null; // 重置 interval
|
||||
// });
|
||||
// }
|
||||
// 监听键盘事件
|
||||
monitorKeyboard() {
|
||||
const keyActions = {
|
||||
KeyQ: () => this.setIntervalhpr(-0.45),
|
||||
KeyE: () => this.setIntervalhpr(0.45),
|
||||
KeyW: () => this.updateFrustumPosition('move', -0.00001),
|
||||
KeyS: () => this.updateFrustumPosition('move', 0.00001),
|
||||
KeyA: () => this.updateFrustumPosition('move', -0.00001, 0),
|
||||
KeyD: () => this.updateFrustumPosition('move', 0.00001, 0),
|
||||
KeyC: () => this.updateFrustumHeight(1), // 增加高度
|
||||
KeyZ: () => this.updateFrustumHeight(-1) // 降低高度
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', event => {
|
||||
if (keyActions[event.code]) keyActions[event.code]()
|
||||
})
|
||||
|
||||
document.addEventListener('keyup', () => this.stopFrustumRotation())
|
||||
}
|
||||
// 停止视锥体旋转
|
||||
stopFrustumRotation() {
|
||||
clearInterval(this.setInterval1)
|
||||
this.setInterval1 = null
|
||||
}
|
||||
// 使用 HeadingPitchRoll 创建视锥体轮廓
|
||||
drawFrustumOutline() {
|
||||
const transform = Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
this.position,
|
||||
this.hpr
|
||||
)
|
||||
const frustumOutlineGeometry = new Cesium.FrustumOutlineGeometry({
|
||||
frustum: this.frustum,
|
||||
origin: Cesium.Matrix4.getTranslation(transform, new Cesium.Cartesian3()),
|
||||
orientation: Cesium.Quaternion.fromRotationMatrix(
|
||||
Cesium.Matrix4.getRotation(transform, new Cesium.Matrix3())
|
||||
)
|
||||
})
|
||||
|
||||
if (this.currentFrustumOutline) {
|
||||
this.viewer.scene.primitives.remove(this.currentFrustumOutline)
|
||||
}
|
||||
|
||||
this.currentFrustumOutline = new Cesium.Primitive({
|
||||
geometryInstances: new Cesium.GeometryInstance({
|
||||
geometry: frustumOutlineGeometry,
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.YELLOW
|
||||
)
|
||||
}
|
||||
}),
|
||||
appearance: new Cesium.PolylineColorAppearance({
|
||||
translucent: false
|
||||
}),
|
||||
asynchronous: false
|
||||
})
|
||||
|
||||
this.viewer.scene.primitives.add(this.currentFrustumOutline)
|
||||
}
|
||||
// 更新视锥体位置
|
||||
updateFrustumPosition(type = 'move', p, deg = 90) {
|
||||
if (type == 'move') {
|
||||
var point = turf.point([
|
||||
this.options.position.lng,
|
||||
this.options.position.lat
|
||||
])
|
||||
const degreesValue = Cesium.Math.toDegrees(this.hpr.heading)
|
||||
var distance = p
|
||||
var bearing = degreesValue + deg
|
||||
var options = { units: 'degrees' }
|
||||
|
||||
var destination = turf.destination(point, distance, bearing, options)
|
||||
.geometry.coordinates
|
||||
this.position = Cesium.Cartesian3.fromDegrees(
|
||||
destination[0],
|
||||
destination[1],
|
||||
this.options.position.alt + 100
|
||||
)
|
||||
this.options.position.lng = destination[0]
|
||||
this.options.position.lat = destination[1]
|
||||
|
||||
this.viewer.camera.setView({
|
||||
destination: Cesium.Cartesian3.fromDegrees(
|
||||
destination[0],
|
||||
destination[1],
|
||||
this.viewer.camera.positionCartographic.height
|
||||
),
|
||||
orientation: {
|
||||
heading: this.viewer.camera.heading,
|
||||
pitch: this.viewer.camera.pitch,
|
||||
roll: this.viewer.camera.roll
|
||||
}
|
||||
})
|
||||
}
|
||||
if (type == 'update') {
|
||||
this.position = p
|
||||
}
|
||||
// this.tongbuHpr();
|
||||
this.drawFrustumOutline()
|
||||
}
|
||||
// 同步视角
|
||||
tongbuHpr() {
|
||||
const { lng, lat, alt } = this.options.position
|
||||
this.viewer1.camera.setView({
|
||||
destination: new Cesium.Cartesian3.fromDegrees(lng, lat, alt + 100),
|
||||
orientation: {
|
||||
heading: this.hpr.heading + Cesium.Math.toRadians(-90.0),
|
||||
pitch: this.hpr.pitch + Cesium.Math.toRadians(-90.0),
|
||||
roll: this.hpr.roll
|
||||
}
|
||||
})
|
||||
}
|
||||
updateFrustumHeight(deltaHeight) {
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(this.position)
|
||||
cartographic.height += deltaHeight // 更新高度
|
||||
this.position = Cesium.Cartesian3.fromDegrees(
|
||||
Cesium.Math.toDegrees(cartographic.longitude),
|
||||
Cesium.Math.toDegrees(cartographic.latitude),
|
||||
cartographic.height
|
||||
)
|
||||
this.options.position.alt = cartographic.height
|
||||
// this.tongbuHpr();
|
||||
this.drawFrustumOutline() // 重新绘制视锥体轮廓
|
||||
}
|
||||
// 更新视锥体的 HeadingPitchRoll
|
||||
updateFrustumHPR(h = 0, p = Cesium.Math.toRadians(90), r = 0) {
|
||||
this.hpr.heading = Cesium.Math.negativePiToPi(h)
|
||||
this.hpr.pitch = Cesium.Math.negativePiToPi(p)
|
||||
this.hpr.roll = Cesium.Math.negativePiToPi(r)
|
||||
// this.tongbuHpr();
|
||||
this.drawFrustumOutline()
|
||||
}
|
||||
// 封装的函数:调整视锥体的 near 和 far 平面
|
||||
updateFrustumNearFar(newNear, newFar) {
|
||||
this.frustum.near = newNear
|
||||
this.frustum.far = newFar
|
||||
this.drawFrustumOutline()
|
||||
}
|
||||
// 封装的函数:调整视锥体的 fov(远裁剪面的大小)
|
||||
updateFrustumFov(newFov) {
|
||||
this.frustum.fov = Cesium.Math.toRadians(newFov)
|
||||
this.drawFrustumOutline()
|
||||
this.viewer.scene.requestRender() // 强制重新渲染场景
|
||||
}
|
||||
// 封装的函数:调整视锥体的高度
|
||||
updateFrustumHeight(deltaHeight) {
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(this.position)
|
||||
cartographic.height += deltaHeight
|
||||
this.position = Cesium.Cartesian3.fromDegrees(
|
||||
Cesium.Math.toDegrees(cartographic.longitude),
|
||||
Cesium.Math.toDegrees(cartographic.latitude),
|
||||
cartographic.height
|
||||
)
|
||||
this.drawFrustumOutline()
|
||||
this.viewer.scene.requestRender() // 强制重新渲染场景
|
||||
}
|
||||
get show() {
|
||||
return this.options.show
|
||||
}
|
||||
set show(bool) {
|
||||
if (typeof bool == 'boolean') {
|
||||
this.currentFrustumOutline.show = bool
|
||||
}
|
||||
}
|
||||
remove() {
|
||||
if (this.currentFrustumOutline) {
|
||||
this.viewer.scene.primitives.remove(this.currentFrustumOutline)
|
||||
}
|
||||
}
|
||||
}
|
||||
290
src/Obj/AirLine/index.js
Normal file
290
src/Obj/AirLine/index.js
Normal file
@ -0,0 +1,290 @@
|
||||
import { closeRotateAround, closeViewFollow} from '../../Global/global'
|
||||
export default class AirLine {
|
||||
constructor(options, earth) {
|
||||
this.earth = earth
|
||||
this.positions = null
|
||||
this.options = { ...options }
|
||||
this.Model = null
|
||||
this.pointCollection = null
|
||||
this.entity = null
|
||||
this.dataSouce = new Cesium.CustomDataSource('airLineSource' + options.id)
|
||||
AirLine.setDefaultValue(this)
|
||||
AirLine.add(this)
|
||||
}
|
||||
static setDefaultValue(that) {
|
||||
that.options.id = that.options.id || Cesium.createGuid()
|
||||
that.options.positions = that.options.positions || []
|
||||
that.options.width = that.options.width || 5
|
||||
that.options.show = that.options.show || true
|
||||
that.options.color = that.options.color || '#00FFFF'
|
||||
that.options.height = that.options.height || 20
|
||||
that.options.url = that.options.url || ''
|
||||
that.options.modelShow = that.options.modelShow || false
|
||||
that.options.flag = that.options.flag || true
|
||||
that.options.speed = that.options.speed || 1
|
||||
that.options.billboard.show = that.options.billboard.show || true
|
||||
that.options.billboard.image = that.options.billboard.image || ''
|
||||
that.options.billboard.scale = that.options.billboard.scale || 1
|
||||
that.options.billboard.width = that.options.billboard.width || 64
|
||||
that.options.billboard.height = that.options.billboard.height || 64
|
||||
that.options.billboard.near = that.options.billboard.near ?? 1000
|
||||
that.options.billboard.far = that.options.billboard.far ?? 10000
|
||||
}
|
||||
static add(that) {
|
||||
if (that.options.positions.length < 3) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('坐标数量至少需要3个')
|
||||
return
|
||||
}
|
||||
that.pointCollection = that.earth.viewer.scene.primitives.add(
|
||||
new Cesium.PointPrimitiveCollection()
|
||||
)
|
||||
for (let i = 0; i < that.options.positions.length - 1; i++) {
|
||||
let entity = that.createLine([
|
||||
that.options.positions[i],
|
||||
that.options.positions[i + 1]
|
||||
])
|
||||
that.dataSouce.entities.add(entity)
|
||||
}
|
||||
that.earth.viewer.dataSources.add(that.dataSouce).then(r => {})
|
||||
that.createPoint()
|
||||
that.createModel()
|
||||
}
|
||||
// 创建线条
|
||||
createLine(positions) {
|
||||
let entity = new Cesium.Entity({
|
||||
id: Cesium.createGuid(),
|
||||
polyline: {
|
||||
positions: positions,
|
||||
clampToGround: false,
|
||||
width: this.options.width,
|
||||
material: new Cesium.PolylineArrowMaterialProperty(
|
||||
Cesium.Color.fromCssColorString(this.options.color)
|
||||
),
|
||||
zIndex: 99999999
|
||||
},
|
||||
show: true,
|
||||
})
|
||||
return entity
|
||||
}
|
||||
// 生成点
|
||||
createPoint() {
|
||||
this.options.positions.forEach((item, index) => {
|
||||
this.pointCollection.add({
|
||||
show: true,
|
||||
position: item,
|
||||
pixelSize: 10.0,
|
||||
color: Cesium.Color.GREENYELLOW,
|
||||
id: index
|
||||
})
|
||||
})
|
||||
}
|
||||
get show() {
|
||||
return this.entity.show
|
||||
}
|
||||
set show(status) {
|
||||
if (typeof status == 'boolean') this.entity.show = status
|
||||
}
|
||||
get modelShow() {
|
||||
return this.Model.show
|
||||
}
|
||||
set modelShow(status) {
|
||||
if (typeof status == 'boolean') this.Model.show = status
|
||||
}
|
||||
// 添加一个模型
|
||||
createModel() {
|
||||
let scaleByDistance = new Cesium.NearFarScalar(
|
||||
this.options.billboard.near,
|
||||
1,
|
||||
this.options.billboard.far,
|
||||
0
|
||||
)
|
||||
const position = this.options.positions[0]
|
||||
const heading = Cesium.Math.toRadians(0)
|
||||
const pitch = 0
|
||||
const roll = 0
|
||||
const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
|
||||
const orientation = Cesium.Transforms.headingPitchRollQuaternion(
|
||||
position,
|
||||
hpr
|
||||
)
|
||||
this.Model = this.earth.viewer.entities.add({
|
||||
name: this.options.billboard.image,
|
||||
position,
|
||||
// orientation,
|
||||
billboard: {
|
||||
show: this.options.billboard.show,
|
||||
image: this.options.billboard.image,
|
||||
scale: this.options.billboard.scale,
|
||||
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
width: this.options.billboard.width,
|
||||
height: this.options.billboard.height,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
scaleByDistance,
|
||||
pixelOffsetScaleByDistance: scaleByDistance
|
||||
},
|
||||
// model: {
|
||||
// uri: this.options.url,
|
||||
// minimumPixelSize: 128,
|
||||
// maximumScale: 20000
|
||||
// },
|
||||
show: this.options.modelShow
|
||||
})
|
||||
}
|
||||
// 开始飞行
|
||||
start() {
|
||||
if (!this.options.positions) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('请先绘制并且生成航线')
|
||||
return
|
||||
}
|
||||
let i
|
||||
let that = this
|
||||
this.timer = setInterval(function() {
|
||||
if (this.index) {
|
||||
i = this.index
|
||||
} else {
|
||||
i = 0
|
||||
}
|
||||
i++
|
||||
this.index = i
|
||||
if (i > that.options.positions.length) {
|
||||
clearInterval(this.timer)
|
||||
} else {
|
||||
let position = that.options.positions[i]
|
||||
if (position) {
|
||||
that.Model.position = new Cesium.CallbackProperty(e => {
|
||||
return position
|
||||
})
|
||||
that.Model.orientation = that.direction(
|
||||
that.options.positions[i - 1],
|
||||
that.options.positions[i]
|
||||
).orientation
|
||||
}
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
// 停止飞行
|
||||
stop() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
}
|
||||
}
|
||||
// 飞入
|
||||
flyTo() {
|
||||
closeRotateAround(this.earth)
|
||||
closeViewFollow(this.earth)
|
||||
let positionArray = []
|
||||
for (let i = 0; i < this.options.positions.length; i++) {
|
||||
let item = this.options.positions[i]
|
||||
positionArray.push(item.x, item.y, item.z)
|
||||
}
|
||||
// console.log('cartesian',cartesian);
|
||||
// let position = Cesium.Cartographic.fromCartesian(cartesian)
|
||||
// console.log('position',position);
|
||||
let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray)
|
||||
this.earth.viewer.camera.flyToBoundingSphere(BoundingSphere, {
|
||||
offset: {
|
||||
heading: Cesium.Math.toRadians(0.0),
|
||||
pitch: Cesium.Math.toRadians(-90.0),
|
||||
roll: Cesium.Math.toRadians(0.0)
|
||||
}
|
||||
})
|
||||
}
|
||||
// 计算一个到另一个点的方向
|
||||
direction(pointA, pointB) {
|
||||
//向量AB
|
||||
const vector2 = Cesium.Cartesian3.subtract(
|
||||
pointB,
|
||||
pointA,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
//归一化
|
||||
const normal = Cesium.Cartesian3.normalize(vector2, new Cesium.Cartesian3())
|
||||
//旋转矩阵 rotationMatrixFromPositionVelocity源码中有,并未出现在cesiumAPI中
|
||||
const rotationMatrix3 = Cesium.Transforms.rotationMatrixFromPositionVelocity(
|
||||
pointA,
|
||||
normal,
|
||||
Cesium.Ellipsoid.WGS84
|
||||
)
|
||||
const modelMatrix4 = Cesium.Matrix4.fromRotationTranslation(
|
||||
rotationMatrix3,
|
||||
pointA
|
||||
)
|
||||
// 获取getHeadingPitchRoll
|
||||
let m1 = Cesium.Transforms.eastNorthUpToFixedFrame(
|
||||
Cesium.Matrix4.getTranslation(modelMatrix4, new Cesium.Cartesian3()),
|
||||
Cesium.Ellipsoid.WGS84,
|
||||
new Cesium.Matrix4()
|
||||
)
|
||||
// 矩阵相除
|
||||
let m3 = Cesium.Matrix4.multiply(
|
||||
Cesium.Matrix4.inverse(m1, new Cesium.Matrix4()),
|
||||
modelMatrix4,
|
||||
new Cesium.Matrix4()
|
||||
)
|
||||
// 得到旋转矩阵
|
||||
let mat3 = Cesium.Matrix4.getMatrix3(m3, new Cesium.Matrix3())
|
||||
// 计算四元数
|
||||
let q = Cesium.Quaternion.fromRotationMatrix(mat3)
|
||||
// 计算旋转角(弧度)
|
||||
let hpr = Cesium.HeadingPitchRoll.fromQuaternion(q)
|
||||
// hpr.pitch = hpr.pitch + 3.14 / 2 + 3.14;
|
||||
hpr.pitch = 0
|
||||
let orientation = Cesium.Transforms.headingPitchRollQuaternion(pointA, hpr)
|
||||
return { hpr, orientation }
|
||||
}
|
||||
// 删除
|
||||
remove() {
|
||||
this.earth.viewer.entities.remove(this.entity)
|
||||
this.dataSouce.entities.removeAll()
|
||||
this.pointCollection && this.pointCollection.destroy()
|
||||
this.Model && this.earth.viewer.entities.remove(this.Model)
|
||||
this.timer && clearInterval(this.timer)
|
||||
this.timer = null
|
||||
}
|
||||
// 返回照片数量 航点数
|
||||
getInfo() {
|
||||
return {
|
||||
length: this.countLength(),
|
||||
time: this.countTime(),
|
||||
waypoint: this.options.positions.length,
|
||||
photos: this.options.positions.length
|
||||
}
|
||||
}
|
||||
//计算航线得长度
|
||||
countLength() {
|
||||
let totalDistance = 0
|
||||
for (let i = 0; i < this.options.positions.length - 1; i++) {
|
||||
const start = this.options.positions[i]
|
||||
const end = this.options.positions[i + 1]
|
||||
const distance = Cesium.Cartesian3.distance(start, end)
|
||||
totalDistance += distance
|
||||
}
|
||||
return totalDistance.toFixed(2) // 返回保留两位小数的距离
|
||||
}
|
||||
//计算航线时间
|
||||
countTime() {
|
||||
let time = Math.floor(Number(this.countLength())) / this.options.speed
|
||||
let s = Math.floor(time % 60)
|
||||
let m = Math.floor(time / 60)
|
||||
let str = m + '分' + s + '秒'
|
||||
return str
|
||||
}
|
||||
//显示隐藏
|
||||
isShow(status) {
|
||||
if (typeof status === 'boolean') {
|
||||
this.modelShow = status
|
||||
this.dataSouce.show = status
|
||||
let len = this.pointCollection.length
|
||||
for (let i = 0; i < len; ++i) {
|
||||
let p = this.pointCollection.get(i)
|
||||
p.show = status
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('参数须为Boolean类型!')
|
||||
}
|
||||
}
|
||||
}
|
||||
359
src/Obj/AirLine/pointRoute.js
Normal file
359
src/Obj/AirLine/pointRoute.js
Normal file
@ -0,0 +1,359 @@
|
||||
import FRUSTUN from './frustum.js'
|
||||
import BillordPointLine from './billord_point_line'
|
||||
|
||||
export default class PointRoute {
|
||||
constructor(options = {}, viewer, viewer1) {
|
||||
this.options = { ...options }
|
||||
this.viewer = viewer
|
||||
this.viewer1 = viewer1
|
||||
this.entity = null
|
||||
this.frustum = null
|
||||
this.billordPointLineMaps = []
|
||||
this.index = 0
|
||||
this.positions = []
|
||||
PointRoute.setDefaultValue(this)
|
||||
this.create()
|
||||
}
|
||||
static setDefaultValue(that) {
|
||||
that.options.positions = that.options.positions || []
|
||||
that.options.show = that.options.show || true
|
||||
that.options.color = that.options.color || '#00d590'
|
||||
that.options.height = that.options.height || 500
|
||||
that.options.speed = that.options.speed || 1
|
||||
that.options.frustumShow = that.options.frustumShow ?? true
|
||||
that.options.saveFun = that.options.saveFun || null
|
||||
that.options.selectFun = that.options.selectFun || null
|
||||
that.options.keyboard = that.options.keyboard ?? true
|
||||
that.options.normalHeight = that.options.normalHeight || 100
|
||||
that.options.airHeight = that.options.airHeight || 100
|
||||
}
|
||||
create() {
|
||||
if (this.options.positions.length < 2) {
|
||||
return
|
||||
}
|
||||
let that = this
|
||||
let { frustumShow } = that.options
|
||||
this.entity = this.viewer.entities.add({
|
||||
show: this.options.show,
|
||||
polyline: {
|
||||
positions: new Cesium.CallbackProperty(() => {
|
||||
let positions = []
|
||||
for (let i = 0; i < this.billordPointLineMaps.length; i++) {
|
||||
const element = this.billordPointLineMaps[i]
|
||||
positions.push(element.billboardEntity.position.getValue())
|
||||
}
|
||||
return positions
|
||||
}, false),
|
||||
width: 3,
|
||||
material: Cesium.Color.fromCssColorString(this.options.color)
|
||||
}
|
||||
})
|
||||
// 创建点、线、billbord
|
||||
for (let i = 0; i < this.options.positions.length; i++) {
|
||||
const element = this.options.positions[i]
|
||||
// console.log("elementelementelement", element);
|
||||
if (frustumShow && i == this.index) {
|
||||
this.frustum = new FRUSTUN(
|
||||
{
|
||||
position: element,
|
||||
show: false,
|
||||
arr: this.options.positions,
|
||||
index: i,
|
||||
normalHeight: this.options.normalHeight
|
||||
},
|
||||
this.viewer,
|
||||
this.viewer1
|
||||
)
|
||||
}
|
||||
let op = new BillordPointLine(
|
||||
{
|
||||
positions: element,
|
||||
index: i + 1,
|
||||
saveFun: that.options.saveFun,
|
||||
selectFun: that.options.selectFun,
|
||||
keyboard: that.options.keyboard,
|
||||
updateFrustumFun: that.updateFrustumPosition,
|
||||
normalHeight: that.options.normalHeight,
|
||||
frustum: that.frustum,
|
||||
airHeight: that.options.airHeight
|
||||
},
|
||||
this.viewer
|
||||
)
|
||||
this.billordPointLineMaps.push(op)
|
||||
}
|
||||
this.onKey()
|
||||
}
|
||||
get show() {
|
||||
return this.options.show
|
||||
}
|
||||
set show(bool) {
|
||||
if (typeof bool === 'boolean') {
|
||||
this.frustum.currentFrustumOutline.show = bool
|
||||
this.billordPointLineMaps.forEach(item => {
|
||||
item.show = bool
|
||||
})
|
||||
this.entity.show = bool
|
||||
}
|
||||
}
|
||||
// 监听键盘事件
|
||||
onKey() {
|
||||
let that = this
|
||||
document.addEventListener('keydown', function(event) {
|
||||
switch (event.key) {
|
||||
case 'ArrowUp':
|
||||
that.index += 1
|
||||
that.updateFrustum(true)
|
||||
break
|
||||
case 'ArrowDown':
|
||||
that.index -= 1
|
||||
that.updateFrustum(false)
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
// 更新frustum
|
||||
updateFrustum(flag) {
|
||||
console.log(this.index)
|
||||
let obj
|
||||
if (this.index > this.options.positions.length - 1 || this.index < 0) {
|
||||
let str = this.index > 0 ? '已选中最后一个航点' : '已选中第一个航点'
|
||||
alert(str)
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < this.billordPointLineMaps.length; i++) {
|
||||
const element = this.billordPointLineMaps[i]
|
||||
const hpr = null
|
||||
if (i == this.index) {
|
||||
let position = element.billboardEntity.position.getValue()
|
||||
if (this.index !== 0) {
|
||||
obj = this.direction(
|
||||
this.billordPointLineMaps[
|
||||
i - 1
|
||||
].billboardEntity.position.getValue(),
|
||||
element.billboardEntity.position.getValue()
|
||||
)
|
||||
hpr = obj.hpr
|
||||
}
|
||||
if (this.index == 0) {
|
||||
obj = this.direction(
|
||||
this.billordPointLineMaps[0].billboardEntity.position.getValue(),
|
||||
this.billordPointLineMaps[1].billboardEntity.position.getValue()
|
||||
)
|
||||
hpr = obj.hpr
|
||||
}
|
||||
if (hpr) {
|
||||
this.frustum.updateFrustumHPR(
|
||||
hpr.heading,
|
||||
this.frustum.pitch,
|
||||
hpr.roll
|
||||
)
|
||||
}
|
||||
if (position) {
|
||||
this.frustum.updateFrustumPosition('update', position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cartesian3Towgs84(cartesian) {
|
||||
var ellipsoid = this.viewer.scene.globe.ellipsoid
|
||||
var cartesian3 = new Cesium.Cartesian3(
|
||||
cartesian.x,
|
||||
cartesian.y,
|
||||
cartesian.z
|
||||
)
|
||||
var cartographic = ellipsoid.cartesianToCartographic(cartesian3)
|
||||
var lat = Cesium.Math.toDegrees(cartographic.latitude)
|
||||
var lng = Cesium.Math.toDegrees(cartographic.longitude)
|
||||
var alt = cartographic.height < 0 ? 0 : cartographic.height
|
||||
return {
|
||||
lng: lng,
|
||||
lat: lat,
|
||||
alt: alt
|
||||
}
|
||||
}
|
||||
// 计算一个到另一个点的方向
|
||||
direction(pointA, pointB) {
|
||||
//向量AB
|
||||
const vector2 = Cesium.Cartesian3.subtract(
|
||||
pointA,
|
||||
pointB,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
//归一化
|
||||
const normal = Cesium.Cartesian3.normalize(vector2, new Cesium.Cartesian3())
|
||||
//旋转矩阵 rotationMatrixFromPositionVelocity源码中有,并未出现在cesiumAPI中
|
||||
const rotationMatrix3 = Cesium.Transforms.rotationMatrixFromPositionVelocity(
|
||||
pointA,
|
||||
normal,
|
||||
Cesium.Ellipsoid.WGS84
|
||||
)
|
||||
const modelMatrix4 = Cesium.Matrix4.fromRotationTranslation(
|
||||
rotationMatrix3,
|
||||
pointA
|
||||
)
|
||||
// 获取getHeadingPitchRoll
|
||||
let m1 = Cesium.Transforms.eastNorthUpToFixedFrame(
|
||||
Cesium.Matrix4.getTranslation(modelMatrix4, new Cesium.Cartesian3()),
|
||||
Cesium.Ellipsoid.WGS84,
|
||||
new Cesium.Matrix4()
|
||||
)
|
||||
// 矩阵相除
|
||||
let m3 = Cesium.Matrix4.multiply(
|
||||
Cesium.Matrix4.inverse(m1, new Cesium.Matrix4()),
|
||||
modelMatrix4,
|
||||
new Cesium.Matrix4()
|
||||
)
|
||||
// 得到旋转矩阵
|
||||
let mat3 = Cesium.Matrix4.getMatrix3(m3, new Cesium.Matrix3())
|
||||
// 计算四元数
|
||||
let q = Cesium.Quaternion.fromRotationMatrix(mat3)
|
||||
// 计算旋转角(弧度)
|
||||
let hpr = Cesium.HeadingPitchRoll.fromQuaternion(q)
|
||||
// hpr.pitch = hpr.pitch + 3.14 / 2 + 3.14;
|
||||
hpr.pitch = 90
|
||||
let orientation = Cesium.Transforms.headingPitchRollQuaternion(pointA, hpr)
|
||||
return { hpr, orientation }
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param {index} 索引
|
||||
*/
|
||||
// 删除航点
|
||||
delPosition(index) {
|
||||
this.options.positions.splice(index, 1)
|
||||
// this.options.positions = this.options.positions.filter((item, index) => index !== i);
|
||||
this.remove()
|
||||
this.create()
|
||||
}
|
||||
// 获取最新的positions
|
||||
getNewPositions() {
|
||||
let positions = []
|
||||
for (let i = 0; i < this.billordPointLineMaps.length; i++) {
|
||||
const element = this.billordPointLineMaps[i]
|
||||
let position = this.cartesian3Towgs84(
|
||||
element.billboardEntity.position.getValue()
|
||||
)
|
||||
positions.push(position)
|
||||
}
|
||||
return positions
|
||||
}
|
||||
// 删除
|
||||
remove() {
|
||||
this.billordPointLineMaps.forEach((item, i) => {
|
||||
item.remove()
|
||||
})
|
||||
if (this.frustum) {
|
||||
this.frustum.remove()
|
||||
}
|
||||
this.viewer.entities.remove(this.entity)
|
||||
this.billordPointLineMaps = []
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {Number} index
|
||||
* @param {Array} position
|
||||
*/
|
||||
// 新增航点 (before,after,end)
|
||||
addPoint(positions) {
|
||||
this.options.positions = positions
|
||||
this.remove()
|
||||
this.create()
|
||||
}
|
||||
// 根据选中的点更新视锥的位置
|
||||
updateFrustumPosition(index) {
|
||||
if (!this.billordPointLineMaps || this.billordPointLineMaps.length === 0)
|
||||
return
|
||||
if (this.frustum) {
|
||||
this.frustum.show = true
|
||||
}
|
||||
let current = this.billordPointLineMaps[
|
||||
index
|
||||
].billboardEntity.position.getValue()
|
||||
if (index !== 0) {
|
||||
let obj
|
||||
let after =
|
||||
index === this.billordPointLineMaps.length - 1
|
||||
? this.billordPointLineMaps[
|
||||
index - 1
|
||||
].billboardEntity.position.getValue() // 获取前一个位置
|
||||
: this.billordPointLineMaps[
|
||||
index + 1
|
||||
].billboardEntity.position.getValue() // 获取下一个位置
|
||||
obj = this.direction(
|
||||
index === this.billordPointLineMaps.length - 1 ? after : current,
|
||||
index === this.billordPointLineMaps.length - 1 ? current : after
|
||||
)
|
||||
let { hpr } = obj
|
||||
this.frustum.updateFrustumHPR(
|
||||
hpr.heading,
|
||||
Cesium.Math.toRadians(this.frustum.pitch),
|
||||
hpr.roll
|
||||
)
|
||||
} else {
|
||||
let obj
|
||||
let after = this.billordPointLineMaps[1].billboardEntity.position.getValue()
|
||||
obj = this.direction(current, after)
|
||||
let { hpr } = obj
|
||||
this.frustum.updateFrustumHPR(
|
||||
hpr.heading,
|
||||
Cesium.Math.toRadians(this.frustum.pitch),
|
||||
hpr.roll
|
||||
)
|
||||
}
|
||||
if (current) {
|
||||
this.frustum.updateFrustumPosition('update', current)
|
||||
}
|
||||
let position = this.cartesian3Towgs84(current)
|
||||
this.billordPointLineMaps.forEach(item => {
|
||||
item.billboardEntity.label.show = false // 先将所有元素的 label.show 设置为 false
|
||||
})
|
||||
const targetItem = this.billordPointLineMaps.find(
|
||||
item => item.billboardEntity.index == index + 1
|
||||
)
|
||||
if (targetItem) {
|
||||
targetItem.billboardEntity.label.show = true // 然后找到匹配的 index 设置为 true
|
||||
}
|
||||
return position
|
||||
}
|
||||
flyTo() {
|
||||
let positionArray = []
|
||||
for (let i = 0; i < this.options.positions.length; i++) {
|
||||
let a = Cesium.Cartesian3.fromDegrees(
|
||||
this.options.positions[i].lng,
|
||||
this.options.positions[i].lat,
|
||||
this.options.positions[i].alt + this.options.height
|
||||
)
|
||||
positionArray.push(a.x, a.y, a.z)
|
||||
}
|
||||
let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray)
|
||||
this.viewer.camera.flyToBoundingSphere(BoundingSphere, {
|
||||
offset: {
|
||||
heading: Cesium.Math.toRadians(0.0),
|
||||
pitch: Cesium.Math.toRadians(-80.0),
|
||||
roll: Cesium.Math.toRadians(0.0)
|
||||
}
|
||||
})
|
||||
}
|
||||
//计算航线的长度
|
||||
countLength() {
|
||||
if (this.options.positions.length < 2) {
|
||||
return 0
|
||||
} else {
|
||||
let lineString = []
|
||||
this.options.positions.forEach(item => {
|
||||
lineString.push([item.lng, item.lat])
|
||||
})
|
||||
var line = turf.lineString(lineString)
|
||||
return (turf.length(line) * 1000).toFixed(2)
|
||||
}
|
||||
}
|
||||
//计算航线时间
|
||||
countTime() {
|
||||
let time = Math.floor(Number(this.countLength())) / this.options.speed
|
||||
let s = Math.floor(time % 60)
|
||||
let m = Math.floor(time / 60)
|
||||
let str = m + '分' + s + '秒'
|
||||
return str
|
||||
}
|
||||
}
|
||||
25
src/Obj/Analysis/CircleViewShed/_element.js
Normal file
25
src/Obj/Analysis/CircleViewShed/_element.js
Normal file
@ -0,0 +1,25 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">视点高度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="999999" step="0.1" @model="viewPointHeight">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">采样精度</span>
|
||||
<input class="input" type="number" title="" min="1" max="100" step="1" @model="precision">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
512
src/Obj/Analysis/CircleViewShed/_index.js
Normal file
512
src/Obj/Analysis/CircleViewShed/_index.js
Normal file
@ -0,0 +1,512 @@
|
||||
/*
|
||||
* @Author: Wang jianLei
|
||||
* @Date: 2022-05-17 21:49:28
|
||||
* @Last Modified by: Wang JianLei
|
||||
* @Last Modified time: 2022-05-19 22:08:14
|
||||
*/
|
||||
|
||||
let ViewShed = function (sdk, canvasEleId) {
|
||||
if (!sdk.viewer) throw new Error("no viewer object!");
|
||||
alert(canvasEleId)
|
||||
let canvasEle = document.getElementById(canvasEleId);
|
||||
if (!canvasEle) throw new Error("the canvas element is not exist");
|
||||
this.canvasEle = canvasEle;
|
||||
this.viewer = sdk.viewer;
|
||||
this.handler = undefined;
|
||||
this.lightCamera;
|
||||
this.pyramid;
|
||||
this.frustumPrimitive;
|
||||
this.viewershedPolygon;
|
||||
};
|
||||
ViewShed.prototype = {
|
||||
/**
|
||||
* 初始化handler
|
||||
*/
|
||||
initHandler() {
|
||||
if (this.handler) {
|
||||
this.handler.destroy();
|
||||
this.handler = undefined;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 开始执行视域分析
|
||||
* @param {number} precision 精度,值越大创建耗时越长,建议在10~20之间
|
||||
*/
|
||||
createViewshed: function (precision) {
|
||||
let $this = this;
|
||||
let scene = $this.viewer.scene;
|
||||
$this.initHandler();
|
||||
$this.clearAll();
|
||||
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
|
||||
$this.handler.setInputAction((event) => {
|
||||
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
|
||||
scene.screenSpaceCameraController.enableRotate = false;
|
||||
scene.screenSpaceCameraController.enableZoom = false;
|
||||
scene.globe.depthTestAgainstTerrain = true;
|
||||
let earthPosition = scene.pickPosition(event.position);
|
||||
let pos = $this.cartesian3ToDegree(earthPosition);
|
||||
$this.handler.setInputAction(function (event) {
|
||||
let newPosition = scene.pickPosition(event.endPosition);
|
||||
if (Cesium.defined(newPosition)) {
|
||||
let pos1 = $this.cartesian3ToDegree(newPosition);
|
||||
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
|
||||
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
|
||||
let pitch = $this.getPitch(earthPosition, newPosition);
|
||||
$this.ViewShedOptions = {
|
||||
viewPosition: earthPosition, //观测点 笛卡尔坐标
|
||||
endPosition: newPosition, //目标点 笛卡尔坐标
|
||||
direction: angle, //观测方位角 默认为`0`,范围`0~360`
|
||||
pitch: pitch, //俯仰角,radius,默认为`0`
|
||||
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
|
||||
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
|
||||
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
|
||||
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
|
||||
visualRange: distance, //距离,单位`米`
|
||||
};
|
||||
$this.updateViewShed();
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
|
||||
|
||||
$this.handler.setInputAction(() => {
|
||||
$this.initHandler();
|
||||
// 开启地球旋转和缩放
|
||||
scene.screenSpaceCameraController.enableRotate = true;
|
||||
scene.screenSpaceCameraController.enableZoom = true;
|
||||
$this.drawViewershed(precision);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_UP);
|
||||
},
|
||||
|
||||
ReturnDistance(pos0, pos1) {
|
||||
let distance = 0;
|
||||
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
|
||||
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
|
||||
/**根据经纬度计算出距离**/
|
||||
let geodesic = new Cesium.EllipsoidGeodesic();
|
||||
geodesic.setEndPoints(point1cartographic, point2cartographic);
|
||||
let s = geodesic.surfaceDistance;
|
||||
return s;
|
||||
},
|
||||
getHeight(x, y, objectsToExclude) {
|
||||
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
|
||||
let endHeight = this.viewer.scene.sampleHeight(
|
||||
endCartographic,
|
||||
objectsToExclude
|
||||
);
|
||||
return endHeight;
|
||||
},
|
||||
|
||||
cartesian3ToDegree: function (Cartesian3) {
|
||||
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
|
||||
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
|
||||
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
|
||||
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
|
||||
let _alt = _cartographic.height;
|
||||
return [_lng, _lat, _alt];
|
||||
},
|
||||
getAngle: function (lng1, lat1, lng2, lat2) {
|
||||
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
|
||||
if (lng2 >= lng1) {
|
||||
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
|
||||
} else {
|
||||
dRotateAngle =
|
||||
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
|
||||
}
|
||||
dRotateAngle = (dRotateAngle * 180) / Math.PI;
|
||||
return dRotateAngle;
|
||||
},
|
||||
|
||||
getPitch(pointA, pointB) {
|
||||
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
|
||||
const vector = Cesium.Cartesian3.subtract(
|
||||
pointB,
|
||||
pointA,
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
let direction = Cesium.Matrix4.multiplyByPointAsVector(
|
||||
Cesium.Matrix4.inverse(transfrom, transfrom),
|
||||
vector,
|
||||
vector
|
||||
);
|
||||
Cesium.Cartesian3.normalize(direction, direction);
|
||||
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
|
||||
},
|
||||
|
||||
updateViewShed: function () {
|
||||
this.clear();
|
||||
this.setLightCamera();
|
||||
this.addVisualPyramid();
|
||||
this.createFrustum();
|
||||
},
|
||||
clear: function () {
|
||||
if (this.pyramid) {
|
||||
this.viewer.entities.removeById(this.pyramid.id);
|
||||
this.pyramid = undefined;
|
||||
}
|
||||
if (this.frustumPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.frustumPrimitive);
|
||||
this.frustumPrimitive = undefined;
|
||||
}
|
||||
if (this.debugModelMatrixPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
|
||||
this.debugModelMatrixPrimitive = undefined;
|
||||
}
|
||||
},
|
||||
clearAll: function () {
|
||||
this.clear();
|
||||
if (this.viewershedPolygon) {
|
||||
this.viewer.scene.primitives.remove(this.viewershedPolygon);
|
||||
this.viewershedPolygon = undefined;
|
||||
}
|
||||
},
|
||||
addVisualPyramid: function () {
|
||||
let options = this.ViewShedOptions;
|
||||
let position = options.viewPosition;
|
||||
let visualRange = Number(options.visualRange);
|
||||
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
|
||||
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.DebugModelMatrixPrimitive({
|
||||
modelMatrix: transform,
|
||||
length: 5.0,
|
||||
})
|
||||
);
|
||||
const halfClock = options.horizontalViewAngle / 2;
|
||||
const halfCone = options.verticalViewAngle / 2;
|
||||
const pitch = Cesium.Math.toDegrees(options.pitch);
|
||||
const ellipsoid = new Cesium.EllipsoidGraphics({
|
||||
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
|
||||
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
|
||||
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
|
||||
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
|
||||
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
|
||||
fill: false,
|
||||
outline: true,
|
||||
subdivisions: 256,
|
||||
stackPartitions: 64,
|
||||
slicePartitions: 64,
|
||||
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
|
||||
});
|
||||
const pyramidEntity = new Cesium.Entity({
|
||||
position: position,
|
||||
ellipsoid,
|
||||
});
|
||||
this.pyramid = this.viewer.entities.add(pyramidEntity);
|
||||
},
|
||||
setLightCamera: function () {
|
||||
if (!this.lightCamera) {
|
||||
this.lightCamera = new Cesium.Camera(this.viewer.scene);
|
||||
}
|
||||
let options = this.ViewShedOptions;
|
||||
let visualRange = Number(options.visualRange);
|
||||
this.lightCamera.position = options.viewPosition;
|
||||
this.lightCamera.frustum.near = 0.1;
|
||||
this.lightCamera.frustum.far = visualRange;
|
||||
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
|
||||
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
|
||||
this.lightCamera.frustum.aspectRatio =
|
||||
(visualRange * Math.tan(hr / 2) * 2) /
|
||||
(visualRange * Math.tan(vr / 2) * 2);
|
||||
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
|
||||
this.lightCamera.setView({
|
||||
destination: options.viewPosition,
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(options.direction || 0),
|
||||
pitch: options.pitch || 0,
|
||||
roll: 0,
|
||||
},
|
||||
});
|
||||
},
|
||||
createFrustum: function () {
|
||||
const scratchRight = new Cesium.Cartesian3();
|
||||
const scratchRotation = new Cesium.Matrix3();
|
||||
const scratchOrientation = new Cesium.Quaternion();
|
||||
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 instanceOutline = new Cesium.GeometryInstance({
|
||||
geometry: new Cesium.FrustumOutlineGeometry({
|
||||
frustum: this.lightCamera.frustum,
|
||||
origin: this.ViewShedOptions.viewPosition,
|
||||
orientation: orientation,
|
||||
}),
|
||||
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true),
|
||||
},
|
||||
});
|
||||
this.frustumPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.Primitive({
|
||||
geometryInstances: instanceOutline,
|
||||
appearance: new Cesium.PerInstanceColorAppearance({
|
||||
flat: true,
|
||||
translucent: false,
|
||||
closed: true,
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
createPoint: function (firstPos, secondPos) {
|
||||
let entity4FirstPos = new Cesium.Entity({
|
||||
name: "firstPos",
|
||||
show: true,
|
||||
position: firstPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 20,
|
||||
color: Cesium.Color.RED,
|
||||
outlineColor: Cesium.Color.YELLOW,
|
||||
outlineWidth: 5,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体起点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4FirstPos);
|
||||
let entity4SecondPos = new Cesium.Entity({
|
||||
name: "secondPos",
|
||||
show: true,
|
||||
position: secondPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 30,
|
||||
color: Cesium.Color.YELLOW,
|
||||
outlineColor: Cesium.Color.RED,
|
||||
outlineWidth: 8,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体视角终点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4SecondPos);
|
||||
},
|
||||
|
||||
//绘制可视域
|
||||
add(positionArr) {
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
Cesium.Cartesian3.fromDegreesArray(positionArr)
|
||||
),
|
||||
height: 0.0,
|
||||
extrudedHeight: 0.0,
|
||||
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
|
||||
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
|
||||
ellipsoid: Cesium.Ellipsoid.WGS84,
|
||||
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
|
||||
perPositionHeight: false, // 每个位置点使用的高度
|
||||
closeTop: true,
|
||||
closeBottom: true,
|
||||
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
|
||||
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
|
||||
});
|
||||
|
||||
let polygonInstance = new Cesium.GeometryInstance({
|
||||
geometry: polygon,
|
||||
name: "ViewershedPolygon",
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.BLUE.withAlpha(0.6)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
},
|
||||
});
|
||||
this.viewershedPolygon = this.viewer.scene.primitives.add(
|
||||
new Cesium.GroundPrimitive({
|
||||
geometryInstances: polygonInstance,
|
||||
appearance: new Cesium.EllipsoidSurfaceAppearance({
|
||||
aboveGround: true,
|
||||
material: new Cesium.Material({
|
||||
fabric: {
|
||||
type: "Image",
|
||||
uniforms: {
|
||||
image: this.returnImgae(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
drawViewershed(precision) {
|
||||
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
|
||||
const radius = this.ViewShedOptions.visualRange;
|
||||
const direction = this.ViewShedOptions.direction;
|
||||
let boundary = this.computeBoundaryOptions(pos, radius, direction);
|
||||
const bbox = boundary.bbox;
|
||||
let mask = turf.polygon([boundary.boundaryPoints]);
|
||||
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
|
||||
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
|
||||
|
||||
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
|
||||
let variogram = kriging.train(
|
||||
pointsResult.values,
|
||||
pointsResult.lngs,
|
||||
pointsResult.lats,
|
||||
"exponential",
|
||||
0,
|
||||
100
|
||||
);
|
||||
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
|
||||
const colors = [
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
];
|
||||
|
||||
this.canvasEle.width = 3840;
|
||||
this.canvasEle.height = 2160;
|
||||
kriging.plot(
|
||||
this.canvasEle,
|
||||
grid,
|
||||
[bbox[0], bbox[2]],
|
||||
[bbox[1], bbox[3]],
|
||||
colors
|
||||
);
|
||||
this.add(boundary.positionArr);
|
||||
},
|
||||
computeBoundaryOptions(pos, radius, angle) {
|
||||
let Ea = 6378137; // 赤道半径
|
||||
let Eb = 6356725; // 极半径
|
||||
const lng = pos[0],
|
||||
lat = pos[1];
|
||||
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
|
||||
let positionArr = [];
|
||||
let boundaryPoints = [];
|
||||
positionArr.push(lng, lat);
|
||||
boundaryPoints.push([lng, lat]);
|
||||
//正北是0°
|
||||
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
|
||||
let end = start + 90;
|
||||
for (let i = start; i <= end; i++) {
|
||||
let dx = radius * Math.sin((i * Math.PI) / 180.0);
|
||||
let dy = radius * Math.cos((i * Math.PI) / 180.0);
|
||||
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
|
||||
let ed = ec * Math.cos((lat * Math.PI) / 180);
|
||||
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
|
||||
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
|
||||
positionArr.push(BJD, BWD);
|
||||
boundaryPoints.push([BJD, BWD]);
|
||||
this.refreshBBox(bbox, BJD, BWD);
|
||||
}
|
||||
boundaryPoints.push([lng, lat]);
|
||||
return {
|
||||
positionArr,
|
||||
boundaryPoints,
|
||||
bbox,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 更新外围矩形 Bbox
|
||||
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
|
||||
* @param {Number} x 经度
|
||||
* @param {Number} y 纬度
|
||||
*/
|
||||
refreshBBox(result, x, y) {
|
||||
result[0] = x < result[0] ? x : result[0];
|
||||
result[1] = y < result[1] ? y : result[1];
|
||||
result[2] = x > result[2] ? x : result[2];
|
||||
result[3] = y > result[3] ? y : result[3];
|
||||
},
|
||||
/**
|
||||
* 插值点用射线判断通视性
|
||||
* @param {*} gridPoints 网格点
|
||||
* @param {*} step 步长,可以理解成是精度
|
||||
* @param {*} sourcePos 视域分析起点
|
||||
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
|
||||
*/
|
||||
createTargetPoints(gridPoints, step, sourcePos) {
|
||||
let positionArr = [];
|
||||
let objectsToExclude = [
|
||||
this.frustumPrimitive,
|
||||
this.pyramid,
|
||||
this.debugModelMatrixPrimitive,
|
||||
];
|
||||
let values = [],
|
||||
lngs = [],
|
||||
lats = [];
|
||||
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
|
||||
positionArr.push({
|
||||
x: sourcePos[0],
|
||||
y: sourcePos[1],
|
||||
z: height,
|
||||
});
|
||||
let viewPoint = this.ViewShedOptions.viewPosition;
|
||||
for (let index = 0; index < gridPoints.features.length; index++) {
|
||||
const feature = gridPoints.features[index];
|
||||
const coords = feature.geometry.coordinates;
|
||||
const x = coords[0],
|
||||
y = coords[1];
|
||||
let h = this.getHeight(x, y, objectsToExclude);
|
||||
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
|
||||
let direction = Cesium.Cartesian3.normalize(
|
||||
Cesium.Cartesian3.subtract(
|
||||
endPoint,
|
||||
viewPoint,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
// 建立射线
|
||||
let ray = new Cesium.Ray(viewPoint, direction);
|
||||
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
|
||||
if (result) {
|
||||
let buffer = this.ReturnDistance(endPoint, result.position);
|
||||
// let M_color = Cesium.Color.GREEN;
|
||||
if (buffer > step) {
|
||||
// M_color = Cesium.Color.RED;
|
||||
values.push(0);
|
||||
} else {
|
||||
values.push(1);
|
||||
}
|
||||
lngs.push(x);
|
||||
lats.push(y);
|
||||
// this.viewer.entities.add(
|
||||
// new Cesium.Entity({
|
||||
// name: "插值点哦",
|
||||
// show: true,
|
||||
// position: endPoint,
|
||||
// point: {
|
||||
// show: true,
|
||||
// pixelSize: 10,
|
||||
// color: M_color,
|
||||
// outlineWidth: 2,
|
||||
// outlineColor: Cesium.Color.YELLOW,
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
}
|
||||
}
|
||||
return {
|
||||
values,
|
||||
lngs,
|
||||
lats,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* canvas转image图片
|
||||
* @returns base64图片
|
||||
*/
|
||||
returnImgae() {
|
||||
return this.canvasEle.toDataURL("image/png");
|
||||
},
|
||||
};
|
||||
|
||||
export default ViewShed;
|
||||
131
src/Obj/Analysis/CircleViewShed/glsl.js
Normal file
131
src/Obj/Analysis/CircleViewShed/glsl.js
Normal file
@ -0,0 +1,131 @@
|
||||
export default `
|
||||
#define USE_CUBE_MAP_SHADOW true
|
||||
uniform sampler2D colorTexture;
|
||||
uniform sampler2D depthTexture;
|
||||
varying vec2 v_textureCoordinates;
|
||||
uniform mat4 camera_projection_matrix;
|
||||
uniform mat4 camera_view_matrix;
|
||||
uniform samplerCube shadowMap_textureCube;
|
||||
uniform mat4 shadowMap_matrix;
|
||||
uniform vec4 shadowMap_lightPositionEC;
|
||||
uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
|
||||
uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
|
||||
uniform float helsing_viewDistance;
|
||||
uniform vec4 helsing_visibleAreaColor;
|
||||
uniform vec4 helsing_invisibleAreaColor;
|
||||
|
||||
struct zx_shadowParameters
|
||||
{
|
||||
vec3 texCoords;
|
||||
float depthBias;
|
||||
float depth;
|
||||
float nDotL;
|
||||
vec2 texelStepSize;
|
||||
float normalShadingSmooth;
|
||||
float darkness;
|
||||
};
|
||||
|
||||
float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)
|
||||
{
|
||||
float depthBias = shadowParameters.depthBias;
|
||||
float depth = shadowParameters.depth;
|
||||
float nDotL = shadowParameters.nDotL;
|
||||
float normalShadingSmooth = shadowParameters.normalShadingSmooth;
|
||||
float darkness = shadowParameters.darkness;
|
||||
vec3 uvw = shadowParameters.texCoords;
|
||||
depth -= depthBias;
|
||||
float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);
|
||||
return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);
|
||||
}
|
||||
|
||||
vec4 getPositionEC(){
|
||||
return czm_windowToEyeCoordinates(gl_FragCoord);
|
||||
}
|
||||
|
||||
vec3 getNormalEC(){
|
||||
return vec3(1.);
|
||||
}
|
||||
|
||||
vec4 toEye(in vec2 uv,in float depth){
|
||||
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
|
||||
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
|
||||
posInCamera=posInCamera/posInCamera.w;
|
||||
return posInCamera;
|
||||
}
|
||||
|
||||
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
|
||||
vec3 v01=point-planeOrigin;
|
||||
float d=dot(planeNormal,v01);
|
||||
return(point-planeNormal*d);
|
||||
}
|
||||
|
||||
float getDepth(in vec4 depth){
|
||||
float z_window=czm_unpackDepth(depth);
|
||||
z_window=czm_reverseLogDepth(z_window);
|
||||
float n_range=czm_depthRange.near;
|
||||
float f_range=czm_depthRange.far;
|
||||
return(2.*z_window-n_range-f_range)/(f_range-n_range);
|
||||
}
|
||||
|
||||
float shadow(in vec4 positionEC){
|
||||
vec3 normalEC=getNormalEC();
|
||||
zx_shadowParameters shadowParameters;
|
||||
shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
|
||||
shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
|
||||
shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
|
||||
shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
|
||||
vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;
|
||||
float distance=length(directionEC);
|
||||
directionEC=normalize(directionEC);
|
||||
float radius=shadowMap_lightPositionEC.w;
|
||||
if(distance>radius)
|
||||
{
|
||||
return 2.0;
|
||||
}
|
||||
vec3 directionWC=czm_inverseViewRotation*directionEC;
|
||||
shadowParameters.depth=distance/radius-0.0003;
|
||||
shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.,1.);
|
||||
shadowParameters.texCoords=directionWC;
|
||||
float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);
|
||||
return visibility;
|
||||
}
|
||||
|
||||
bool visible(in vec4 result)
|
||||
{
|
||||
result.x/=result.w;
|
||||
result.y/=result.w;
|
||||
result.z/=result.w;
|
||||
return result.x>=-1.&&result.x<=1.
|
||||
&&result.y>=-1.&&result.y<=1.
|
||||
&&result.z>=-1.&&result.z<=1.;
|
||||
}
|
||||
|
||||
void main(){
|
||||
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
|
||||
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
|
||||
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
|
||||
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
|
||||
// 视角 = (纹理坐标, 深度)
|
||||
vec4 viewPos = toEye(v_textureCoordinates, depth);
|
||||
// 世界坐标
|
||||
vec4 wordPos = czm_inverseView * viewPos;
|
||||
// 虚拟相机中坐标
|
||||
vec4 vcPos = camera_view_matrix * wordPos;
|
||||
float near = .001 * helsing_viewDistance;
|
||||
float dis = length(vcPos.xyz);
|
||||
if(dis > near && dis < helsing_viewDistance){
|
||||
// 透视投影
|
||||
vec4 posInEye = camera_projection_matrix * vcPos;
|
||||
// 可视区颜色
|
||||
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
|
||||
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
|
||||
if(visible(posInEye)){
|
||||
float vis = shadow(viewPos);
|
||||
if(vis > 0.3){
|
||||
// gl_FragColor = mix(gl_FragColor,helsing_visibleAreaColor,.5);
|
||||
} else {
|
||||
gl_FragColor = mix(gl_FragColor,helsing_invisibleAreaColor,.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
380
src/Obj/Analysis/CircleViewShed/index.js
Normal file
380
src/Obj/Analysis/CircleViewShed/index.js
Normal file
@ -0,0 +1,380 @@
|
||||
// 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
|
||||
213
src/Obj/Analysis/Contour/index.js
Normal file
213
src/Obj/Analysis/Contour/index.js
Normal file
@ -0,0 +1,213 @@
|
||||
|
||||
|
||||
import Tools from '../../../Tools';
|
||||
class ContourAnalysis {
|
||||
/**
|
||||
* @constructor 等高线分析
|
||||
* @param sdk
|
||||
* **/
|
||||
constructor(sdk, options = {}) {
|
||||
this.viewer = sdk.viewer
|
||||
let terrainAvailability = this.viewer.terrainProvider.availability;
|
||||
if (!terrainAvailability) {
|
||||
this.error = '未加载地形数据!'
|
||||
window.ELEMENT && window.ELEMENT.Message({
|
||||
message: '未加载地形数据!',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
console.warn(this.error)
|
||||
return
|
||||
}
|
||||
this.positions = options.positions
|
||||
this.interfaceNum = options.interfaceNum || 25 //内插时均分的数量,即沿着边界长或宽均分成n分进行插点,默认值25
|
||||
this.colorFill = options.colorFill || [
|
||||
"#8CEA00",
|
||||
"#B7FF4A",
|
||||
"#FFFF37",
|
||||
"#FFE66F",
|
||||
"#FFD1A4",
|
||||
"#FFCBB3",
|
||||
"#FFBD9D",
|
||||
"#FFAD86",
|
||||
"#FF9D6F",
|
||||
"#FF8F59",
|
||||
"#FF8040",
|
||||
"#FF5809",
|
||||
"#F75000",
|
||||
"#D94600",
|
||||
"#BB3D00",
|
||||
"#A23400",
|
||||
"#842B00",
|
||||
"#642100",
|
||||
"#4D0000",
|
||||
"#2F0000",
|
||||
]; //等高线赋值颜色,内含default值
|
||||
this.countorLineList = Cesium.defaultValue(options.countorLineList, []);
|
||||
YJ.Analysis.AnalysesResults.push(this)
|
||||
this.createNewLine();
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'ContourAnalysis'
|
||||
}
|
||||
|
||||
createNewLine() {
|
||||
ContourAnalysis.interpolatePoint(this);
|
||||
}
|
||||
|
||||
//利用turf在box内进行插点
|
||||
static interpolatePoint(that) {
|
||||
let curPoints = that.positions
|
||||
let features = [];
|
||||
const boundaryCoord = {
|
||||
minX: 360,
|
||||
maxX: -360,
|
||||
minY: 180,
|
||||
maxY: -180,
|
||||
}; //绘制几何图形的外围矩形box
|
||||
for (let index = 0; index < curPoints.length; index++) {
|
||||
const element = Cesium.Cartesian3.fromDegrees(curPoints[index].lng, curPoints[index].lat, curPoints[index].alt);
|
||||
let ellipsoid = that.viewer.scene.globe.ellipsoid;
|
||||
let cartographic = ellipsoid.cartesianToCartographic(element);
|
||||
let lat = Cesium.Math.toDegrees(cartographic.latitude);
|
||||
let lng = Cesium.Math.toDegrees(cartographic.longitude);
|
||||
boundaryCoord.maxY = Math.max(lat, boundaryCoord.maxY);
|
||||
boundaryCoord.minY = Math.min(lat, boundaryCoord.minY);
|
||||
boundaryCoord.maxX = Math.max(lng, boundaryCoord.maxX);
|
||||
boundaryCoord.minX = Math.min(lng, boundaryCoord.minX);
|
||||
|
||||
let curFeature = {
|
||||
type: "Feature",
|
||||
properties: {},
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [lng, lat],
|
||||
},
|
||||
};
|
||||
features.push(curFeature);
|
||||
}
|
||||
let boundaryJson = {
|
||||
type: "FeatureCollection",
|
||||
features: features,
|
||||
};
|
||||
turf.featureEach(boundaryJson, function (point) {
|
||||
point.properties.height = 0;
|
||||
});
|
||||
let options = {
|
||||
gridType: "points",
|
||||
property: "height",
|
||||
units: "kilometers",
|
||||
};
|
||||
let from = turf.point([boundaryCoord.minX, boundaryCoord.minY]);
|
||||
let to = turf.point([boundaryCoord.maxX, boundaryCoord.maxY]);
|
||||
let diagonalDistance = turf.rhumbDistance(from, to, {
|
||||
units: "kilometers",
|
||||
});
|
||||
let grid = turf.interpolate(
|
||||
boundaryJson,
|
||||
diagonalDistance / that.interfaceNum,
|
||||
options
|
||||
);
|
||||
let minHeight = 10000000; //最低点高程值
|
||||
let maxHeight = -100000000; //最高点高程值
|
||||
turf.featureEach(grid, function (point) {
|
||||
let pos = point.geometry.coordinates;
|
||||
let cartographic = Cesium.Cartographic.fromDegrees(pos[0], pos[1]);
|
||||
let height = that.viewer.scene.globe.getHeight(cartographic);
|
||||
maxHeight = Math.max(height, maxHeight);
|
||||
minHeight = Math.min(height, minHeight);
|
||||
point.properties.height = height;
|
||||
});
|
||||
let breaks = [];
|
||||
let stepCount = that.colorFill.length - 1;
|
||||
let step = (maxHeight - minHeight) / stepCount;
|
||||
for (let index = 0; index < stepCount + 1; index++) {
|
||||
breaks.push(Math.ceil(minHeight + step * index));
|
||||
}
|
||||
// console.log('grid', grid)
|
||||
let linesJson = turf.isolines(grid, breaks, { zProperty: "height" });
|
||||
let _countorLine = Cesium.GeoJsonDataSource.load(linesJson, {
|
||||
clampToGround: true,
|
||||
});
|
||||
// console.log(linesJson)
|
||||
_countorLine.then(function (dataSource) {
|
||||
console.log(dataSource)
|
||||
that.countorLine = dataSource; //最终计算生成的等高线对象,GeoJsonDataSource
|
||||
that.countorLineList.push(dataSource); //等高线数组
|
||||
that.viewer.dataSources.add(dataSource);
|
||||
let entities = dataSource.entities.values;
|
||||
for (let index = 0; index < entities.length; index++) {
|
||||
const element = entities[index];
|
||||
let center = getPolylineCenter(element.polyline);
|
||||
element.position = center;
|
||||
// dataSource.entities.add(new Cesium.Entity({
|
||||
// position: center,
|
||||
// label: {
|
||||
// text: element.properties.height._value + '',
|
||||
// font: '20px Microsoft YaHei',
|
||||
// fillColor: Cesium.Color.fromCssColorString('#f1d20c'),
|
||||
// style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
||||
// disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
|
||||
// },
|
||||
// }))
|
||||
// element.label = new Cesium.LabelGraphics({
|
||||
|
||||
// })
|
||||
let cur_index = that.getObjectIndex(
|
||||
breaks,
|
||||
element.properties.height._value
|
||||
);
|
||||
if (cur_index) {
|
||||
element.polyline.material = Cesium.Color.fromCssColorString(
|
||||
that.colorFill[cur_index - 1]
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function getPolylineCenter(polyline) {
|
||||
let tools = new Tools()
|
||||
let positions = polyline.positions;
|
||||
let length = positions._value.length;
|
||||
let array = []
|
||||
for (let i = 0; i < length; i++) {
|
||||
let pos = tools.cartesian3Towgs84(positions._value[i], that.viewer)
|
||||
array.push([pos.lng, pos.lat])
|
||||
}
|
||||
let line = turf.lineString(array);
|
||||
let distance = turf.length(line, { units: "kilometers" });
|
||||
let along = turf.along(line, distance/2, { units: "kilometers" });
|
||||
return Cesium.Cartesian3.fromDegrees(along.geometry.coordinates[0], along.geometry.coordinates[1], 0);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 返回随机插入的数在数组中的位置
|
||||
* @param {*} arr 元数组
|
||||
* @param {*} num 随机数
|
||||
* @returns 序号
|
||||
* @example getObjectIndex([0,218,325,333,444],354)=>4;
|
||||
*/
|
||||
getObjectIndex(arr, num) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i] > num) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
clear(countorLine) {
|
||||
if (countorLine) {
|
||||
this.viewer.dataSources.remove(countorLine);
|
||||
let index = this.countorLineList.indexOf(countorLine);
|
||||
this.countorLineList.splice(index, 1);
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
this.countorLineList.forEach((element) => {
|
||||
this.viewer.dataSources.remove(element);
|
||||
});
|
||||
this.countorLineList = [];
|
||||
}
|
||||
}
|
||||
export default ContourAnalysis;
|
||||
125
src/Obj/Analysis/CutFill/CreatePolygon.js
Normal file
125
src/Obj/Analysis/CutFill/CreatePolygon.js
Normal file
@ -0,0 +1,125 @@
|
||||
class CreatePolygon {
|
||||
constructor(viewer) {
|
||||
if (!viewer) throw new Error("no viewer object!");
|
||||
this.activePoints = [];
|
||||
this.viewer = viewer;
|
||||
this.handler = undefined;
|
||||
this.init();
|
||||
}
|
||||
init() {
|
||||
this.activeShapePoints = [];
|
||||
this.floatingPoint = undefined;
|
||||
this.activeShape = undefined;
|
||||
this.activePoints.forEach((element) => {
|
||||
this.viewer.entities.remove(element);
|
||||
});
|
||||
this.activePoints = [];
|
||||
this.initHandler();
|
||||
}
|
||||
start(callback) {
|
||||
const $this = this;
|
||||
$this.keyDownStatus(true);
|
||||
$this.init();
|
||||
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
|
||||
$this.handler.setInputAction(function (event) {
|
||||
let earthPosition = $this.viewer.scene.pickPosition(event.position);
|
||||
if (Cesium.defined(earthPosition)) {
|
||||
if ($this.activeShapePoints.length === 0) {
|
||||
$this.floatingPoint = $this.createPoint(earthPosition);
|
||||
$this.activeShapePoints.push(earthPosition);
|
||||
let dynamicPositions = new Cesium.CallbackProperty(function () {
|
||||
return new Cesium.PolygonHierarchy($this.activeShapePoints);
|
||||
}, false);
|
||||
$this.activeShape = $this.drawShape(dynamicPositions); //绘制动态图
|
||||
}
|
||||
$this.activeShapePoints.push(earthPosition);
|
||||
$this.createPoint(earthPosition);
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
||||
$this.handler.setInputAction(function (event) {
|
||||
if (Cesium.defined($this.floatingPoint)) {
|
||||
let newPosition = $this.viewer.scene.pickPosition(event.endPosition);
|
||||
if (Cesium.defined(newPosition)) {
|
||||
$this.floatingPoint.position.setValue(newPosition);
|
||||
$this.activeShapePoints.pop();
|
||||
$this.activeShapePoints.push(newPosition);
|
||||
}
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
||||
$this.handler.setInputAction(function () {
|
||||
$this.activeShapePoints.pop(); //去除最后一个动态点
|
||||
if ($this.activeShapePoints.length) {
|
||||
$this.polygon = $this.drawShape($this.activeShapePoints); //绘制最终图
|
||||
}
|
||||
$this.viewer.entities.remove($this.floatingPoint); //去除动态点图形(当前鼠标点)
|
||||
$this.viewer.entities.remove($this.activeShape); //去除动态图形
|
||||
$this.activePoints.forEach((element) => {
|
||||
$this.viewer.entities.remove(element);
|
||||
});
|
||||
$this.handler.destroy();
|
||||
setTimeout(() => {
|
||||
if (typeof callback == "function") callback();
|
||||
}, 1000);
|
||||
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
|
||||
}
|
||||
initHandler() {
|
||||
if (this.handler) {
|
||||
this.handler.destroy();
|
||||
this.handler = undefined;
|
||||
}
|
||||
}
|
||||
createPoint(worldPosition) {
|
||||
let point = this.viewer.entities.add({
|
||||
position: worldPosition,
|
||||
point: {
|
||||
color: Cesium.Color.SKYBLUE,
|
||||
pixelSize: 5,
|
||||
},
|
||||
});
|
||||
this.activePoints.push(point);
|
||||
return point;
|
||||
}
|
||||
drawShape(positionData) {
|
||||
let shape = this.viewer.entities.add({
|
||||
polygon: {
|
||||
hierarchy: positionData,
|
||||
material: new Cesium.ColorMaterialProperty(
|
||||
Cesium.Color.BLUE.withAlpha(0.4)
|
||||
),
|
||||
zIndex: 99999999
|
||||
},
|
||||
});
|
||||
return shape;
|
||||
}
|
||||
//快捷键//Ctrl + Z
|
||||
keyDownStatus(bool) {
|
||||
const $this = this;
|
||||
document.onkeydown = function (event) {
|
||||
if (event.ctrlKey && window.event.keyCode == 90) {
|
||||
if (!bool) {
|
||||
return false;
|
||||
}
|
||||
$this.activeShapePoints.pop();
|
||||
$this.viewer.entities.remove(
|
||||
$this.activePoints[$this.activePoints.length - 1]
|
||||
);
|
||||
$this.activePoints.pop();
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Cesium中世界坐标系(笛卡尔)转经纬度
|
||||
* @param {*} cartesian3
|
||||
* @returns 经纬度
|
||||
*/
|
||||
Cartesian3ToDgrees(cartesian3) {
|
||||
let cartographic =
|
||||
window.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian3);
|
||||
let lat = Cesium.Math.toDegrees(cartographic.latitude);
|
||||
let lng = Cesium.Math.toDegrees(cartographic.longitude);
|
||||
let alt = cartographic.height;
|
||||
return { lng: lng, lat: lat, alt: alt };
|
||||
}
|
||||
}
|
||||
|
||||
export default CreatePolygon;
|
||||
75
src/Obj/Analysis/CutFill/_element.js
Normal file
75
src/Obj/Analysis/CutFill/_element.js
Normal file
@ -0,0 +1,75 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 70px;">绘制分析区域</span>
|
||||
<button class="draw-btn"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>开始绘制</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">基准高度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="-999999" max="999999" name="height">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">精度</span>
|
||||
<div class="input-number input-number-unit">
|
||||
<input class="input" type="number" title="" min="1" max="1250" name="precision">
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 74px;">总分析面积:</span>
|
||||
<span class="text-number" name="allArea">0</span>
|
||||
<span class="unit text-number">m²</span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 90px;">无须填挖面积:</span>
|
||||
<span class="text-number" name="noArea">0</span>
|
||||
<span class="unit text-number">m²</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 74px;">填方面积:</span>
|
||||
<span class="text-number" name="fillArea">0</span>
|
||||
<span class="unit text-number">m²</span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 90px;">挖方面积:</span>
|
||||
<span class="text-number" name="cutArea">0</span>
|
||||
<span class="unit text-number">m²</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 74px;">填方体积:</span>
|
||||
<span class="text-number" name="fillVolume">0</span>
|
||||
<span class="unit text-number">m³</span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 90px;">挖方体积:</span>
|
||||
<span class="text-number" name="cutVolume">0</span>
|
||||
<span class="unit text-number">m³</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
354
src/Obj/Analysis/CutFill/index.js
Normal file
354
src/Obj/Analysis/CutFill/index.js
Normal file
@ -0,0 +1,354 @@
|
||||
import Dialog from '../../../BaseDialog';
|
||||
import { html } from "./_element";
|
||||
// import CreatePolygon from "./CreatePolygon";
|
||||
import DrawPolygon from "../../../Draw/drawPolygon"
|
||||
|
||||
class CutFillAnalysis {
|
||||
/**
|
||||
* @constructor 填挖方分析
|
||||
* @param sdk
|
||||
* **/
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
this.sdk = sdk;
|
||||
this.viewer = sdk.viewer;
|
||||
// if (!positions) throw new Error("no positions object!");
|
||||
// this.positions = positions;
|
||||
this.height = options.height || 70
|
||||
this.maxHeigh = -1000000;
|
||||
this.precision = options.precision || 125
|
||||
this.Dialog = _Dialog
|
||||
this.result = {
|
||||
allArea: "",
|
||||
cutArea: "",
|
||||
cutVolume: "",
|
||||
fillArea: "",
|
||||
fillVolume: "",
|
||||
noArea: "",
|
||||
}
|
||||
this.entities = []
|
||||
this.Draw = new DrawPolygon(this.sdk)
|
||||
YJ.Analysis.AnalysesResults.push(this)
|
||||
CutFillAnalysis.EditBox(this)
|
||||
}
|
||||
|
||||
create() {
|
||||
this.clean()
|
||||
this.Draw.start((a, positions) => {
|
||||
if(!positions || positions.length<3) {
|
||||
let _error = '最少需要三个坐标!'
|
||||
console.warn(_error)
|
||||
window.ELEMENT && window.ELEMENT.Message({
|
||||
message: _error,
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
return
|
||||
}
|
||||
let fromDegreesArray = []
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
fromDegreesArray.push(positions[i].lng, positions[i].lat, positions[i].alt)
|
||||
}
|
||||
this.positions = Cesium.Cartesian3.fromDegreesArrayHeights(fromDegreesArray)
|
||||
this.createPolygonGeo(this.positions);
|
||||
this.result = this.VolumeAnalysis();
|
||||
this.viewer.scene.screenSpaceCameraController.enableCollisionDetection = false; //允许相机进入地下
|
||||
})
|
||||
// const $this = this;
|
||||
// if (!this.cp) {
|
||||
// this.cp = new CreatePolygon(this.viewer)
|
||||
// }
|
||||
// this.cp.start(function () {
|
||||
// console.log($this.cp.activeShapePoints)
|
||||
// $this.positions = $this.cp.activeShapePoints;
|
||||
// $this.createPolygonGeo($this.positions);
|
||||
// $this.result = $this.VolumeAnalysis();
|
||||
// $this.viewer.entities.remove($this.cp.polygon);
|
||||
// $this.viewer.scene.screenSpaceCameraController.enableCollisionDetection = false; //允许相机进入地下
|
||||
|
||||
// });
|
||||
}
|
||||
createPolygonGeo(points) {
|
||||
//计算网格粒度-精度
|
||||
let granularity = Math.PI / Math.pow(2, 11);
|
||||
granularity = granularity / this.precision;
|
||||
let polygonGeometry = new Cesium.PolygonGeometry.fromPositions({
|
||||
positions: points,
|
||||
vertexFormat: Cesium.PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
|
||||
granularity: granularity,
|
||||
});
|
||||
//创建自定义平面几何体
|
||||
this.geom = new Cesium.PolygonGeometry.createGeometry(polygonGeometry);
|
||||
}
|
||||
VolumeAnalysis() {
|
||||
let cutArea = 0,
|
||||
cutVolume = 0,
|
||||
fillArea = 0,
|
||||
fillVolume = 0,
|
||||
noArea = 0;
|
||||
const indices = this.geom.indices; //获取顶点索引数据
|
||||
if (!this.geom || !this.geom.attributes || !this.geom.attributes.position) {
|
||||
return;
|
||||
}
|
||||
const positions = this.geom.attributes.position.values;
|
||||
for (let index = 0; index < indices.length; index += 3) {
|
||||
const pos0 = this.returnPosition(positions, indices[index]);
|
||||
const pos1 = this.returnPosition(positions, indices[index + 1]);
|
||||
const pos2 = this.returnPosition(positions, indices[index + 2]);
|
||||
let entity = this.viewer.entities.add({
|
||||
name: "三角面",
|
||||
polygon: {
|
||||
hierarchy: [pos0.heightPos, pos1.heightPos, pos2.heightPos],
|
||||
perPositionHeight: true,
|
||||
material: Cesium.Color.fromRandom(),
|
||||
extrudedHeight: this.height,
|
||||
outline: true,
|
||||
outlineColor: Cesium.Color.BLACK,
|
||||
},
|
||||
});
|
||||
this.entities.push(entity)
|
||||
//水平状态下三角形面积
|
||||
const area = this.computeArea4Triangle(
|
||||
pos0.noHeightPos,
|
||||
pos1.noHeightPos,
|
||||
pos2.noHeightPos
|
||||
);
|
||||
//计算三个点的均高
|
||||
const height = (pos0.height + pos1.height + pos2.height) / 3;
|
||||
if (height < this.height) {
|
||||
// 需要填方的部分
|
||||
fillArea += area;
|
||||
const volume = area * (this.height - height);
|
||||
fillVolume += volume;
|
||||
} else if (height == this.height) {
|
||||
noArea += area;
|
||||
} else {
|
||||
// 需要挖方的部分
|
||||
cutArea += area;
|
||||
const volume = area * (height - this.height);
|
||||
cutVolume += volume;
|
||||
}
|
||||
}
|
||||
const allArea = cutArea + fillArea + noArea;
|
||||
// this.result = {
|
||||
// allArea,
|
||||
// cutArea,
|
||||
// cutVolume,
|
||||
// fillArea,
|
||||
// fillVolume,
|
||||
// noArea,
|
||||
// };
|
||||
this.result.allArea = allArea
|
||||
this.result.cutArea = cutArea
|
||||
this.result.cutVolume = cutVolume
|
||||
this.result.fillArea = fillArea
|
||||
this.result.fillVolume = fillVolume
|
||||
this.result.noArea = noArea
|
||||
return this.result;
|
||||
}
|
||||
computeCentroid4Polygon(positions) {
|
||||
let x = [],
|
||||
y = [];
|
||||
let allX = 0,
|
||||
allY = 0;
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
let cartographic = Cesium.Cartographic.fromCartesian(positions[i]);
|
||||
allX += cartographic.longitude;
|
||||
allY += cartographic.latitude;
|
||||
x.push(cartographic.longitude);
|
||||
y.push(cartographic.latitude);
|
||||
}
|
||||
let centroidx = allX / positions.length;
|
||||
let centroidy = allY / positions.length;
|
||||
const Cartographic = new Cesium.Cartographic(centroidx, centroidy);
|
||||
return Cesium.Cartesian3.fromRadians(
|
||||
Cartographic.longitude,
|
||||
Cartographic.latitude,
|
||||
this.maxHeigh + 30
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 海伦公式求取三角形面积
|
||||
* @param {*} pos1
|
||||
* @param {*} pos2
|
||||
* @param {*} pos3
|
||||
* @returns 三角形面积㎡
|
||||
*/
|
||||
computeArea4Triangle(pos1, pos2, pos3) {
|
||||
let a = Cesium.Cartesian3.distance(pos1, pos2);
|
||||
let b = Cesium.Cartesian3.distance(pos2, pos3);
|
||||
let c = Cesium.Cartesian3.distance(pos3, pos1);
|
||||
let S = (a + b + c) / 2;
|
||||
return Math.sqrt(S * (S - a) * (S - b) * (S - c));
|
||||
}
|
||||
returnPosition(positions, index) {
|
||||
let cartesian = new Cesium.Cartesian3(
|
||||
positions[index * 3],
|
||||
positions[index * 3 + 1],
|
||||
positions[index * 3 + 2]
|
||||
);
|
||||
let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
||||
let height = this.viewer.scene.sampleHeightSupported
|
||||
? this.viewer.scene.sampleHeight(cartographic)
|
||||
: this.viewer.scene.globe.getHeight(cartographic);
|
||||
if (height > this.maxHeigh) {
|
||||
this.maxHeigh = height;
|
||||
}
|
||||
return {
|
||||
heightPos: Cesium.Cartesian3.fromRadians(
|
||||
cartographic.longitude,
|
||||
cartographic.latitude,
|
||||
height
|
||||
),
|
||||
noHeightPos: Cesium.Cartesian3.fromRadians(
|
||||
cartographic.longitude,
|
||||
cartographic.latitude,
|
||||
0
|
||||
),
|
||||
height: height,
|
||||
};
|
||||
}
|
||||
|
||||
static async EditBox(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.clean()
|
||||
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
|
||||
},
|
||||
})
|
||||
await that._DialogObject.init()
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
that._DialogObject.contentAppChild(contentElm)
|
||||
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' cut-fill'
|
||||
|
||||
// 高度值
|
||||
let e_height = contentElm.querySelector("input[name='height']")
|
||||
e_height.value = that.height
|
||||
e_height.addEventListener('blur', (e) => {
|
||||
let value = e.target.value
|
||||
if (e.data != '.' && (e.data != '-' || e.target.value)) {
|
||||
value = Number(value)
|
||||
if ((e.target.max) && value > Number(e.target.max)) {
|
||||
value = Number(e.target.max)
|
||||
}
|
||||
if ((e.target.min) && value < Number(e.target.min)) {
|
||||
value = Number(e.target.min)
|
||||
}
|
||||
e_height.value = value
|
||||
that.height = e_height.value;
|
||||
}
|
||||
});
|
||||
|
||||
// 精度值
|
||||
let e_precision = contentElm.querySelector("input[name='precision']")
|
||||
e_precision.value = that.precision
|
||||
e_precision.addEventListener('blur', (e) => {
|
||||
let value = Number(e.target.value)
|
||||
if ((e.target.max) && value > Number(e.target.max)) {
|
||||
value = Number(e.target.max)
|
||||
}
|
||||
if ((e.target.min) && value < Number(e.target.min)) {
|
||||
value = Number(e.target.min)
|
||||
}
|
||||
e_precision.value = value
|
||||
that.precision = e_precision.value;
|
||||
});
|
||||
|
||||
// 总分析面积
|
||||
let e_allArea = contentElm.querySelector("span[name='allArea']")
|
||||
e_allArea.innerHTML = that.result.allArea || 0
|
||||
Object.defineProperty(that.result, 'allArea', {
|
||||
get() {
|
||||
return e_allArea.innerHTML
|
||||
},
|
||||
set(value) {
|
||||
e_allArea.innerHTML = Number(value.toFixed(4))
|
||||
}
|
||||
})
|
||||
// 填方面积
|
||||
let e_fillArea = contentElm.querySelector("span[name='fillArea']")
|
||||
e_fillArea.innerHTML = that.result.fillArea || 0
|
||||
Object.defineProperty(that.result, 'fillArea', {
|
||||
get() {
|
||||
return e_fillArea.innerHTML
|
||||
},
|
||||
set(value) {
|
||||
e_fillArea.innerHTML = Number(value.toFixed(4))
|
||||
}
|
||||
})
|
||||
|
||||
// 填方体积
|
||||
let e_fillVolume = contentElm.querySelector("span[name='fillVolume']")
|
||||
e_fillVolume.innerHTML = that.result.fillVolume || 0
|
||||
Object.defineProperty(that.result, 'fillVolume', {
|
||||
get() {
|
||||
return e_fillVolume.innerHTML
|
||||
},
|
||||
set(value) {
|
||||
e_fillVolume.innerHTML = Number(value.toFixed(4))
|
||||
}
|
||||
})
|
||||
|
||||
// 挖方面积
|
||||
let e_cutArea = contentElm.querySelector("span[name='cutArea']")
|
||||
e_cutArea.innerHTML = that.result.cutArea || 0
|
||||
Object.defineProperty(that.result, 'cutArea', {
|
||||
get() {
|
||||
return e_cutArea.innerHTML
|
||||
},
|
||||
set(value) {
|
||||
e_cutArea.innerHTML = Number(value.toFixed(4))
|
||||
}
|
||||
})
|
||||
|
||||
// 挖方体积
|
||||
let e_cutVolume = contentElm.querySelector("span[name='cutVolume']")
|
||||
e_cutVolume.innerHTML = that.result.cutVolume || 0
|
||||
Object.defineProperty(that.result, 'cutVolume', {
|
||||
get() {
|
||||
return e_cutVolume.innerHTML
|
||||
},
|
||||
set(value) {
|
||||
e_cutVolume.innerHTML = Number(value.toFixed(4))
|
||||
}
|
||||
})
|
||||
|
||||
// 无须填挖面积
|
||||
let e_noArea = contentElm.querySelector("span[name='noArea']")
|
||||
e_noArea.innerHTML = that.result.noArea || 0
|
||||
Object.defineProperty(that.result, 'noArea', {
|
||||
get() {
|
||||
return e_noArea.innerHTML
|
||||
},
|
||||
set(value) {
|
||||
e_noArea.innerHTML = Number(value.toFixed(4))
|
||||
}
|
||||
})
|
||||
|
||||
let newDivBtn = contentElm.getElementsByClassName('draw-btn')[0];
|
||||
newDivBtn.addEventListener('click', () => {
|
||||
that.create()
|
||||
});
|
||||
}
|
||||
|
||||
clean() {
|
||||
this.Draw && this.Draw.end()
|
||||
for (let i = 0; i < this.entities.length; i++) {
|
||||
this.viewer.entities.remove(this.entities[i])
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.clean()
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
}
|
||||
}
|
||||
export default CutFillAnalysis;
|
||||
483
src/Obj/Analysis/Flat/index.js
Normal file
483
src/Obj/Analysis/Flat/index.js
Normal file
@ -0,0 +1,483 @@
|
||||
import Base from "../../Base/index";
|
||||
import Dialog from '../../../BaseDialog'
|
||||
import { setActiveViewer, closeRotateAround, closeViewFollow} from '../../../Global/global'
|
||||
|
||||
let FlatList = {}
|
||||
class Flat extends Base {
|
||||
/**
|
||||
* @constructor
|
||||
* @description 模型压平
|
||||
* @param sdk
|
||||
* @param {Cesium.Cesium3DTileset} tileset 三维模型
|
||||
* @param {Object} options
|
||||
* @param {string} attr.id id
|
||||
* @param {Cesium.Cartesian3[]} attr.positions 压平面坐标
|
||||
*/
|
||||
constructor(sdk, tileset, options = {}, _Dialog = {}) {
|
||||
super(sdk)
|
||||
if (!tileset || !this.sdk || !this.sdk.viewer) return;
|
||||
this.options = { ...options }
|
||||
this.options.id = options.id || this.randomString()
|
||||
this.options.name = options.name || '压平面'
|
||||
this.options.positions = options.positions || []
|
||||
this.options.show = (options.show || options.show === false) ? options.show : true
|
||||
this.tileset = tileset;
|
||||
this.Dialog = _Dialog
|
||||
|
||||
if (!this.options.height && this.options.height !== 0) {
|
||||
let height = this.options.positions[0].alt
|
||||
for (let i = 0; i < this.options.positions.length; i++) {
|
||||
if (height > this.options.positions[i].alt) {
|
||||
height = this.options.positions[i].alt
|
||||
}
|
||||
}
|
||||
this.options.height = height
|
||||
}
|
||||
|
||||
if (FlatList[this.tileset.id]) {
|
||||
FlatList[this.tileset.id].push({ ...this.options })
|
||||
}
|
||||
else {
|
||||
FlatList[this.tileset.id] = [{ ...this.options }]
|
||||
}
|
||||
|
||||
this.center = tileset.boundingSphere.center.clone();
|
||||
this.center84 = this.cartesian3Towgs84(this.center, this.sdk.viewer)
|
||||
this.matrix = Cesium.Transforms.eastNorthUpToFixedFrame(this.center.clone());
|
||||
this.localMatrix = Cesium.Matrix4.inverse(this.matrix, new Cesium.Matrix4());
|
||||
// this.entity = {
|
||||
// id: this.options.id
|
||||
// }
|
||||
this.addFlat()
|
||||
// Flat.createPolygon(this)
|
||||
}
|
||||
|
||||
get show() {
|
||||
return this.options.show
|
||||
}
|
||||
|
||||
set show(v) {
|
||||
this.options.show = v
|
||||
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
|
||||
if (FlatList[this.tileset.id][i].id == this.options.id) {
|
||||
FlatList[this.tileset.id][i].show = v
|
||||
}
|
||||
}
|
||||
this.addFlat()
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this.options.height
|
||||
}
|
||||
|
||||
set height(v) {
|
||||
this.options.height = Number(v)
|
||||
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
|
||||
if (FlatList[this.tileset.id][i].id == this.options.id) {
|
||||
FlatList[this.tileset.id][i].height = Number(v)
|
||||
}
|
||||
}
|
||||
this.addFlat()
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.options.name
|
||||
}
|
||||
|
||||
set name(v) {
|
||||
this.options.name = v
|
||||
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
|
||||
if (FlatList[this.tileset.id][i].id == this.options.id) {
|
||||
FlatList[this.tileset.id][i].name = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addFlat() {
|
||||
let localPositionsArr = []
|
||||
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
|
||||
let item = FlatList[this.tileset.id][i];
|
||||
if (item.show) {
|
||||
const positions = item.positions;
|
||||
let height = item.height
|
||||
let fromDegreesArray = []
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
fromDegreesArray.push(positions[i].lng, positions[i].lat)
|
||||
}
|
||||
FlatList[this.tileset.id][i].flatHeight = height - this.center84.alt
|
||||
let localCoor = this.cartesiansToLocal(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray));
|
||||
localPositionsArr.push(localCoor);
|
||||
}
|
||||
}
|
||||
|
||||
const funstr = this.getIsinPolygonFun(localPositionsArr);
|
||||
let str = ``;
|
||||
for (let i = 0; i < localPositionsArr.length; i++) {
|
||||
const coors = localPositionsArr[i];
|
||||
const n = coors.length;
|
||||
let instr = ``;
|
||||
coors.forEach((coordinate, index) => {
|
||||
instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
|
||||
})
|
||||
str += `
|
||||
${instr}
|
||||
if(isPointInPolygon_${n}(position2D)){
|
||||
vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z + ${FlatList[this.tileset.id][i].flatHeight}, 1.0);
|
||||
vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
|
||||
|
||||
vsOutput.positionMC.xy = model_local_position_transformed.xy;
|
||||
vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
|
||||
return;
|
||||
}`;
|
||||
|
||||
}
|
||||
|
||||
this.updateShader(funstr, str);
|
||||
}
|
||||
|
||||
// static createPolygon(that) {
|
||||
// let color = '#ffffff'
|
||||
// let linecolor = '#000000'
|
||||
// let positions = that.options.positions
|
||||
// let fromDegreesArray = []
|
||||
// for (let i = 0; i < positions.length; i++) {
|
||||
// fromDegreesArray.push(positions[i].lng, positions[i].lat, that.options.height)
|
||||
// }
|
||||
// that.positions = Cesium.Cartesian3.fromDegreesArrayHeights(fromDegreesArray)
|
||||
// that.entity = that.sdk.viewer.entities.add({
|
||||
// show: that.options.show,
|
||||
// id: that.options.id,
|
||||
// polyline: {
|
||||
// positions: [...that.positions, that.positions[0], that.positions[1]],
|
||||
// width: 2,
|
||||
// material: Cesium.Color.fromCssColorString(linecolor),
|
||||
// depthFailMaterial: new Cesium.PolylineDashMaterialProperty({
|
||||
// color: Cesium.Color.YELLOW
|
||||
// }),
|
||||
// clampToGround: false,
|
||||
// zIndex: that.sdk._entityZIndex
|
||||
// },
|
||||
// })
|
||||
// that.sdk._entityZIndex++
|
||||
// }
|
||||
|
||||
remove() {
|
||||
FlatList[this.tileset.id] = FlatList[this.tileset.id].filter((attr) => {
|
||||
return attr.id != this.options.id;
|
||||
})
|
||||
|
||||
let localPositionsArr = [];
|
||||
for (let i = 0; i < FlatList[this.tileset.id].length; i++) {
|
||||
let item = FlatList[this.tileset.id][i];
|
||||
if (item.show) {
|
||||
const positions = item.positions;
|
||||
let height = item.height
|
||||
let fromDegreesArray = []
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
fromDegreesArray.push(positions[i].lng, positions[i].lat)
|
||||
}
|
||||
FlatList[this.tileset.id][i].flatHeight = height - this.center84.alt
|
||||
let localCoor = this.cartesiansToLocal(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray));
|
||||
localPositionsArr.push(localCoor);
|
||||
}
|
||||
}
|
||||
|
||||
const funstr = this.getIsinPolygonFun(localPositionsArr);
|
||||
let str = ``;
|
||||
for (let i = 0; i < localPositionsArr.length; i++) {
|
||||
const coors = localPositionsArr[i];
|
||||
const n = coors.length;
|
||||
let instr = ``;
|
||||
coors.forEach((coordinate, index) => {
|
||||
instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
|
||||
})
|
||||
str += `
|
||||
${instr}
|
||||
if(isPointInPolygon_${n}(position2D)){
|
||||
vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z + ${FlatList[this.tileset.id][i].flatHeight}, 1.0);
|
||||
vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
|
||||
vsOutput.positionMC.xy = model_local_position_transformed.xy;
|
||||
vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
|
||||
return;
|
||||
}`;
|
||||
|
||||
}
|
||||
this.updateShader(funstr, str);
|
||||
}
|
||||
|
||||
// 根据数组长度,构建 判断点是否在面内 的压平函数
|
||||
getIsinPolygonFun(polygons) {
|
||||
let pmap = polygons.map((polygon) => polygon.length);
|
||||
let uniqueArray = this.getUniqueArray(pmap);
|
||||
let str = ``;
|
||||
uniqueArray.forEach(length => {
|
||||
str += `
|
||||
vec2 points_${length}[${length}];
|
||||
bool isPointInPolygon_${length}(vec2 point){
|
||||
int nCross = 0; // 交点数
|
||||
const int n = ${length};
|
||||
for(int i = 0; i < n; i++){
|
||||
vec2 p1 = points_${length}[i];
|
||||
vec2 p2 = points_${length}[int(mod(float(i+1),float(n)))];
|
||||
if(p1[1] == p2[1]){
|
||||
continue;
|
||||
}
|
||||
if(point[1] < min(p1[1], p2[1])){
|
||||
continue;
|
||||
}
|
||||
if(point[1] >= max(p1[1], p2[1])){
|
||||
continue;
|
||||
}
|
||||
float x = p1[0] + ((point[1] - p1[1]) * (p2[0] - p1[0])) / (p2[1] - p1[1]);
|
||||
if(x > point[0]){
|
||||
nCross++;
|
||||
}
|
||||
}
|
||||
return int(mod(float(nCross), float(2))) == 1;
|
||||
}
|
||||
`
|
||||
})
|
||||
return str
|
||||
}
|
||||
|
||||
updateShader(vtx1, vtx2) {
|
||||
let flatCustomShader = new Cesium.CustomShader({
|
||||
uniforms: {
|
||||
u_tileset_localToWorldMatrix: {
|
||||
type: Cesium.UniformType.MAT4,
|
||||
value: this.matrix,
|
||||
},
|
||||
u_tileset_worldToLocalMatrix: {
|
||||
type: Cesium.UniformType.MAT4,
|
||||
value: this.localMatrix,
|
||||
},
|
||||
u_flatHeight: {
|
||||
type: Cesium.UniformType.FLOAT,
|
||||
value: this.flatHeight,
|
||||
},
|
||||
},
|
||||
vertexShaderText: `
|
||||
// 所有isPointInPolygon函数
|
||||
${vtx1}
|
||||
void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput){
|
||||
vec3 modelMC = vsInput.attributes.positionMC;
|
||||
vec4 model_local_position = vec4(modelMC.x, modelMC.y, modelMC.z, 1.0);
|
||||
vec4 tileset_local_position = u_tileset_worldToLocalMatrix * czm_model * model_local_position;
|
||||
vec2 position2D = vec2(tileset_local_position.x,tileset_local_position.y);
|
||||
float ground_z = 0.0;
|
||||
// 多个多边形区域
|
||||
${vtx2}
|
||||
}`,
|
||||
});
|
||||
this.tileset.customShader = flatCustomShader;
|
||||
this.sdk.viewer.scene.requestRender();
|
||||
|
||||
}
|
||||
|
||||
// 数组去重,不能处理嵌套的数组
|
||||
getUniqueArray = (arr) => {
|
||||
return arr.filter(function (item, index, arr) {
|
||||
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
|
||||
return arr.indexOf(item, 0) === index;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 世界坐标转数组局部坐标
|
||||
cartesiansToLocal(positions) {
|
||||
let arr = [];
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
let position = positions[i];
|
||||
let localp = Cesium.Matrix4.multiplyByPoint(
|
||||
this.localMatrix,
|
||||
position.clone(),
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
arr.push([localp.x, localp.y]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 飞到
|
||||
*/
|
||||
async flyTo() {
|
||||
setActiveViewer(0)
|
||||
closeRotateAround(this.sdk)
|
||||
closeViewFollow(this.sdk)
|
||||
|
||||
if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) {
|
||||
let orientation = {
|
||||
heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0),
|
||||
pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0),
|
||||
roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0)
|
||||
}
|
||||
|
||||
let lng = this.options.customView.relativePosition.lng
|
||||
let lat = this.options.customView.relativePosition.lat
|
||||
let alt = this.options.customView.relativePosition.alt
|
||||
let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
|
||||
let position = { lng: 0, lat: 0 }
|
||||
if (this.options.position) {
|
||||
position = { ...this.options.position }
|
||||
}
|
||||
else if (this.options.positions) {
|
||||
position = { ...this.options.positions[0] }
|
||||
}
|
||||
else if (this.options.line && this.options.line.positions) {
|
||||
position = { ...this.options.line.positions[0] }
|
||||
}
|
||||
else if (this.options.center) {
|
||||
position = { ...this.options.center }
|
||||
}
|
||||
else if (this.options.start) {
|
||||
position = { ...this.options.start }
|
||||
}
|
||||
else {
|
||||
if (this.options.hasOwnProperty('lng')) {
|
||||
position.lng = this.options.lng
|
||||
}
|
||||
if (this.options.hasOwnProperty('lat')) {
|
||||
position.lat = this.options.lat
|
||||
}
|
||||
if (this.options.hasOwnProperty('alt')) {
|
||||
position.alt = this.options.alt
|
||||
}
|
||||
}
|
||||
// 如果没有高度值,则获取紧贴高度计算
|
||||
if (!position.hasOwnProperty('alt')) {
|
||||
position.alt = await this.getClampToHeight(position)
|
||||
}
|
||||
lng = this.options.customView.relativePosition.lng + position.lng
|
||||
lat = this.options.customView.relativePosition.lat + position.lat
|
||||
alt = this.options.customView.relativePosition.alt + position.alt
|
||||
destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
this.sdk.viewer.camera.flyTo({
|
||||
destination: destination,
|
||||
orientation: orientation
|
||||
})
|
||||
}
|
||||
else {
|
||||
let positionArray = []
|
||||
for (let i = 0; i < this.options.positions.length; i++) {
|
||||
let a = Cesium.Cartesian3.fromDegrees(this.options.positions[i].lng, this.options.positions[i].lat, this.center84.alt)
|
||||
positionArray.push(a.x, a.y, a.z)
|
||||
}
|
||||
let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray)
|
||||
this.sdk.viewer.camera.flyToBoundingSphere(BoundingSphere, {
|
||||
offset: {
|
||||
heading: Cesium.Math.toRadians(0.0),
|
||||
pitch: Cesium.Math.toRadians(-90.0),
|
||||
roll: Cesium.Math.toRadians(0.0)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async edit(state) {
|
||||
if (state) {
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
this._DialogObject = await new Dialog(this.sdk.viewer._container, {
|
||||
title: '压平面属性', left: '180px', top: '100px',
|
||||
removeCallBack: () => {
|
||||
this.Dialog.removeCallBack && this.Dialog.removeCallBack()
|
||||
},
|
||||
closeCallBack: () => {
|
||||
this.reset()
|
||||
this.Dialog.closeCallBack && this.Dialog.closeCallBack()
|
||||
}
|
||||
})
|
||||
await this._DialogObject.init()
|
||||
// 内容部分
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label" style="width: 56px;flex: 0 0 56px;">名称</span>
|
||||
<input class="input input-name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label" style="width: 56px;flex: 0 0 56px;">压平高度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input flat-height" type="number" title="" min="-9999999" max="999999999">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
this._DialogObject.contentAppChild(contentElm)
|
||||
let name_elm = contentElm.getElementsByClassName('input-name')[0]
|
||||
name_elm.value = this.options.name
|
||||
name_elm.addEventListener('input', () => {
|
||||
this.name = name_elm.value
|
||||
})
|
||||
|
||||
let height_elm = contentElm.getElementsByClassName('flat-height')[0]
|
||||
height_elm.value = this.options.height
|
||||
height_elm.addEventListener('input', () => {
|
||||
this.height = Number(height_elm.value)
|
||||
this.addFlat()
|
||||
})
|
||||
|
||||
let confirmElm = document.createElement('button');
|
||||
confirmElm.className = 'btn'
|
||||
confirmElm.innerHTML = '确认'
|
||||
this._DialogObject.footAppChild(confirmElm)
|
||||
confirmElm.addEventListener('click', () => {
|
||||
if (!this.options.name) {
|
||||
this.options.name = '压平面'
|
||||
}
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
this._DialogObject.close()
|
||||
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.options)
|
||||
})
|
||||
|
||||
// let flatElm = document.createElement('button');
|
||||
// flatElm.className = 'btn'
|
||||
// flatElm.innerHTML = '<svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>二次编辑'
|
||||
// flatElm.style.width = 'auto'
|
||||
// flatElm.style.position = 'absolute'
|
||||
// flatElm.style.left = '10px'
|
||||
// this._DialogObject.footAppChild(flatElm)
|
||||
// flatElm.addEventListener('click', () => {
|
||||
// console.log('二次编辑')
|
||||
// })
|
||||
}
|
||||
else {
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.options = this.deepCopyObj(this.originalOptions)
|
||||
this.name = this.options.name
|
||||
this.height = this.options.height
|
||||
this.addFlat()
|
||||
}
|
||||
|
||||
flatEdit(state) {
|
||||
if (state) {
|
||||
let positions = that.options.positions
|
||||
let fromDegreesArray = []
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
fromDegreesArray.push(positions[i].lng, positions[i].lat, FlatList[this.tileset.id])
|
||||
}
|
||||
that.positions = Cesium.Cartesian3.fromDegreesArrayHeights(fromDegreesArray)
|
||||
}
|
||||
}
|
||||
|
||||
flicker() { }
|
||||
|
||||
}
|
||||
|
||||
export default Flat;
|
||||
227
src/Obj/Analysis/Flat/index1.js
Normal file
227
src/Obj/Analysis/Flat/index1.js
Normal file
@ -0,0 +1,227 @@
|
||||
import Tools from "../../../Tools";
|
||||
class Flat extends Tools {
|
||||
/**
|
||||
* @constructor
|
||||
* @description 模型压平
|
||||
* @param sdk
|
||||
* @param {Cesium.Cesium3DTileset} tileset 三维模型
|
||||
* @param {Object} options
|
||||
* @param {string} attr.id id
|
||||
* @param {Number} options.height 压平高度
|
||||
* @param {Cesium.Cartesian3[]} attr.positions 压平面坐标
|
||||
*/
|
||||
constructor(sdk, tileset, options = {}) {
|
||||
super(sdk)
|
||||
if (!tileset) return;
|
||||
this.options = { ...options }
|
||||
this.options.id = options.id || this.randomString()
|
||||
this.options.positions = options.positions || []
|
||||
this.tileset = tileset;
|
||||
this.height = options.height;
|
||||
this.center = tileset.boundingSphere.center.clone();
|
||||
this.matrix = Cesium.Transforms.eastNorthUpToFixedFrame(this.center.clone());
|
||||
this.localMatrix = Cesium.Matrix4.inverse(this.matrix, new Cesium.Matrix4());
|
||||
// 多面的坐标数组
|
||||
this.regionList = [];
|
||||
// 多个面坐标转为局部模型坐标
|
||||
this.localPositionsArr = [];
|
||||
this.addRegion()
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加压平面
|
||||
* @param {Object} attr 参数
|
||||
* @param {Cesium.Cartesian3[]} attr.positions 压平面坐标
|
||||
* @param {Number} attr.height 压平深度,当前不支持单独设置
|
||||
* @param {Number} attr.id 唯一标识
|
||||
*/
|
||||
addRegion(attr) {
|
||||
// let { positions, height, id } = attr || {};
|
||||
// // this.flatHeight = height;
|
||||
// if (!id) id = (new Date()).getTime() + "" + Number(Math.random() * 1000).toFixed(0);
|
||||
// this.regionList.push(attr);
|
||||
// for (let i = 0; i < this.regionList.length; i++) {
|
||||
// let item = this.regionList[i];
|
||||
// const positions = item.positions;
|
||||
// let localCoor = this.cartesiansToLocal(positions);
|
||||
// this.localPositionsArr.push(localCoor);
|
||||
// }
|
||||
let positions = this.options.positions
|
||||
let fromDegreesArray = []
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
fromDegreesArray.push(positions[i].lng, positions[i].lat)
|
||||
}
|
||||
let localCoor = this.cartesiansToLocal(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray));
|
||||
this.localPositionsArr.push(localCoor);
|
||||
|
||||
const funstr = this.getIsinPolygonFun(this.localPositionsArr);
|
||||
let str = ``;
|
||||
for (let i = 0; i < this.localPositionsArr.length; i++) {
|
||||
const coors = this.localPositionsArr[i];
|
||||
const n = coors.length;
|
||||
let instr = ``;
|
||||
coors.forEach((coordinate, index) => {
|
||||
instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
|
||||
})
|
||||
str += `
|
||||
${instr}
|
||||
if(isPointInPolygon_${n}(position2D)){
|
||||
vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z, 1.0);
|
||||
vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
|
||||
vsOutput.positionMC.xy = model_local_position_transformed.xy;
|
||||
vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
|
||||
return;
|
||||
}`;
|
||||
|
||||
}
|
||||
|
||||
this.updateShader(funstr, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id删除压平的面
|
||||
* @param {String} id 唯一标识
|
||||
*/
|
||||
removeRegionById(id) {
|
||||
if (!id) return;
|
||||
|
||||
this.regionList = this.regionList.filter((attr) => {
|
||||
return attr.id != id;
|
||||
})
|
||||
|
||||
this.localPositionsArr = [];
|
||||
for (let i = 0; i < this.regionList.length; i++) {
|
||||
let item = this.regionList[i];
|
||||
const positions = item.positions;
|
||||
let localCoor = this.cartesiansToLocal(positions);
|
||||
this.localPositionsArr.push(localCoor);
|
||||
}
|
||||
|
||||
const funstr = this.getIsinPolygonFun(this.localPositionsArr);
|
||||
let str = ``;
|
||||
for (let i = 0; i < this.localPositionsArr.length; i++) {
|
||||
const coors = this.localPositionsArr[i];
|
||||
const n = coors.length;
|
||||
let instr = ``;
|
||||
coors.forEach((coordinate, index) => {
|
||||
instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
|
||||
})
|
||||
str += `
|
||||
${instr}
|
||||
if(isPointInPolygon_${n}(position2D)){
|
||||
vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z, 1.0);
|
||||
vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
|
||||
vsOutput.positionMC.xy = model_local_position_transformed.xy;
|
||||
vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
|
||||
return;
|
||||
}`;
|
||||
|
||||
}
|
||||
this.updateShader(funstr, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
destroy() {
|
||||
this.tileset.customShader = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数组长度,构建 判断点是否在面内 的压平函数
|
||||
*/
|
||||
getIsinPolygonFun(polygons) {
|
||||
let pmap = polygons.map((polygon) => polygon.length);
|
||||
let uniqueArray = this.getUniqueArray(pmap);
|
||||
let str = ``;
|
||||
uniqueArray.forEach(length => {
|
||||
str += `
|
||||
vec2 points_${length}[${length}];
|
||||
bool isPointInPolygon_${length}(vec2 point){
|
||||
int nCross = 0; // 交点数
|
||||
const int n = ${length};
|
||||
for(int i = 0; i < n; i++){
|
||||
vec2 p1 = points_${length}[i];
|
||||
vec2 p2 = points_${length}[int(mod(float(i+1),float(n)))];
|
||||
if(p1[1] == p2[1]){
|
||||
continue;
|
||||
}
|
||||
if(point[1] < min(p1[1], p2[1])){
|
||||
continue;
|
||||
}
|
||||
if(point[1] >= max(p1[1], p2[1])){
|
||||
continue;
|
||||
}
|
||||
float x = p1[0] + ((point[1] - p1[1]) * (p2[0] - p1[0])) / (p2[1] - p1[1]);
|
||||
if(x > point[0]){
|
||||
nCross++;
|
||||
}
|
||||
}
|
||||
return int(mod(float(nCross), float(2))) == 1;
|
||||
}
|
||||
`
|
||||
})
|
||||
return str
|
||||
}
|
||||
|
||||
updateShader(vtx1, vtx2) {
|
||||
let flatCustomShader = new Cesium.CustomShader({
|
||||
uniforms: {
|
||||
u_tileset_localToWorldMatrix: {
|
||||
type: Cesium.UniformType.MAT4,
|
||||
value: this.matrix,
|
||||
},
|
||||
u_tileset_worldToLocalMatrix: {
|
||||
type: Cesium.UniformType.MAT4,
|
||||
value: this.localMatrix,
|
||||
},
|
||||
u_flatHeight: {
|
||||
type: Cesium.UniformType.FLOAT,
|
||||
value: this.height,
|
||||
},
|
||||
},
|
||||
vertexShaderText: `
|
||||
// 所有isPointInPolygon函数
|
||||
${vtx1}
|
||||
void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput){
|
||||
vec3 modelMC = vsInput.attributes.positionMC;
|
||||
vec4 model_local_position = vec4(modelMC.x, modelMC.y, modelMC.z, 1.0);
|
||||
vec4 tileset_local_position = u_tileset_worldToLocalMatrix * czm_model * model_local_position;
|
||||
vec2 position2D = vec2(tileset_local_position.x,tileset_local_position.y);
|
||||
float ground_z = 0.0 + u_flatHeight;
|
||||
// 多个多边形区域
|
||||
${vtx2}
|
||||
}`,
|
||||
});
|
||||
this.tileset.customShader = flatCustomShader;
|
||||
this.sdk.viewer.scene.requestRender();
|
||||
}
|
||||
|
||||
// 数组去重,不能处理嵌套的数组
|
||||
getUniqueArray = (arr) => {
|
||||
return arr.filter(function (item, index, arr) {
|
||||
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
|
||||
return arr.indexOf(item, 0) === index;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 世界坐标转数组局部坐标
|
||||
cartesiansToLocal(positions) {
|
||||
let arr = [];
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
let position = positions[i];
|
||||
let localp = Cesium.Matrix4.multiplyByPoint(
|
||||
this.localMatrix,
|
||||
position.clone(),
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
arr.push([localp.x, localp.y]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default Flat;
|
||||
8
src/Obj/Analysis/Profile/_element.js
Normal file
8
src/Obj/Analysis/Profile/_element.js
Normal file
@ -0,0 +1,8 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="profile-echarts"></div>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
637
src/Obj/Analysis/Profile/index.js
Normal file
637
src/Obj/Analysis/Profile/index.js
Normal file
@ -0,0 +1,637 @@
|
||||
import Draw from "../../../Draw/draw";
|
||||
import MouseEvent from "../../../Event";
|
||||
import MouseTip from "../../../MouseTip";
|
||||
import Dialog from '../../../BaseDialog';
|
||||
import { html } from "./_element";
|
||||
class Profile extends Draw {
|
||||
/**
|
||||
* @constructor 剖面分析
|
||||
* @param sdk
|
||||
**/
|
||||
constructor(sdk, _Dialog = {}) {
|
||||
window.addEventListener("resize", () => {
|
||||
this.echartsObject && this.echartsObject.resize();
|
||||
});
|
||||
super(sdk)
|
||||
this.viewer = sdk.viewer;
|
||||
this.Dialog = _Dialog
|
||||
YJ.Analysis.AnalysesResults.push(this)
|
||||
Profile.create(this)
|
||||
}
|
||||
static create(that) {
|
||||
this._currentId = Cesium.createGuid()
|
||||
let id = this._currentId
|
||||
that.clean()
|
||||
if (YJ.Measure.GetMeasureStatus()) {
|
||||
console.warn('上一次测量未结束')
|
||||
} else {
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
that.tip = new MouseTip('左键确定,右键取消', that.sdk)
|
||||
that.event = new MouseEvent(that.sdk)
|
||||
that.positions = []
|
||||
that.points_ids = [] //存放左键点击时临时添加的point的id
|
||||
|
||||
let cache_positions = []
|
||||
let car = undefined
|
||||
that.event.mouse_left(async (movement, cartesian) => {
|
||||
try {
|
||||
if (!that.entityHasCreated) {
|
||||
Profile.create_polyline(that)
|
||||
}
|
||||
cache_positions.push(cartesian)
|
||||
that.points_ids.push(that.create_point(cartesian,))
|
||||
if (cache_positions.length == 2) {
|
||||
that.end()
|
||||
let positions = []
|
||||
cache_positions.forEach((item) => {
|
||||
positions.push(that.cartesian3Towgs84(item, that.viewer))
|
||||
})
|
||||
Profile.interPoints(that).then((points) => {
|
||||
if (this._currentId && this._currentId === id) {
|
||||
that._DialogObject ? Profile.initEcharts(that, points) : Profile.edit(that, points)
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
})
|
||||
that.event.mouse_right((movement, cartesian) => {
|
||||
let positions = []
|
||||
cache_positions = []
|
||||
that.clean()
|
||||
})
|
||||
that.event.mouse_move((movement, cartesian) => {
|
||||
that.positions = cache_positions.concat(cartesian)
|
||||
that.tip.setPosition(
|
||||
cartesian,
|
||||
movement.endPosition.x,
|
||||
movement.endPosition.y
|
||||
)
|
||||
})
|
||||
|
||||
that.event.gesture_pinck_start((movement, cartesian) => {
|
||||
let startTime = new Date()
|
||||
that.event.gesture_pinck_end(() => {
|
||||
let endTime = new Date()
|
||||
if (endTime - startTime >= 500) {
|
||||
let positions = []
|
||||
cache_positions = []
|
||||
that.end()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static create_polyline(that) {
|
||||
that.entityHasCreated = true
|
||||
let id = that.randomString()
|
||||
that.polyline = that.viewer.entities.add(
|
||||
new Cesium.Entity({
|
||||
id: id,
|
||||
polyline: {
|
||||
positions: new Cesium.CallbackProperty(() => {
|
||||
return that.positions
|
||||
}, false),
|
||||
width: 5,
|
||||
material: Cesium.Color.fromCssColorString(that.color),
|
||||
clampToGround: true,
|
||||
zIndex: 99999999
|
||||
},
|
||||
})
|
||||
)
|
||||
return id
|
||||
}
|
||||
|
||||
/**
|
||||
* 线段插值点
|
||||
*/
|
||||
static async interPoints(that) {
|
||||
let viewer = that.viewer
|
||||
let positions = that.positions
|
||||
let positionsCartographic = []
|
||||
let positions84 = [];
|
||||
for (let index = 0; index < positions.length; index++) {
|
||||
const element = positions[index];
|
||||
let cartographic = viewer.scene.globe.ellipsoid.cartesianToCartographic(element);
|
||||
positionsCartographic.push(cartographic);
|
||||
let pos84 = that.cartesian3Towgs84(element, viewer)
|
||||
positions84.push(pos84);
|
||||
}
|
||||
let positions_Inter = [];
|
||||
let height = await that.getClampToHeight({ lng: positions84[0].lng, lat: positions84[0].lat });
|
||||
positions_Inter.push({
|
||||
position: { lng: positions84[0].lng, lat: positions84[0].lat, height: height },
|
||||
distance: 0,
|
||||
});
|
||||
for (let i = 0; i < positionsCartographic.length - 1; i++) {
|
||||
let line = turf.lineString([[positions84[i].lng, positions84[i].lat], [positions84[i + 1].lng, positions84[i + 1].lat]]);
|
||||
let totalDistance = turf.length(line, { units: 'kilometers' });
|
||||
|
||||
const m_Cartographic0 = positionsCartographic[i];
|
||||
const m_Cartographic1 = positionsCartographic[i + 1];
|
||||
let a =
|
||||
Math.abs(m_Cartographic0.longitude - m_Cartographic1.longitude) *
|
||||
10000000;
|
||||
let b =
|
||||
Math.abs(m_Cartographic0.latitude - m_Cartographic1.latitude) *
|
||||
10000000;
|
||||
//等距采样
|
||||
if (a > b) b = a;
|
||||
let length = parseInt(b / 2);
|
||||
if (length > 150) length = 150;
|
||||
if (length < 2) length = 2;
|
||||
let distance = totalDistance / (length - 1)
|
||||
for (let j = 0; j < length - 1; j++) {
|
||||
let start = j * distance
|
||||
let stop = (j + 1) * distance
|
||||
let sliced = await turf.lineSliceAlong(line, start, stop, { units: 'kilometers' });
|
||||
let lng = sliced.geometry.coordinates[sliced.geometry.coordinates.length - 1][0]
|
||||
let lat = sliced.geometry.coordinates[sliced.geometry.coordinates.length - 1][1]
|
||||
let height = await that.getClampToHeight({ lng: lng, lat: lat });
|
||||
positions_Inter.push({
|
||||
position: { lng: lng, lat: lat, height: height },
|
||||
distance: stop * 1000,
|
||||
});
|
||||
}
|
||||
}
|
||||
return positions_Inter
|
||||
}
|
||||
|
||||
static async edit(that, points) {
|
||||
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.clean()
|
||||
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
|
||||
},
|
||||
})
|
||||
await that._DialogObject.init()
|
||||
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' profile'
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
that._DialogObject.contentAppChild(contentElm)
|
||||
let resetBtn = document.createElement('button');
|
||||
resetBtn.innerHTML = '<svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>重新绘制'
|
||||
resetBtn.style.width = 'auto'
|
||||
resetBtn.addEventListener('click', () => {
|
||||
Profile.create(that)
|
||||
Profile.initEcharts(that)
|
||||
})
|
||||
that._DialogObject.footAppChild(resetBtn)
|
||||
Profile.initEcharts(that, points)
|
||||
}
|
||||
|
||||
static initEcharts(that, points) {
|
||||
let datas = [],
|
||||
coords = [];
|
||||
const pointsData = points;
|
||||
|
||||
let option
|
||||
if (pointsData) {
|
||||
const maxDistance = pointsData[pointsData.length - 1].distance;
|
||||
let xAixMax = Math.ceil(maxDistance);
|
||||
for (let index = 0; index < pointsData.length; index++) {
|
||||
const element = pointsData[index];
|
||||
if (element.position.height === void 0) {
|
||||
continue
|
||||
}
|
||||
const curData = [
|
||||
element.distance.toFixed(2),
|
||||
element.position.height.toFixed(2),
|
||||
];
|
||||
datas.push(curData);
|
||||
const curCoords = [element.position.lng, element.position.lat];
|
||||
coords.push(curCoords);
|
||||
}
|
||||
const pointOption = {
|
||||
show: true,
|
||||
pixelSize: 10,
|
||||
color: Cesium.Color.GREEN,
|
||||
outlineColor: Cesium.Color.SKYBLUE,
|
||||
outlineWidth: 3,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY
|
||||
};
|
||||
const ele = that._DialogObject._element.content.getElementsByClassName("profile-echarts")[0];
|
||||
that.echartsObject = echarts.init(ele);
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
textStyle: {
|
||||
align: "left",
|
||||
},
|
||||
formatter(params) {
|
||||
const xy = coords[params[0].dataIndex];
|
||||
const tipData = params[0]["data"];
|
||||
if (!that.tipEntity) {
|
||||
that.tipEntity = that.sdk.viewer.entities.add({
|
||||
position: Cesium.Cartesian3.fromDegrees(
|
||||
xy[0],
|
||||
xy[1],
|
||||
Number(tipData[1])
|
||||
),
|
||||
point: pointOption,
|
||||
});
|
||||
} else {
|
||||
that.tipEntity.position = Cesium.Cartesian3.fromDegrees(
|
||||
xy[0],
|
||||
xy[1],
|
||||
Number(tipData[1])
|
||||
);
|
||||
}
|
||||
return (
|
||||
"距离:" +
|
||||
tipData[0] +
|
||||
"m<br>" +
|
||||
"高度:" +
|
||||
tipData[1] +
|
||||
"m<br>" +
|
||||
"坐标:" +
|
||||
xy[0].toFixed(5) +
|
||||
"," +
|
||||
xy[1].toFixed(5)
|
||||
);
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 40,
|
||||
bottom: 20,
|
||||
left: 55,
|
||||
right: 30
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [
|
||||
{
|
||||
type: "value",
|
||||
max: xAixMax,
|
||||
scale: true,
|
||||
axisLabel: {
|
||||
color: '#ffffff'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "#ffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: "value",
|
||||
scale: true,
|
||||
axisLabel: {
|
||||
color: '#ffffff'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "#ffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: "ProfileLine",
|
||||
type: "line",
|
||||
data: datas,
|
||||
smooth: true,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#39FDA1",
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 3,
|
||||
color: {
|
||||
type: "linear",
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 1,
|
||||
y2: 0,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: "rgba(85,254,139,1)", // 0% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 0.5,
|
||||
color: "rgba(7,252,202,1)", // 100% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "rgba(14,245,210,1)", // 100% 处的颜色
|
||||
},
|
||||
],
|
||||
globalCoord: false, // 缺省为 false
|
||||
},
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: "rgba(102,153,255,1)",
|
||||
},
|
||||
{
|
||||
offset: 0.8,
|
||||
color: "rgba(102,153,255,0.08)",
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "rgba(9,173,208,0.15)",
|
||||
},
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: "rgba(14,245,210,1)", //阴影颜色
|
||||
shadowBlur: 20,
|
||||
},
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{
|
||||
type: "max",
|
||||
name: "最高点",
|
||||
label: {
|
||||
color: '#ffffff',
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "min",
|
||||
name: "最低点",
|
||||
label: {
|
||||
color: '#ffffff',
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
else {
|
||||
const ele = that._DialogObject._element.content.getElementsByClassName("profile-echarts")[0];
|
||||
that.echartsObject = echarts.init(ele);
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
textStyle: {
|
||||
align: "left",
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 40,
|
||||
bottom: 20,
|
||||
left: 55,
|
||||
right: 30
|
||||
},
|
||||
calculable: true,
|
||||
xAxis: [
|
||||
{
|
||||
type: "value",
|
||||
scale: true,
|
||||
axisLabel: {
|
||||
color: '#ffffff'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "#ffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: "value",
|
||||
scale: true,
|
||||
axisLabel: {
|
||||
color: '#ffffff'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: "#ffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: "ProfileLine",
|
||||
type: "line",
|
||||
data: [],
|
||||
smooth: true,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#39FDA1",
|
||||
},
|
||||
},
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 3,
|
||||
color: {
|
||||
type: "linear",
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 1,
|
||||
y2: 0,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: "rgba(85,254,139,1)", // 0% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 0.5,
|
||||
color: "rgba(7,252,202,1)", // 100% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "rgba(14,245,210,1)", // 100% 处的颜色
|
||||
},
|
||||
],
|
||||
globalCoord: false, // 缺省为 false
|
||||
},
|
||||
},
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: "rgba(102,153,255,1)",
|
||||
},
|
||||
{
|
||||
offset: 0.8,
|
||||
color: "rgba(102,153,255,0.08)",
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "rgba(9,173,208,0.15)",
|
||||
},
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: "rgba(14,245,210,1)", //阴影颜色
|
||||
shadowBlur: 20,
|
||||
},
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{
|
||||
type: "max",
|
||||
name: "最高点",
|
||||
label: {
|
||||
color: '#ffffff',
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "min",
|
||||
name: "最低点",
|
||||
label: {
|
||||
color: '#ffffff',
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
that.echartsObject.setOption(option);
|
||||
}
|
||||
|
||||
clean() {
|
||||
this.end()
|
||||
this._currentId = null
|
||||
this.entityHasCreated = false
|
||||
this.polyline && this.viewer.entities.remove(this.polyline)
|
||||
this.tipEntity && this.viewer.entities.remove(this.tipEntity)
|
||||
this.polyline = null
|
||||
this.tipEntity = null
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.clean()
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// const Profile = function (viewer, callback) {
|
||||
// if (!viewer) throw new Error("no viewer object!");
|
||||
// if (window.profileEntities && window.profileEntities.length > 0) {
|
||||
// window.profileEntities.forEach((element) => {
|
||||
// window.viewer.entities.remove(element);
|
||||
// });
|
||||
// }
|
||||
// window.profileEntities = [];
|
||||
// CreatePolyline(
|
||||
// viewer,
|
||||
// window.profileEntities,
|
||||
// { color: Cesium.Color.RED, width: 2 },
|
||||
// function (e) {
|
||||
// e.polyline.clampToGround = true;
|
||||
// console.log(e.pottingPoint);
|
||||
// let points = interPoints(viewer, e.pottingPoint, [e]);
|
||||
// console.log(points);
|
||||
// if (typeof callback == "function") callback(points);
|
||||
// }
|
||||
// );
|
||||
// };
|
||||
// /**
|
||||
// * 线段插值点
|
||||
// * @param {*} viewer
|
||||
// * @param {*} positions 线段节点集合
|
||||
// * @param {*} objectsToExclude 高度采集时排除的对象集合
|
||||
// * @returns 经纬度点集合,包含距离值
|
||||
// */
|
||||
// function interPoints(viewer, positions, objectsToExclude) {
|
||||
// let positionsCartographic = [];
|
||||
// let terrainSamplePositions = [];
|
||||
// for (let index = 0; index < positions.length; index++) {
|
||||
// const element = positions[index];
|
||||
// let ellipsoid = viewer.scene.globe.ellipsoid;
|
||||
// let cartographic = ellipsoid.cartesianToCartographic(element);
|
||||
// positionsCartographic.push(cartographic);
|
||||
// }
|
||||
// for (let i = 0; i < positionsCartographic.length; i++) {
|
||||
// const m_Cartographic0 = positionsCartographic[i];
|
||||
// const m_Cartographic1 = positionsCartographic[i + 1];
|
||||
// if (m_Cartographic1) {
|
||||
// let a =
|
||||
// Math.abs(m_Cartographic0.longitude - m_Cartographic1.longitude) *
|
||||
// 10000000;
|
||||
// let b =
|
||||
// Math.abs(m_Cartographic0.latitude - m_Cartographic1.latitude) *
|
||||
// 10000000;
|
||||
// //等距采样
|
||||
// if (a > b) b = a;
|
||||
// let length = parseInt(b / 2);
|
||||
// if (length > 1000) length = 1000;
|
||||
// if (length < 2) length = 2;
|
||||
// for (let j = 0; j < length; j++) {
|
||||
// terrainSamplePositions.push(
|
||||
// new Cesium.Cartographic(
|
||||
// Cesium.Math.lerp(
|
||||
// m_Cartographic0.longitude,
|
||||
// m_Cartographic1.longitude,
|
||||
// j / (length - 1)
|
||||
// ),
|
||||
// Cesium.Math.lerp(
|
||||
// m_Cartographic0.latitude,
|
||||
// m_Cartographic1.latitude,
|
||||
// j / (length - 1)
|
||||
// )
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
// terrainSamplePositions.pop();
|
||||
// } else {
|
||||
// terrainSamplePositions.push(m_Cartographic0);
|
||||
// }
|
||||
// }
|
||||
// let positions_Inter = [];
|
||||
// let distance = 0;
|
||||
// for (let n = 0; n < terrainSamplePositions.length; n++) {
|
||||
// //地理坐标(弧度)转经纬度坐标
|
||||
// let curCartographic = terrainSamplePositions[n];
|
||||
// let height = viewer.scene.sampleHeight(curCartographic, objectsToExclude);
|
||||
// const lon = (curCartographic.longitude / Math.PI) * 180;
|
||||
// const lat = (curCartographic.latitude / Math.PI) * 180;
|
||||
// let point = Cesium.Cartesian3.fromDegrees(lon, lat, height);
|
||||
// let preCartographic = terrainSamplePositions[n - 1];
|
||||
// if (preCartographic) {
|
||||
// const lon1 = (preCartographic.longitude / Math.PI) * 180;
|
||||
// const lat1 = (preCartographic.latitude / Math.PI) * 180;
|
||||
// let point1 = Cesium.Cartesian3.fromDegrees(lon1, lat1, height);
|
||||
// let curDis = Cesium.Cartesian3.distance(point1, point);
|
||||
// distance += curDis;
|
||||
// }
|
||||
// positions_Inter.push({
|
||||
// position: { lon: lon, lat: lat, height: height },
|
||||
// distance: distance,
|
||||
// });
|
||||
// }
|
||||
// return positions_Inter;
|
||||
// }
|
||||
export default Profile;
|
||||
145
src/Obj/Analysis/Section/index.js
Normal file
145
src/Obj/Analysis/Section/index.js
Normal file
@ -0,0 +1,145 @@
|
||||
import Tools from "../../../Tools";
|
||||
class Section extends Tools {
|
||||
/**
|
||||
* @constructor 剖切
|
||||
* @param sdk
|
||||
* @param tiles3d {object} 3dtiles对象
|
||||
* @param {Array.<object>} options.positions 经纬度[{lon,lat,alt},...]
|
||||
* @param options.regionsType=false 裁剪类型 false:裁剪内部,true:裁剪外部
|
||||
* **/
|
||||
constructor(sdk, tiles3d, options = {}) {
|
||||
|
||||
|
||||
super(sdk, options)
|
||||
this.viewer = sdk.viewer
|
||||
this.tiles3d = tiles3d
|
||||
this.options = { ...options }
|
||||
this.options.regionsType = this.options.regionsType || false
|
||||
YJ.Analysis.SectionResults.push(this)
|
||||
this.Planes = []
|
||||
Section.start(this)
|
||||
}
|
||||
|
||||
get regionsType() {
|
||||
return this.options.regionsType
|
||||
}
|
||||
set regionsType(v) {
|
||||
this.options.regionsType = v
|
||||
if (this.Planes.length > 0) {
|
||||
this.Planes = []
|
||||
Section.planeCollection(this)
|
||||
}
|
||||
}
|
||||
|
||||
static start(that) {
|
||||
let positions = that.options.positions || []
|
||||
if(!that.isConvex(positions)) {
|
||||
window.ELEMENT && window.ELEMENT.Message({
|
||||
message: '不支持凹多边形',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
console.log('不支持凹多边形')
|
||||
return
|
||||
}
|
||||
that.inverseTransform = getInverseTransform(that.tiles3d)
|
||||
that.Planes = []
|
||||
let array = []
|
||||
if (positions.length > 0) {
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
array.push([positions[i].lng, positions[i].lat])
|
||||
}
|
||||
array.push([positions[0].lng, positions[0].lat])
|
||||
that.isClockwise = turf.booleanClockwise(turf.lineString(array));
|
||||
}
|
||||
Section.planeCollection(that)
|
||||
|
||||
function getInverseTransform(tileSet) {
|
||||
let transform
|
||||
const tmp = tileSet.root.transform
|
||||
if ((tmp && tmp.equals(Cesium.Matrix4.IDENTITY)) || !tmp) {
|
||||
transform = Cesium.Transforms.eastNorthUpToFixedFrame(tileSet.boundingSphere.center)
|
||||
} else {
|
||||
transform = Cesium.Matrix4.fromArray(tileSet.root.transform)
|
||||
}
|
||||
return Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4())
|
||||
}
|
||||
}
|
||||
|
||||
static planeCollection(that) {
|
||||
let positions = that.options.positions || []
|
||||
if (that.regionsType == that.isClockwise) {
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
if (i === (positions.length - 1)) {
|
||||
that.Planes.push(createPlane(positions[i], positions[0], that.inverseTransform))
|
||||
} else {
|
||||
that.Planes.push(createPlane(positions[i], positions[i + 1], that.inverseTransform))
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = positions.length - 1; i >= 0; i--) {
|
||||
if (i === 0) {
|
||||
that.Planes.push(createPlane(positions[i], positions[positions.length - 1], that.inverseTransform))
|
||||
} else {
|
||||
that.Planes.push(createPlane(positions[i], positions[i - 1], that.inverseTransform))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(that.tiles3d.clippingPlanes) {
|
||||
that.tiles3d.clippingPlanes.removeAll()
|
||||
for(let i=0;i<that.Planes.length;i++) {
|
||||
that.tiles3d.clippingPlanes.add(that.Planes[i])
|
||||
}
|
||||
that.tiles3d.clippingPlanes.enabled = true
|
||||
|
||||
}
|
||||
else {
|
||||
const PlaneCollection = new Cesium.ClippingPlaneCollection({
|
||||
planes: that.Planes,
|
||||
enabled: true,
|
||||
unionClippingRegions: that.regionsType,
|
||||
edgeColor: Cesium.Color.WHITE,
|
||||
edgeWidth: 1,
|
||||
})
|
||||
that.tiles3d.clippingPlanes = PlaneCollection
|
||||
}
|
||||
|
||||
function createPlane(p1, p2, inverseTransform) {
|
||||
// 将仅包含经纬度信息的p1,p2,转换为相应坐标系的cartesian3对象
|
||||
const p1C3 = getOriginCoordinateSystemPoint(p1, inverseTransform)
|
||||
const p2C3 = getOriginCoordinateSystemPoint(p2, inverseTransform)
|
||||
|
||||
// 定义一个垂直向上的向量up
|
||||
const up = new Cesium.Cartesian3(0, 0, 10)
|
||||
// right 实际上就是由p1指向p2的向量
|
||||
const right = Cesium.Cartesian3.subtract(p2C3, p1C3, new Cesium.Cartesian3())
|
||||
|
||||
// 计算normal, right叉乘up,得到平面法向量,这个法向量指向right的右侧
|
||||
let normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3())
|
||||
normal = Cesium.Cartesian3.normalize(normal, normal)
|
||||
|
||||
// 由于已经获得了法向量和过平面的一点,因此可以直接构造Plane,并进一步构造ClippingPlane
|
||||
const planeTmp = Cesium.Plane.fromPointNormal(p1C3, normal)
|
||||
return Cesium.ClippingPlane.fromPlane(planeTmp)
|
||||
}
|
||||
|
||||
function getOriginCoordinateSystemPoint(point, inverseTransform) {
|
||||
const val = Cesium.Cartesian3.fromDegrees(point.lng, point.lat)
|
||||
return Cesium.Matrix4.multiplyByPoint(
|
||||
inverseTransform, val, new Cesium.Cartesian3(0, 0, 0))
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.Planes = []
|
||||
// this.tiles3d.clippingPlanes = new Cesium.ClippingPlaneCollection()
|
||||
if(this.tiles3d.clippingPlanes) {
|
||||
this.tiles3d.clippingPlanes.enabled = false
|
||||
this.tiles3d.clippingPlanes.removeAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Section;
|
||||
517
src/Obj/Analysis/SlopeAspect/index.js
Normal file
517
src/Obj/Analysis/SlopeAspect/index.js
Normal file
@ -0,0 +1,517 @@
|
||||
|
||||
import Tools from "../../../Tools";
|
||||
import DrawPolygon from "../../../Draw/drawPolygon"
|
||||
import MouseEvent from '../../../Event/index'
|
||||
class SlopeAspect extends Tools {
|
||||
/**
|
||||
* @constructor 坡度坡向分析
|
||||
* @param sdk
|
||||
* **/
|
||||
constructor(sdk) {
|
||||
super(sdk)
|
||||
this.viewer = sdk.viewer;
|
||||
let terrainAvailability = this.viewer.terrainProvider.availability;
|
||||
if (!terrainAvailability) {
|
||||
this.error = '未加载地形数据!'
|
||||
window.ELEMENT && window.ELEMENT.Message({
|
||||
message: '未加载地形数据!',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
return
|
||||
}
|
||||
this.event
|
||||
this.result = []; //存储创建的坡度分析结果,primitive集合
|
||||
this.handler = undefined;
|
||||
this.toolTip = "";
|
||||
YJ.Analysis.AnalysesResults.push(this)
|
||||
this.Draw = new DrawPolygon(this.sdk)
|
||||
// this.createNew4Distance()
|
||||
this.createNew4Num(50)
|
||||
}
|
||||
|
||||
//等距离切分网格
|
||||
createNew4Distance(distance) {
|
||||
distance = distance || 0.1; //默认0.1km精度
|
||||
let width = distance * 200 > 35 ? 35 : distance * 200;
|
||||
this.arrowWidth = width < 15 ? 15 : width;
|
||||
const $this = this;
|
||||
const viewer = this.viewer;
|
||||
this.Draw.start((e, positions) => {
|
||||
if (!positions || positions.length <= 2) {
|
||||
window.ELEMENT && window.ELEMENT.Message({
|
||||
message: '至少拥有三个坐标位置!',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
return
|
||||
}
|
||||
let boundary = [];
|
||||
let minX = 10000,
|
||||
minY = 10000,
|
||||
maxX = -10000,
|
||||
maxY = -1000;
|
||||
for (let index = 0; index < positions.length; index++) {
|
||||
const element = positions[index];
|
||||
const x = element.lng;
|
||||
const y = element.lat;
|
||||
boundary.push([x, y]);
|
||||
minX = x < minX ? x : minX;
|
||||
minY = y < minY ? y : minY;
|
||||
maxX = x > maxX ? x : maxX;
|
||||
maxY = y > maxY ? y : maxY;
|
||||
}
|
||||
boundary.push(boundary[0]);
|
||||
let bbox = [minX, minY, maxX, maxY];
|
||||
let mask = turf.polygon([boundary]);
|
||||
let gridSquare = turf.squareGrid(bbox, distance, { mask: mask });
|
||||
this.createEllipse(gridSquare);
|
||||
})
|
||||
}
|
||||
// 等分切分网格,切分成一个num*num的网格
|
||||
createNew4Num(n) {
|
||||
let num = n
|
||||
this.Draw.start((e, positions) => {
|
||||
if (!positions || positions.length <= 2) {
|
||||
console.warn('至少拥有三个坐标位置!')
|
||||
return
|
||||
}
|
||||
let boundary = [];
|
||||
let minX = 10000,
|
||||
minY = 10000,
|
||||
maxX = -10000,
|
||||
maxY = -1000;
|
||||
for (let index = 0; index < positions.length; index++) {
|
||||
const element = positions[index];
|
||||
const x = element.lng;
|
||||
const y = element.lat;
|
||||
boundary.push([x, y]);
|
||||
minX = x < minX ? x : minX;
|
||||
minY = y < minY ? y : minY;
|
||||
maxX = x > maxX ? x : maxX;
|
||||
maxY = y > maxY ? y : maxY;
|
||||
}
|
||||
boundary.push(boundary[0]);
|
||||
let bbox = [minX, minY, maxX, maxY];
|
||||
let a = maxX - minX;
|
||||
let b = maxY - minY;
|
||||
b = b > a ? b : a;
|
||||
|
||||
// 根据面积修改网格数
|
||||
let mask = turf.polygon([boundary]);
|
||||
let area = turf.area(mask);
|
||||
if (area > 5000000000000) {
|
||||
num = num - 25;
|
||||
}
|
||||
else if (area > 1000000000000) {
|
||||
num = num - 20;
|
||||
}
|
||||
else if (area > 500000000000) {
|
||||
num = num - 15;
|
||||
}
|
||||
else if (area > 100000000000) {
|
||||
num = num - 10;
|
||||
}
|
||||
else if (area > 60000000000) {
|
||||
num = num - 5;
|
||||
}
|
||||
|
||||
const step = b / num;
|
||||
let width = step * 2000 > 35 ? 35 : step * 2000;
|
||||
this.arrowWidth = width < 15 ? 15 : width;
|
||||
|
||||
let gridSquare = turf.squareGrid(bbox, step, {
|
||||
units: "degrees",
|
||||
mask: mask,
|
||||
});
|
||||
this.createEllipse(gridSquare);
|
||||
})
|
||||
// CreatePolygonOnGround(
|
||||
// viewer,
|
||||
// [],
|
||||
// {
|
||||
// color: Cesium.Color.RED.withAlpha(0.1),
|
||||
// outlineColor: Cesium.Color.YELLOW,
|
||||
// outlineWidth: 2,
|
||||
// },
|
||||
// function (polygon) {
|
||||
// let degrees = $this.Cartesian3ListToWGS84(polygon.pottingPoint);
|
||||
// viewer.entities.remove(polygon);
|
||||
// let boundary = [];
|
||||
// let minX = 10000,
|
||||
// minY = 10000,
|
||||
// maxX = -10000,
|
||||
// maxY = -1000;
|
||||
// for (let index = 0; index < degrees.length; index++) {
|
||||
// const element = degrees[index];
|
||||
// const x = element.lng;
|
||||
// const y = element.lat;
|
||||
// boundary.push([x, y]);
|
||||
// minX = x < minX ? x : minX;
|
||||
// minY = y < minY ? y : minY;
|
||||
// maxX = x > maxX ? x : maxX;
|
||||
// maxY = y > maxY ? y : maxY;
|
||||
// }
|
||||
// boundary.push(boundary[0]);
|
||||
// let bbox = [minX, minY, maxX, maxY];
|
||||
// let a = maxX - minX;
|
||||
// let b = maxY - minY;
|
||||
// b = b > a ? b : a;
|
||||
// const step = b / num;
|
||||
// let width = step * 2000 > 35 ? 35 : step * 2000;
|
||||
// this.arrowWidth = width < 15 ? 15 : width;
|
||||
// let mask = turf.polygon([boundary]);
|
||||
// let gridSquare = turf.squareGrid(bbox, step, {
|
||||
// units: "degrees",
|
||||
// mask: mask,
|
||||
// });
|
||||
// this.createEllipse(gridSquare);
|
||||
// }
|
||||
// );
|
||||
}
|
||||
createEllipse(gridSquare) {
|
||||
let boxResults = [];
|
||||
for (let index = 0; index < gridSquare.features.length; index++) {
|
||||
const feature = gridSquare.features[index];
|
||||
const coordinates = feature.geometry.coordinates[0];
|
||||
const centerdegree = [
|
||||
(coordinates[0][0] + coordinates[2][0]) / 2,
|
||||
(coordinates[0][1] + coordinates[2][1]) / 2,
|
||||
];
|
||||
let centerCartographic = Cesium.Cartographic.fromDegrees(
|
||||
centerdegree[0],
|
||||
centerdegree[1]
|
||||
);
|
||||
boxResults.push(centerCartographic);
|
||||
for (let i = 0; i < coordinates.length; i++) {
|
||||
const coord = coordinates[i];
|
||||
let cartographic = Cesium.Cartographic.fromDegrees(coord[0], coord[1]);
|
||||
boxResults.push(cartographic);
|
||||
const coord1 = coordinates[i + 1];
|
||||
if (coord1) {
|
||||
let newCoord = [
|
||||
(coord[0] + coord1[0]) / 2,
|
||||
(coord[1] + coord1[1]) / 2,
|
||||
];
|
||||
let newCartographic = Cesium.Cartographic.fromDegrees(
|
||||
newCoord[0],
|
||||
newCoord[1]
|
||||
);
|
||||
boxResults.push(newCartographic);
|
||||
}
|
||||
}
|
||||
}
|
||||
let _this = this
|
||||
// 点位过多,分为三份计算
|
||||
let num = (Math.floor(boxResults.length / 3) + '')
|
||||
num = Number(num.substring(0, num.length - 1))*10
|
||||
let i=0
|
||||
let points = boxResults.slice(i * num, (i + 1) * num)
|
||||
if (points.length > 0) {
|
||||
sampleTerrainMostDetailed(points)
|
||||
}
|
||||
function sampleTerrainMostDetailed(ps) {
|
||||
Cesium.sampleTerrainMostDetailed(
|
||||
_this.viewer.scene.terrainProvider,
|
||||
ps
|
||||
).then((updatePositions) => {
|
||||
i++
|
||||
let points = boxResults.slice(i * num, (i + 1) * num)
|
||||
if (points.length > 0) {
|
||||
sampleTerrainMostDetailed(points)
|
||||
}
|
||||
let arrr = [];
|
||||
let ellipseResults = updatePositions.reduce(function (
|
||||
pre,
|
||||
item,
|
||||
index,
|
||||
updatePositions
|
||||
) {
|
||||
var begin = index * 10;
|
||||
var end = begin + 10;
|
||||
var res = updatePositions.slice(begin, end);
|
||||
if (res.length != 0) {
|
||||
arrr[index] = res;
|
||||
}
|
||||
return arrr;
|
||||
},
|
||||
[]);
|
||||
_this.calculateSlope(ellipseResults);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createPolygonInsrance(points, color, curSlope) {
|
||||
let positions = [];
|
||||
for (let index = 1; index < points.length - 1; index++) {
|
||||
const element = points[index];
|
||||
positions.push(Cesium.Cartographic.toCartesian(element));
|
||||
}
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(positions),
|
||||
});
|
||||
|
||||
let polygonInstance = new Cesium.GeometryInstance({
|
||||
id: {
|
||||
type: "SlopeAspect",
|
||||
value: curSlope
|
||||
},
|
||||
geometry: polygon,
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.fromCssColorString(color)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
},
|
||||
});
|
||||
return polygonInstance;
|
||||
}
|
||||
createArrowInstance(
|
||||
targetPoint,
|
||||
center,
|
||||
diagonalPoint,
|
||||
heightDifference,
|
||||
curSlope
|
||||
) {
|
||||
let cartographic_0 = new Cesium.Cartographic(
|
||||
(targetPoint.longitude + center.longitude) / 2,
|
||||
(targetPoint.latitude + center.latitude) / 2,
|
||||
(targetPoint.height + center.height) / 2
|
||||
);
|
||||
let cartographic_1 = new Cesium.Cartographic(
|
||||
(diagonalPoint.longitude + center.longitude) / 2,
|
||||
(diagonalPoint.latitude + center.latitude) / 2,
|
||||
(diagonalPoint.height + center.height) / 2
|
||||
);
|
||||
//偏移的
|
||||
let positions1 =
|
||||
heightDifference > 0
|
||||
? [
|
||||
Cesium.Cartographic.toCartesian(cartographic_0),
|
||||
Cesium.Cartographic.toCartesian(cartographic_1),
|
||||
]
|
||||
: [
|
||||
Cesium.Cartographic.toCartesian(cartographic_1),
|
||||
Cesium.Cartographic.toCartesian(cartographic_0),
|
||||
];
|
||||
//箭头线
|
||||
const instance = new Cesium.GeometryInstance({
|
||||
id: {
|
||||
type: "SlopeAspect",
|
||||
value: curSlope,
|
||||
},
|
||||
geometry: new Cesium.GroundPolylineGeometry({
|
||||
positions: positions1,
|
||||
width: this.arrowWidth,
|
||||
}),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.BLUE.withAlpha(0.6)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
},
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
calculateSlope(ellipseResults) {
|
||||
let instances = [];
|
||||
let polygonInstance = [];
|
||||
for (let index = 0; index < ellipseResults.length; index++) {
|
||||
const ellipse = ellipseResults[index];
|
||||
|
||||
const center = ellipse[0];
|
||||
let heightDifference = 0;
|
||||
let maxIndex = 0;
|
||||
for (let i = 1; i < ellipse.length - 1; i++) {
|
||||
const point = ellipse[i];
|
||||
let curHD = point.height - center.height;
|
||||
if (Math.abs(curHD) > heightDifference) {
|
||||
heightDifference = curHD;
|
||||
maxIndex = i;
|
||||
}
|
||||
}
|
||||
let pos0 = new Cesium.Cartographic(center.longitude, center.latitude, 0);
|
||||
let pos1 = new Cesium.Cartographic(
|
||||
ellipse[maxIndex].longitude,
|
||||
ellipse[maxIndex].latitude,
|
||||
0
|
||||
);
|
||||
let distance = Cesium.Cartesian3.distance(
|
||||
Cesium.Cartographic.toCartesian(pos0),
|
||||
Cesium.Cartographic.toCartesian(pos1)
|
||||
);
|
||||
let curSlope = Math.abs(heightDifference / distance); //坡度的tan值
|
||||
let curColor = this.calculateSlopeColor(curSlope, 0.4);
|
||||
const curPolygonInstance = this.createPolygonInsrance(ellipse, curColor, curSlope);
|
||||
polygonInstance.push(curPolygonInstance);
|
||||
|
||||
let diagonalPoint =
|
||||
maxIndex > 4 ? ellipse[maxIndex - 4] : ellipse[maxIndex + 4]; //对角点
|
||||
let targetPoint = ellipse[maxIndex];
|
||||
const arrowInstance = this.createArrowInstance(
|
||||
targetPoint,
|
||||
center,
|
||||
diagonalPoint,
|
||||
heightDifference,
|
||||
curSlope
|
||||
);
|
||||
instances.push(arrowInstance);
|
||||
}
|
||||
const mapPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.GroundPrimitive({
|
||||
geometryInstances: polygonInstance,
|
||||
appearance: new Cesium.PerInstanceColorAppearance({
|
||||
translucent: true, //false时透明度无效
|
||||
closed: false,
|
||||
}),
|
||||
})
|
||||
);
|
||||
const arrowPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.GroundPolylinePrimitive({
|
||||
geometryInstances: instances,
|
||||
appearance: new Cesium.PolylineMaterialAppearance({
|
||||
material: new Cesium.Material({
|
||||
fabric: {
|
||||
type: "PolylineArrow",
|
||||
uniforms: {
|
||||
color: new Cesium.Color(1.0, 1.0, 0.0, 0.8),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
this.result.push(arrowPrimitive, mapPrimitive);
|
||||
|
||||
this.event = new MouseEvent(this.sdk)
|
||||
let mouseEvent = (movement, cartesian) => {
|
||||
// console.log(movement, cartesian)
|
||||
let infoBox = document.getElementById('SlopeAspect-box')
|
||||
if (!infoBox) {
|
||||
infoBox = document.createElement('div')
|
||||
infoBox.id = 'SlopeAspect-box'
|
||||
infoBox.style.pointerEvents = 'none'
|
||||
infoBox.style.display = 'none'
|
||||
infoBox.style.position = 'absolute'
|
||||
infoBox.style.background = '#333333'
|
||||
infoBox.style.color = '#fff'
|
||||
infoBox.style.color = '#fff'
|
||||
infoBox.style.padding = '5px'
|
||||
infoBox.style.fontSize = '12px'
|
||||
infoBox.style.borderRadius = '5px'
|
||||
infoBox.style.transform = 'translate(-50%, -10px)'
|
||||
infoBox.innerHTML = `
|
||||
<div class="value">坡度:</div>
|
||||
<span style="
|
||||
position: absolute;
|
||||
border: 4px solid;
|
||||
border-color: #fff0 #fff0 #333333 #333333;
|
||||
transform: rotate(-45deg);
|
||||
left: calc(50% - 5px);
|
||||
"></span>
|
||||
`
|
||||
document.body.appendChild(infoBox)
|
||||
}
|
||||
let vlaElm = infoBox.getElementsByClassName('value')[0]
|
||||
let position = { ...movement.position }
|
||||
let pickedObject = this.sdk.viewer.scene.pick(position);
|
||||
if (pickedObject && pickedObject.id && pickedObject.id.type && pickedObject.id.type === "SlopeAspect") {
|
||||
let top = 0
|
||||
let left = 0
|
||||
|
||||
if (this.sdk.viewer && this.sdk.viewer._element) {
|
||||
let element = this.sdk.viewer._element.getElementsByClassName('cesium-widget')[0].getElementsByTagName('canvas')[0]
|
||||
top = element.getBoundingClientRect().top + window.scrollY
|
||||
left = element.getBoundingClientRect().left + window.scrollX
|
||||
}
|
||||
infoBox.style.display = 'block'
|
||||
infoBox.style.left = position.x + 2 + left + 'px'
|
||||
infoBox.style.top = position.y - 20 + top + 'px'
|
||||
vlaElm.innerHTML = '坡度:' + Number(Cesium.Math.toDegrees(pickedObject.id.value || 0).toFixed(2)) + '°'
|
||||
}
|
||||
else {
|
||||
infoBox.style.display = 'none'
|
||||
}
|
||||
}
|
||||
this.event.mouse_move((movement, cartesian) => {
|
||||
let newMovement = {
|
||||
position: { ...movement.endPosition }
|
||||
}
|
||||
mouseEvent(newMovement, cartesian)
|
||||
})
|
||||
this.event.mouse_left(mouseEvent)
|
||||
|
||||
this._camera = {
|
||||
position: this.sdk.viewer.camera.position,
|
||||
heading: this.sdk.viewer.camera.heading,
|
||||
pitch: this.sdk.viewer.camera.pitch,
|
||||
roll: this.sdk.viewer.camera.roll
|
||||
}
|
||||
this.sdk.viewer.scene.preRender.addEventListener(this._watchEvent, this)
|
||||
// this.sdk.viewer.clock.onTick.addEventListener(() => {
|
||||
// console.log(111111)
|
||||
// let infoBox = document.getElementById('SlopeAspect-box')
|
||||
// if(infoBox) {
|
||||
// infoBox.style.display = 'none'
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
_watchEvent() {
|
||||
if (
|
||||
this._camera.position.x.toFixed(8) !== this.sdk.viewer.camera.position.x.toFixed(8) ||
|
||||
this._camera.position.y.toFixed(8) !== this.sdk.viewer.camera.position.y.toFixed(8) ||
|
||||
this._camera.position.z.toFixed(8) !== this.sdk.viewer.camera.position.z.toFixed(8) ||
|
||||
this._camera.heading.toFixed(8) !== this.sdk.viewer.camera.heading.toFixed(8) ||
|
||||
this._camera.pitch.toFixed(8) !== this.sdk.viewer.camera.pitch.toFixed(8) ||
|
||||
this._camera.roll.toFixed(8) !== this.sdk.viewer.camera.roll.toFixed(8)
|
||||
) {
|
||||
let infoBox = document.getElementById('SlopeAspect-box')
|
||||
if (infoBox) {
|
||||
infoBox.style.display = 'none'
|
||||
}
|
||||
}
|
||||
this._camera = {
|
||||
position: this.sdk.viewer.camera.position,
|
||||
heading: this.sdk.viewer.camera.heading,
|
||||
pitch: this.sdk.viewer.camera.pitch,
|
||||
roll: this.sdk.viewer.camera.roll
|
||||
}
|
||||
}
|
||||
|
||||
//根据坡度值赋值颜色
|
||||
calculateSlopeColor(value, alpha) {
|
||||
// 0°~0.5°为平原0.00872686779075879,rgb(85,182,43)
|
||||
// 0.5°~2°为微斜坡0.03492076949174773,rgb(135,211,43)
|
||||
// 2°~5°为缓斜坡0.08748866352592401,rgb(204,244,44)
|
||||
// 5°~15°为斜坡0.2679491924311227,rgb(245,233,44)
|
||||
// 15°~35°为陡坡0.7002075382097097,rgb(255,138,43)
|
||||
// 35°~55°为峭坡1.4281480067421144,rgb(255,84,43)
|
||||
// 55°~90°为垂直壁,rgb(255,32,43)
|
||||
if (value < 0.00872686779075879) {
|
||||
return "rgba(85,182,43," + alpha + ")";
|
||||
} else if (value < 0.03492076949174773) {
|
||||
return "rgba(135,211,43," + alpha + ")";
|
||||
} else if (value < 0.08748866352592401) {
|
||||
return "rgba(204,244,44," + alpha + ")";
|
||||
} else if (value < 0.2679491924311227) {
|
||||
return "rgba(245,233,44," + alpha + ")";
|
||||
} else if (value < 0.7002075382097097) {
|
||||
return "rgba(255,138,43," + alpha + ")";
|
||||
} else if (value < 1.4281480067421144) {
|
||||
return "rgba(255,84,43," + alpha + ")";
|
||||
} else {
|
||||
return "rgba(255,32,43," + alpha + ")";
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.result && this.result.forEach((element) => {
|
||||
this.viewer.scene.primitives.remove(element);
|
||||
});
|
||||
this.result = [];
|
||||
this.sdk.viewer.scene.preRender.removeEventListener(this._watchEvent, this)
|
||||
}
|
||||
}
|
||||
export default SlopeAspect;
|
||||
92
src/Obj/Analysis/Submerge/_element.js
Normal file
92
src/Obj/Analysis/Submerge/_element.js
Normal file
@ -0,0 +1,92 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">水量</span>
|
||||
<div class="input-number input-number-unit-3">
|
||||
<input class="input" type="number" title="" name="waterVolume">
|
||||
<span class="unit">m³</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">最小水位</span>
|
||||
<div class="input-number input-number-unit-3">
|
||||
<input class="input" type="number" title="" name="minWaterLevel">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">水面面积</span>
|
||||
<div class="input-number input-number-unit-3">
|
||||
<input class="input area" type="number" readonly="readonly" type="text">
|
||||
<span class="unit">㎡</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">最大水位</span>
|
||||
<div class="input-number input-number-unit-3">
|
||||
<input class="input" type="number" title="" name="maxWaterLevel">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row subtitle-box">
|
||||
<span class="subtitle">上升速度</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<input type="range" max="50" min="0" step="0.01" name="risingSpeed">
|
||||
<div class="input-number input-number-unit-3" style="flex: 0 0 110px;margin-left: 10px;">
|
||||
<input class="input" type="number" title="" name="risingSpeed">
|
||||
<span class="unit">m/s</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col operate-btn-box">
|
||||
<button class="draw"><svg class="icon-draw"><use xlink:href="#yj-icon-draw"></use></svg>绘制范围</button>
|
||||
<button class="flyto"><svg class="icon-positions"><use xlink:href="#yj-icon-positions"></use></svg>定位</button>
|
||||
<button class="reset"><svg class="icon-reset"><use xlink:href="#yj-icon-reset"></use></svg>重置</button>
|
||||
<button class="analog"><svg class="icon-play"><use xlink:href="#yj-icon-play"></use></svg>开始模拟</button>
|
||||
<button class="pause" style="margin-right: 0px;"><svg class="icon-pause"><use xlink:href="#yj-icon-pause"></use></svg>暂停</button>
|
||||
<button class="start" style="display: none;margin-right: 0px;"><svg class="icon-play"><use xlink:href="#yj-icon-play"></use></svg>播放</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table">
|
||||
<div class="table-head">
|
||||
<div class="tr">
|
||||
<div class="th">序号</div>
|
||||
<div class="th">经度</div>
|
||||
<div class="th">纬度</div>
|
||||
<div class="th">高程</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
</div>
|
||||
<div class="table-empty">
|
||||
<div class="empty-img"></div>
|
||||
<p>暂无数据</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider" style="margin-top: 20px;"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
484
src/Obj/Analysis/Submerge/index.js
Normal file
484
src/Obj/Analysis/Submerge/index.js
Normal file
@ -0,0 +1,484 @@
|
||||
import Dialog from '../../../BaseDialog';
|
||||
import { html } from "./_element";
|
||||
import DrawPolygon from "../../../Draw/drawPolygon"
|
||||
import Tools from "../../../Tools";
|
||||
import { closeRotateAround, closeViewFollow} from '../../../Global/global'
|
||||
class Submerge extends Tools {
|
||||
/**
|
||||
* @constructor
|
||||
* @param sdk
|
||||
* @description 淹没效果
|
||||
* */
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
super(sdk, options);
|
||||
this.sdk = sdk
|
||||
this.options = {}
|
||||
this.options.name = options.name
|
||||
this.options.risingSpeed = 1
|
||||
this.options.minWaterLevel = 0
|
||||
this.options.maxWaterLevel = 0
|
||||
this.options.waterVolume = 0
|
||||
this.currentWaterLaver
|
||||
this.color = '#00d9ff66'
|
||||
this.Dialog = _Dialog
|
||||
this.Draw = new DrawPolygon(this.sdk)
|
||||
this.positions
|
||||
this.status = true
|
||||
this.area = 0
|
||||
this._elms = {};
|
||||
YJ.Analysis.AnalysesResults.push(this)
|
||||
Submerge.EditBox(this)
|
||||
// Submerge.create(this)
|
||||
}
|
||||
|
||||
static create(that) {
|
||||
that.Draw.start((a, positions) => {
|
||||
if (!positions || positions.length < 3) {
|
||||
let _error = '至少需要三个坐标!'
|
||||
console.warn(_error)
|
||||
window.ELEMENT &&
|
||||
window.ELEMENT.Message({
|
||||
message: _error,
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
})
|
||||
return
|
||||
}
|
||||
that.destroy()
|
||||
if (!positions || positions.length == 0) {
|
||||
that.positions = []
|
||||
that._positions = []
|
||||
that.options.minWaterLevel = 0
|
||||
that.options.maxWaterLevel = 0
|
||||
that.options.waterVolume = 0
|
||||
that.area = 0
|
||||
return
|
||||
}
|
||||
let fromDegreesArray = []
|
||||
that.positions = positions
|
||||
that._positions = positions
|
||||
that.options.minWaterLevel = positions[0].alt
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
if (that.options.minWaterLevel > positions[i].alt) {
|
||||
that.options.minWaterLevel = positions[i].alt
|
||||
}
|
||||
fromDegreesArray.push(positions[i].lng, positions[i].lat)
|
||||
}
|
||||
// for (let i = 0; i < positions.length; i++) {
|
||||
// fromDegreesArray.push(positions[i].lng, positions[i].lat, that.options.minWaterLevel)
|
||||
// }
|
||||
let pos = Cesium.Cartesian3.fromDegreesArray(fromDegreesArray)
|
||||
that.currentWaterLaver = that.options.minWaterLevel
|
||||
that.entity = that.sdk.viewer.entities.add({
|
||||
polygon: {
|
||||
hierarchy: new Cesium.PolygonHierarchy(pos),
|
||||
height: new Cesium.CallbackProperty(function () {
|
||||
return that.options.minWaterLevel
|
||||
}, false),
|
||||
extrudedHeight: new Cesium.CallbackProperty(function () {
|
||||
return that.currentWaterLaver
|
||||
}, false),
|
||||
material: Cesium.Color.fromCssColorString(that.color),
|
||||
},
|
||||
})
|
||||
that.area = that.computeArea(positions)
|
||||
if (that.TweenAnimate) {
|
||||
TWEEN.remove(that.TweenAnimate)
|
||||
that.TweenAnimate = null
|
||||
}
|
||||
let contentElm = that._DialogObject._element.body
|
||||
let pauseBtn = contentElm.getElementsByClassName('pause')[0];
|
||||
let startBtn = contentElm.getElementsByClassName('start')[0];
|
||||
startBtn.style.display = 'flex'
|
||||
pauseBtn.style.display = 'none'
|
||||
// that.move()
|
||||
// Submerge.EditBox(that)
|
||||
})
|
||||
}
|
||||
|
||||
static async EditBox(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.destroy()
|
||||
that.Dialog.closeCallBack && that.Dialog.closeCallBack()
|
||||
},
|
||||
})
|
||||
await that._DialogObject.init()
|
||||
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' submerge'
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
that._DialogObject.contentAppChild(contentElm)
|
||||
let stopBtn = document.createElement('button');
|
||||
stopBtn.className = 'el-button'
|
||||
stopBtn.innerHTML = '暂停'
|
||||
stopBtn.style.width = '80px'
|
||||
|
||||
let drawBtn = contentElm.getElementsByClassName('draw')[0]
|
||||
drawBtn.addEventListener('click', () => {
|
||||
Submerge.create(that)
|
||||
})
|
||||
let analogBtn = contentElm.getElementsByClassName('analog')[0];
|
||||
analogBtn.addEventListener('click', () => {
|
||||
that.move()
|
||||
})
|
||||
let flytoBtn = contentElm.getElementsByClassName('flyto')[0];
|
||||
flytoBtn.addEventListener('click', () => {
|
||||
that.flyTo()
|
||||
})
|
||||
let resetBtn = contentElm.getElementsByClassName('reset')[0];
|
||||
resetBtn.addEventListener('click', () => {
|
||||
that.restart()
|
||||
})
|
||||
let pauseBtn = contentElm.getElementsByClassName('pause')[0];
|
||||
let startBtn = contentElm.getElementsByClassName('start')[0];
|
||||
pauseBtn.addEventListener('click', () => {
|
||||
that.pause()
|
||||
pauseBtn.style.display = 'none'
|
||||
startBtn.style.display = 'flex'
|
||||
})
|
||||
startBtn.addEventListener('click', () => {
|
||||
that.start()
|
||||
startBtn.style.display = 'none'
|
||||
pauseBtn.style.display = 'flex'
|
||||
})
|
||||
|
||||
// that._DialogObject.footAppChild(stopBtn)
|
||||
// that._DialogObject.footAppChild(resetBtn)
|
||||
// that._DialogObject.footAppChild(flytoBtn)
|
||||
// that._DialogObject.footAppChild(analogBtn)
|
||||
// that._DialogObject.footAppChild(drawBtn)
|
||||
|
||||
// 速度
|
||||
let e_risingSpeed = contentElm.querySelectorAll("input[name='risingSpeed']")
|
||||
e_risingSpeed[0].value = that.options.risingSpeed
|
||||
e_risingSpeed[1].value = that.options.risingSpeed
|
||||
e_risingSpeed[0].addEventListener('input', e => {
|
||||
that.options.risingSpeed = Number(e.target.value);
|
||||
});
|
||||
e_risingSpeed[1].addEventListener('input', e => {
|
||||
if (e.data != '.') {
|
||||
let value = Number(e.target.value)
|
||||
let max = Number(e_risingSpeed[0].max)
|
||||
let min = Number(e_risingSpeed[0].min)
|
||||
if (value > max) {
|
||||
that.options.risingSpeed = max;
|
||||
}
|
||||
else if (value < min) {
|
||||
that.options.risingSpeed = min;
|
||||
}
|
||||
else {
|
||||
that.options.risingSpeed = Math.floor(value * 100) / 100;
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(that.options, 'risingSpeed', {
|
||||
get() {
|
||||
return e_risingSpeed[0].value
|
||||
},
|
||||
set(value) {
|
||||
e_risingSpeed[0].value = value
|
||||
e_risingSpeed[1].value = value
|
||||
}
|
||||
})
|
||||
that.waterLevel = that.options.maxWaterLevel - that.options.minWaterLevel
|
||||
// 最低水位
|
||||
let e_minWaterLevel = contentElm.querySelector("input[name='minWaterLevel']")
|
||||
e_minWaterLevel.value = that.options.minWaterLevel
|
||||
e_minWaterLevel.addEventListener('input', e => {
|
||||
if (e.data != '.') {
|
||||
let value = Number(e.target.value)
|
||||
if (value > 999999999) {
|
||||
value = 999999999
|
||||
}
|
||||
if (value < 0) {
|
||||
value = 0
|
||||
}
|
||||
that.options.minWaterLevel = Math.floor(value * 10000) / 10000;
|
||||
that.options.maxWaterLevel = that.options.minWaterLevel + that.waterLevel;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(that.options, 'minWaterLevel', {
|
||||
get() {
|
||||
return Number(e_minWaterLevel.value)
|
||||
},
|
||||
set(value) {
|
||||
e_minWaterLevel.value = Math.floor(Number(value) * 10000) / 10000;
|
||||
}
|
||||
})
|
||||
|
||||
// 最高水位
|
||||
let e_maxWaterLevel = contentElm.querySelector("input[name='maxWaterLevel']")
|
||||
e_maxWaterLevel.value = that.options.maxWaterLevel
|
||||
e_maxWaterLevel.addEventListener('input', e => {
|
||||
if (e.data != '.') {
|
||||
let value = Number(e.target.value)
|
||||
if (value > 999999999) {
|
||||
value = 999999999
|
||||
}
|
||||
if (value < 0) {
|
||||
value = 0
|
||||
}
|
||||
if (value < that.options.minWaterLevel) {
|
||||
that.options.maxWaterLevel = that.options.minWaterLevel;
|
||||
}
|
||||
else {
|
||||
that.options.maxWaterLevel = Math.floor(value * 10000) / 10000;
|
||||
}
|
||||
that.waterLevel = that.options.maxWaterLevel - that.options.minWaterLevel
|
||||
that.options.waterVolume = Number((that.waterLevel * that.area).toFixed(4))
|
||||
}
|
||||
});
|
||||
Object.defineProperty(that.options, 'maxWaterLevel', {
|
||||
get() {
|
||||
return Number(e_maxWaterLevel.value)
|
||||
},
|
||||
set(value) {
|
||||
if (isNaN(value)) {
|
||||
value = 0
|
||||
}
|
||||
e_maxWaterLevel.value = Math.floor(Number(value) * 10000) / 10000;
|
||||
}
|
||||
})
|
||||
|
||||
// 水量
|
||||
let e_waterVolume = contentElm.querySelector("input[name='waterVolume']")
|
||||
e_waterVolume.value = that.options.waterVolume
|
||||
e_waterVolume.addEventListener('input', e => {
|
||||
if (e.data != '.') {
|
||||
let value = Number(e.target.value)
|
||||
if (value > 99999999999999) {
|
||||
value = 99999999999999
|
||||
}
|
||||
if (value < 0) {
|
||||
value = 0
|
||||
}
|
||||
that.options.waterVolume = Math.floor(value * 10000) / 10000;
|
||||
if (that.area) {
|
||||
that.waterLevel = Number((that.options.waterVolume / that.area).toFixed(4))
|
||||
that.options.maxWaterLevel = that.options.minWaterLevel + that.waterLevel
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(that.options, 'waterVolume', {
|
||||
get() {
|
||||
return Number(e_waterVolume.value)
|
||||
},
|
||||
set(value) {
|
||||
e_waterVolume.value = value
|
||||
}
|
||||
})
|
||||
|
||||
// 面积
|
||||
let e_area = contentElm.getElementsByClassName('area')[0]
|
||||
e_area.value = that.area
|
||||
Object.defineProperty(that, 'area', {
|
||||
get() {
|
||||
return Number(e_area.value)
|
||||
},
|
||||
set(value) {
|
||||
e_area.value = value
|
||||
that.waterLevel = Number((that.options.waterVolume / that.area).toFixed(4))
|
||||
that.options.maxWaterLevel = that.options.minWaterLevel + that.waterLevel
|
||||
}
|
||||
})
|
||||
// 表格
|
||||
let e_tableBody = contentElm.getElementsByClassName('table-body')[0]
|
||||
let e_tableEmpty = contentElm.getElementsByClassName('table-empty')[0]
|
||||
Object.defineProperty(that, 'positions', {
|
||||
get() {
|
||||
return that._positions
|
||||
},
|
||||
set(value) {
|
||||
if (value && value.length > 0) {
|
||||
e_tableEmpty.style.display = 'none'
|
||||
let tr = ''
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
tr = tr + `<div class="tr">
|
||||
<div class="td">${i + 1}</div>
|
||||
<div class="td">${Number(value[i].lng.toFixed(10))}</div>
|
||||
<div class="td">${Number(value[i].lat.toFixed(10))}</div>
|
||||
<div class="td">${Number(value[i].alt.toFixed(4))}</div>
|
||||
</div>`
|
||||
}
|
||||
e_tableBody.innerHTML = tr
|
||||
}
|
||||
else {
|
||||
e_tableBody.innerHTML = ''
|
||||
e_tableEmpty.style.display = 'flex'
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
move() {
|
||||
if (this.TweenAnimate) {
|
||||
TWEEN.remove(this.TweenAnimate)
|
||||
}
|
||||
let totalTime = ((this.options.maxWaterLevel - this.options.minWaterLevel) / this.options.risingSpeed) * 1000
|
||||
this.TweenAnimate = new TWEEN.Tween({ waterLevel: this.options.minWaterLevel }).to({ waterLevel: this.options.maxWaterLevel }, totalTime).delay(this.delay).easing(TWEEN.Easing.Linear.None).onUpdate(async (r, a) => {
|
||||
this.currentWaterLaver = r.waterLevel
|
||||
}).start()
|
||||
let contentElm = this._DialogObject._element.body
|
||||
let pauseBtn = contentElm.getElementsByClassName('pause')[0];
|
||||
let startBtn = contentElm.getElementsByClassName('start')[0];
|
||||
startBtn.style.display = 'none'
|
||||
pauseBtn.style.display = 'flex'
|
||||
}
|
||||
|
||||
restart() {
|
||||
this.currentWaterLaver = this.options.minWaterLevel
|
||||
let isPaused = false
|
||||
if (this.TweenAnimate) {
|
||||
isPaused = this.TweenAnimate._isPaused
|
||||
TWEEN.remove(this.TweenAnimate)
|
||||
}
|
||||
let totalTime = ((this.options.maxWaterLevel - this.options.minWaterLevel) / this.options.risingSpeed) * 1000
|
||||
this.TweenAnimate = new TWEEN.Tween({ waterLevel: this.options.minWaterLevel }).to({ waterLevel: this.options.maxWaterLevel }, totalTime).delay(this.delay).easing(TWEEN.Easing.Linear.None).onUpdate(async (r, a) => {
|
||||
this.currentWaterLaver = r.waterLevel
|
||||
}).start()
|
||||
if (isPaused) {
|
||||
this.pause()
|
||||
}
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this.TweenAnimate) {
|
||||
this.TweenAnimate.resume()
|
||||
}
|
||||
}
|
||||
pause() {
|
||||
if (this.TweenAnimate) {
|
||||
this.TweenAnimate.pause()
|
||||
}
|
||||
}
|
||||
|
||||
calculateVolumeHeight() {
|
||||
that.options.maxWaterLevel
|
||||
}
|
||||
|
||||
/**
|
||||
* 飞到
|
||||
*/
|
||||
flyTo() {
|
||||
if (!this.positions || this.positions.length === 0) {
|
||||
return
|
||||
}
|
||||
closeRotateAround(this.sdk)
|
||||
closeViewFollow(this.sdk)
|
||||
|
||||
let positionArray = []
|
||||
for (let i = 0; i < this.positions.length; i++) {
|
||||
let fromDegrees = Cesium.Cartesian3.fromDegrees(this.positions[i].lng, this.positions[i].lat, this.options.maxWaterLevel)
|
||||
positionArray.push(fromDegrees.x, fromDegrees.y, fromDegrees.z)
|
||||
}
|
||||
let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray)
|
||||
this.sdk.viewer.camera.flyToBoundingSphere(BoundingSphere, {
|
||||
offset: {
|
||||
heading: Cesium.Math.toRadians(0.0),
|
||||
pitch: Cesium.Math.toRadians(-90.0),
|
||||
roll: Cesium.Math.toRadians(0.0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.TweenAnimate) {
|
||||
TWEEN.remove(this.TweenAnimate)
|
||||
}
|
||||
this.Draw.end()
|
||||
this.sdk.viewer.entities.remove(this.entity)
|
||||
this.entity = null
|
||||
}
|
||||
|
||||
static EventBinding(that, elements) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let Event = []
|
||||
let isEvent = false
|
||||
let removeName = []
|
||||
if (!elements[i] || !elements[i].attributes) {
|
||||
continue;
|
||||
}
|
||||
for (let m of elements[i].attributes) {
|
||||
switch (m.name) {
|
||||
case '@model': {
|
||||
isEvent = true
|
||||
if (elements[i].type == 'checkbox') {
|
||||
Event.push((e) => { that[m.value] = e.target.checked })
|
||||
elements[i].checked = that[m.value]
|
||||
}
|
||||
else {
|
||||
Event.push((e) => {
|
||||
let value = e.target.value
|
||||
if (e.target.type == 'number') {
|
||||
value = Number(value)
|
||||
}
|
||||
that[m.value] = value
|
||||
})
|
||||
if (elements[i].nodeName == 'IMG') {
|
||||
elements[i].src = that[m.value]
|
||||
}
|
||||
else {
|
||||
elements[i].value = that[m.value]
|
||||
}
|
||||
}
|
||||
if (that._elms[m.value]) {
|
||||
that._elms[m.value].push(elements[i])
|
||||
}
|
||||
else {
|
||||
that._elms[m.value] = [elements[i]]
|
||||
}
|
||||
removeName.push(m.name)
|
||||
break;
|
||||
}
|
||||
case '@click': {
|
||||
elements[i].addEventListener('click', (e) => {
|
||||
if (typeof (that[m.value]) === 'function') {
|
||||
that[m.value](e)
|
||||
}
|
||||
});
|
||||
removeName.push(m.name)
|
||||
// elements[i].attributes.removeNamedItem(m.name)
|
||||
break;
|
||||
}
|
||||
case '@change': {
|
||||
isEvent = true
|
||||
Event.push((e) => {
|
||||
let value = e.target.value
|
||||
if (e.target.type == 'number' && value != '') {
|
||||
value = Number(value)
|
||||
e.target.value = value
|
||||
}
|
||||
if (typeof (that[m.value]) === 'function') {
|
||||
that[m.value](e, value)
|
||||
}
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
// elements[i].attributes[m] = undefined
|
||||
}
|
||||
for (let n = 0; n < removeName.length; n++) {
|
||||
elements[i].attributes.removeNamedItem(removeName[n])
|
||||
}
|
||||
|
||||
if (isEvent) {
|
||||
let ventType = 'input'
|
||||
if (elements[i].tagName != 'INPUT' || elements[i].type == 'checkbox') {
|
||||
ventType = 'change'
|
||||
}
|
||||
elements[i].addEventListener(ventType, (e) => {
|
||||
for (let t = 0; t < Event.length; t++) {
|
||||
Event[t](e)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Submerge
|
||||
32
src/Obj/Analysis/TerrainExcavation/_element.js
Normal file
32
src/Obj/Analysis/TerrainExcavation/_element.js
Normal file
@ -0,0 +1,32 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 70px;">挖掘高度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="5000000" name="height">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">绘制开挖区域</span>
|
||||
<button class="start-excavation"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>绘制</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">清除开挖区域</span>
|
||||
<button class="clean-excavation"><svg class="icon-close"><use xlink:href="#yj-icon-close"></use></svg>清除</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
459
src/Obj/Analysis/TerrainExcavation/index.js
Normal file
459
src/Obj/Analysis/TerrainExcavation/index.js
Normal file
@ -0,0 +1,459 @@
|
||||
import Tools from "../../../Tools";
|
||||
import DrawPolygon from "../../../Draw/drawPolygon"
|
||||
import Dialog from '../../../BaseDialog';
|
||||
import { html } from "./_element";
|
||||
|
||||
let ExcavationFaces = []
|
||||
class TerrainExcavation extends Tools {
|
||||
/**
|
||||
* @constructor
|
||||
* @description 地形开挖
|
||||
* @param sdk
|
||||
* */
|
||||
constructor(sdk, options = {}) {
|
||||
super(sdk)
|
||||
this.viewer = sdk.viewer
|
||||
this.options = options || {};
|
||||
this.options.height = (this.options.height || this.options.height === 0) ? this.options.height : 10;
|
||||
this.options.show = (this.options.show || this.options.show === false) ? this.options.show : true;
|
||||
this.bottomImg = this.getSourceRootPath() + '/img/excavationregion_top.jpg';
|
||||
this.wallImg = this.getSourceRootPath() + '/img/excavationregion_side.jpg';
|
||||
this.splitNum = Cesium.defaultValue(options.splitNum, 50);
|
||||
this.Draw = new DrawPolygon(this.sdk)
|
||||
this.bottomMaterial = Cesium.Material.fromType('Color', {
|
||||
color: Cesium.Color.fromAlpha(Cesium.Color.fromCssColorString('#735d4f'))
|
||||
})
|
||||
this.wallMaterial = Cesium.Material.fromType('Color', {
|
||||
color: Cesium.Color.fromAlpha(Cesium.Color.fromCssColorString('#976b4e'))
|
||||
})
|
||||
let imageBottom = new Image();
|
||||
let wallBottom = new Image();
|
||||
imageBottom.src = this.bottomImg;
|
||||
wallBottom.src = this.wallImg;
|
||||
imageBottom.crossOrigin = "Anonymous";
|
||||
wallBottom.crossOrigin = "Anonymous";
|
||||
imageBottom.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = imageBottom.width;
|
||||
canvas.height = imageBottom.height;
|
||||
const context = canvas.getContext('2d');
|
||||
context.drawImage(imageBottom, 0, 0, imageBottom.width, imageBottom.height);
|
||||
const base64 = canvas.toDataURL('image/jpg');
|
||||
this.bottomMaterial = new Cesium.Material({
|
||||
fabric: {
|
||||
type: "Image",
|
||||
uniforms: {
|
||||
image: base64,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (this.bottomSurface) {
|
||||
this.bottomSurface.appearance.material = this.bottomMaterial;
|
||||
}
|
||||
}
|
||||
wallBottom.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = wallBottom.width;
|
||||
canvas.height = wallBottom.height;
|
||||
const context = canvas.getContext('2d');
|
||||
context.drawImage(wallBottom, 0, 0, wallBottom.width, wallBottom.height);
|
||||
const base64 = canvas.toDataURL('image/jpg');
|
||||
this.wallMaterial = new Cesium.Material({
|
||||
fabric: {
|
||||
type: "Image",
|
||||
uniforms: {
|
||||
image: base64,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (this.wellWall) {
|
||||
this.wellWall.appearance.material = this.wallMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
this.init();
|
||||
}
|
||||
get show() {
|
||||
return this.options.show
|
||||
}
|
||||
set show(v) {
|
||||
this.options.show = v;
|
||||
this.switchExcavate(v);
|
||||
}
|
||||
get height() {
|
||||
return this.options.height
|
||||
}
|
||||
set height(v) {
|
||||
this.options.height = v;
|
||||
// this.updateExcavateDepth(v);
|
||||
}
|
||||
init() {
|
||||
TerrainExcavation.edit(this, true)
|
||||
}
|
||||
static async edit(that, state) {
|
||||
if (that._DialogObject && that._DialogObject.close) {
|
||||
that._DialogObject.close()
|
||||
that._DialogObject = null
|
||||
}
|
||||
if (state) {
|
||||
that._DialogObject = await new Dialog(that.sdk.viewer._container, {
|
||||
title: '地形开挖',
|
||||
closeCallBack: () => {
|
||||
},
|
||||
})
|
||||
await that._DialogObject.init()
|
||||
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' terrain-excavation'
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
that._DialogObject.contentAppChild(contentElm)
|
||||
|
||||
// 开始
|
||||
let e_start = contentElm.getElementsByClassName('start-excavation')[0]
|
||||
e_start.addEventListener('click', () => {
|
||||
that.startCreate()
|
||||
});
|
||||
// 清除
|
||||
let e_clean = contentElm.getElementsByClassName('clean-excavation')[0]
|
||||
e_clean.addEventListener('click', () => {
|
||||
that.clear()
|
||||
});
|
||||
|
||||
// 高度值
|
||||
let e_height = contentElm.querySelector("input[name='height']")
|
||||
e_height.value = that.height
|
||||
e_height.addEventListener('change', (e) => {
|
||||
let value = e.target.value
|
||||
value = Number(value)
|
||||
if (value < 0.01) {
|
||||
value = 0.01
|
||||
e.target.value = value
|
||||
that.height = value;
|
||||
}
|
||||
})
|
||||
e_height.addEventListener('blur', (e) => {
|
||||
let value = e.target.value
|
||||
value = Number(value)
|
||||
if ((e.target.max) && value > Number(e.target.max)) {
|
||||
value = Number(e.target.max)
|
||||
}
|
||||
if (value < 0.01) {
|
||||
value = 0.01
|
||||
}
|
||||
e.target.value = value
|
||||
that.height = value;
|
||||
});
|
||||
|
||||
} else {
|
||||
if (that._DialogObject && that._DialogObject.close) {
|
||||
that._DialogObject.close()
|
||||
that._DialogObject = null
|
||||
}
|
||||
}
|
||||
}
|
||||
startCreate() {
|
||||
this.Draw.start((e, positions) => {
|
||||
if (!positions || positions.length <= 2) {
|
||||
window.ELEMENT && window.ELEMENT.Message({
|
||||
message: '至少拥有三个坐标位置!',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
return
|
||||
}
|
||||
if(!this.isConvex(positions)) {
|
||||
window.ELEMENT && window.ELEMENT.Message({
|
||||
message: '不支持凹多边形',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
console.log('不支持凹多边形')
|
||||
return
|
||||
}
|
||||
this.updateData(positions)
|
||||
})
|
||||
}
|
||||
updateData(activePoints) {
|
||||
let viewer = this.viewer;
|
||||
this.clear();
|
||||
let clippingPlanesList = [];
|
||||
let array = []
|
||||
for (let i = 0; i < activePoints.length; i++) {
|
||||
array.push([activePoints[i].lng, activePoints[i].lat])
|
||||
}
|
||||
array.push([activePoints[0].lng, activePoints[0].lat])
|
||||
let clockwiseRing = turf.lineString(array);
|
||||
// 是否顺时针
|
||||
let boolDiff = turf.booleanClockwise(clockwiseRing);
|
||||
this.excavateMinHeight = 9999;
|
||||
for (let index = 0; index < activePoints.length; ++index) {
|
||||
let s = (index + 1) % activePoints.length;
|
||||
let position1 = Cesium.Cartesian3.fromDegrees(activePoints[index].lng, activePoints[index].lat, activePoints[index].alt)
|
||||
let position2 = Cesium.Cartesian3.fromDegrees(activePoints[s].lng, activePoints[s].lat, activePoints[s].alt)
|
||||
let curMidPoint = Cesium.Cartesian3.midpoint(
|
||||
position1,
|
||||
position2,
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
let cartographic = Cesium.Cartographic.fromCartesian(position1);
|
||||
let curHeight =
|
||||
viewer.scene.globe.getHeight(cartographic) || cartographic.height;
|
||||
if (curHeight < this.excavateMinHeight) {
|
||||
this.excavateMinHeight = curHeight;
|
||||
}
|
||||
let curMidPointNormal = Cesium.Cartesian3.normalize(
|
||||
curMidPoint,
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
let curMidPointDifference = boolDiff
|
||||
? Cesium.Cartesian3.subtract(
|
||||
position1,
|
||||
curMidPoint,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
: Cesium.Cartesian3.subtract(
|
||||
position2,
|
||||
curMidPoint,
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
curMidPointDifference = Cesium.Cartesian3.normalize(
|
||||
curMidPointDifference,
|
||||
curMidPointDifference
|
||||
);
|
||||
let curMidPointCross = Cesium.Cartesian3.cross(
|
||||
curMidPointDifference,
|
||||
curMidPointNormal,
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
curMidPointCross = Cesium.Cartesian3.normalize(
|
||||
curMidPointCross,
|
||||
curMidPointCross
|
||||
);
|
||||
let plane = new Cesium.Plane(curMidPointCross, 0);
|
||||
let distance = Cesium.Plane.getPointDistance(plane, curMidPoint);
|
||||
clippingPlanesList.push(
|
||||
new Cesium.ClippingPlane(curMidPointCross, distance)
|
||||
);
|
||||
}
|
||||
this.viewer.scene.globe.clippingPlanes = new Cesium.ClippingPlaneCollection(
|
||||
{
|
||||
planes: clippingPlanesList,
|
||||
edgeWidth: 1,
|
||||
edgeColor: Cesium.Color.WHITE,
|
||||
enabled: true,
|
||||
}
|
||||
);
|
||||
this.prepareWell(activePoints);
|
||||
this.createWell(this.wellData);
|
||||
// this.viewer.entities.remove(this.drawGeomtry);
|
||||
}
|
||||
|
||||
clear() {
|
||||
if (this.viewer.scene.globe.clippingPlanes) {
|
||||
this.viewer.scene.globe.clippingPlanes.removeAll();
|
||||
this.viewer.scene.primitives.remove(this.bottomSurface);
|
||||
this.viewer.scene.primitives.remove(this.wellWall);
|
||||
this.viewer.scene.render();
|
||||
}
|
||||
for (let i = ExcavationFaces.length - 1; i >= 0; i--) {
|
||||
this.viewer.scene.primitives.remove(ExcavationFaces[i]);
|
||||
}
|
||||
ExcavationFaces = []
|
||||
this.Draw && this.Draw.end()
|
||||
}
|
||||
destroy() {
|
||||
this.clear()
|
||||
}
|
||||
//计算并更新wellData
|
||||
prepareWell(activePoints) {
|
||||
let pointLength = activePoints.length;
|
||||
let heightDiff = this.excavateMinHeight - this.height;
|
||||
let no_height_top = [],
|
||||
bottom_pos = [],
|
||||
lerp_pos = [];
|
||||
for (let l = 0; l < pointLength; l++) {
|
||||
let u = l == pointLength - 1 ? 0 : l + 1;
|
||||
let point0 = [
|
||||
Cesium.Cartographic.fromDegrees(activePoints[l].lng, activePoints[l].lat, activePoints[l].alt).longitude,
|
||||
Cesium.Cartographic.fromDegrees(activePoints[l].lng, activePoints[l].lat, activePoints[l].alt).latitude,
|
||||
];
|
||||
let point1 = [
|
||||
Cesium.Cartographic.fromDegrees(activePoints[u].lng, activePoints[u].lat, activePoints[u].alt).longitude,
|
||||
Cesium.Cartographic.fromDegrees(activePoints[u].lng, activePoints[u].lat, activePoints[u].alt).latitude,
|
||||
];
|
||||
if (0 == l) {
|
||||
lerp_pos.push(new Cesium.Cartographic(point0[0], point0[1]));
|
||||
bottom_pos.push(
|
||||
Cesium.Cartesian3.fromRadians(point0[0], point0[1], heightDiff)
|
||||
);
|
||||
no_height_top.push(
|
||||
Cesium.Cartesian3.fromRadians(point0[0], point0[1], 0)
|
||||
);
|
||||
}
|
||||
for (let p = 1; p <= this.splitNum; p++) {
|
||||
let m = Cesium.Math.lerp(point0[0], point1[0], p / this.splitNum);
|
||||
let g = Cesium.Math.lerp(point0[1], point1[1], p / this.splitNum);
|
||||
(l == pointLength - 1 && p == this.splitNum) ||
|
||||
(lerp_pos.push(new Cesium.Cartographic(m, g)),
|
||||
bottom_pos.push(Cesium.Cartesian3.fromRadians(m, g, heightDiff)),
|
||||
no_height_top.push(Cesium.Cartesian3.fromRadians(m, g, 0)));
|
||||
}
|
||||
}
|
||||
this.wellData = {
|
||||
lerp_pos: lerp_pos,
|
||||
bottom_pos: bottom_pos,
|
||||
no_height_top: no_height_top,
|
||||
};
|
||||
}
|
||||
//开始创建底面和侧面
|
||||
createWell(wallData) {
|
||||
let $this = this;
|
||||
if (this.viewer.terrainProvider._layers) {
|
||||
this.createBottomSurface(wallData.bottom_pos);
|
||||
let positions = Cesium.sampleTerrainMostDetailed(
|
||||
this.viewer.terrainProvider,
|
||||
wallData.lerp_pos
|
||||
);
|
||||
positions.then((pos) => {
|
||||
let positionList = [];
|
||||
for (let index = 0; index < pos.length; index++) {
|
||||
const element = pos[index];
|
||||
let curPos = Cesium.Cartesian3.fromRadians(
|
||||
element.longitude,
|
||||
element.latitude,
|
||||
element.height
|
||||
);
|
||||
positionList.push(curPos);
|
||||
}
|
||||
$this.createWellWall(wallData.bottom_pos, positionList);
|
||||
});
|
||||
} else {
|
||||
this.createBottomSurface(wallData.bottom_pos);
|
||||
this.createWellWall(wallData.bottom_pos, wallData.no_height_top);
|
||||
}
|
||||
}
|
||||
//坐标转换,转出经纬度格式
|
||||
ellipsoidToDegree(pos) {
|
||||
let cartesian3 = new Cesium.Cartesian3(pos.x, pos.y, pos.z);
|
||||
let cartographic =
|
||||
this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian3);
|
||||
return {
|
||||
longitude: Cesium.Math.toDegrees(cartographic.longitude),
|
||||
latitude: Cesium.Math.toDegrees(cartographic.latitude),
|
||||
altitude: cartographic.height,
|
||||
};
|
||||
}
|
||||
//创建地形开挖的底面对象
|
||||
createBottomSurface(points) {
|
||||
if (points.length) {
|
||||
let minHeight = this.getMinHeight(points);
|
||||
let positions = [];
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
let curPoint = this.ellipsoidToDegree(points[i]);
|
||||
positions.push(curPoint.longitude, curPoint.latitude, minHeight);
|
||||
}
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
Cesium.Cartesian3.fromDegreesArrayHeights(positions)
|
||||
),
|
||||
perPositionHeight: true,
|
||||
});
|
||||
|
||||
let appearance = new Cesium.MaterialAppearance({
|
||||
translucent: false,
|
||||
flat: true,
|
||||
material: this.bottomMaterial,
|
||||
});
|
||||
this.bottomSurface = new Cesium.Primitive({
|
||||
geometryInstances: new Cesium.GeometryInstance({
|
||||
geometry: Cesium.PolygonGeometry.createGeometry(polygon),
|
||||
}),
|
||||
appearance: appearance,
|
||||
asynchronous: false,
|
||||
});
|
||||
ExcavationFaces.push(this.bottomSurface);
|
||||
this.viewer.scene.primitives.add(this.bottomSurface);
|
||||
}
|
||||
}
|
||||
// 创建地形开挖的侧面墙对象
|
||||
createWellWall(bottomPos, positionList) {
|
||||
let minHeight = this.getMinHeight(bottomPos);
|
||||
let maxHeights = [],
|
||||
minHeights = [];
|
||||
for (let i = 0; i < positionList.length; i++) {
|
||||
maxHeights.push(this.ellipsoidToDegree(positionList[i]).altitude);
|
||||
minHeights.push(minHeight);
|
||||
}
|
||||
let wall = new Cesium.WallGeometry({
|
||||
positions: positionList,
|
||||
maximumHeights: maxHeights,
|
||||
minimumHeights: minHeights,
|
||||
});
|
||||
let geometry = Cesium.WallGeometry.createGeometry(wall);
|
||||
let appearance = new Cesium.MaterialAppearance({
|
||||
translucent: false,
|
||||
flat: true,
|
||||
material: this.wallMaterial,
|
||||
});
|
||||
this.wellWall = new Cesium.Primitive({
|
||||
geometryInstances: new Cesium.GeometryInstance({
|
||||
geometry: geometry,
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.GREY
|
||||
),
|
||||
},
|
||||
id: "PitWall",
|
||||
}),
|
||||
appearance: appearance,
|
||||
asynchronous: false,
|
||||
});
|
||||
ExcavationFaces.push(this.wellWall);
|
||||
this.viewer.scene.primitives.add(this.wellWall);
|
||||
}
|
||||
//获取地形开挖最低点高程值
|
||||
getMinHeight(points) {
|
||||
let minHeight = 5000000;
|
||||
let minPoint = null;
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
let height = points[i]["z"];
|
||||
if (height < minHeight) {
|
||||
minHeight = height;
|
||||
minPoint = this.ellipsoidToDegree(points[i]);
|
||||
}
|
||||
}
|
||||
return minPoint.altitude;
|
||||
}
|
||||
switchExcavate(show) {
|
||||
if (show) {
|
||||
this.viewer.scene.globe.material = null;
|
||||
this.wellWall.show = true;
|
||||
this.bottomSurface.show = true;
|
||||
} else {
|
||||
this.viewer.scene.globe.material = null;
|
||||
this.wellWall.show = false;
|
||||
this.bottomSurface.show = false;
|
||||
}
|
||||
}
|
||||
|
||||
updateExcavateDepth(height) {
|
||||
this.viewer.scene.primitives.remove(this.bottomSurface);
|
||||
this.viewer.scene.primitives.remove(this.wellWall);
|
||||
if (!this.wellData) {
|
||||
return
|
||||
}
|
||||
let lerp_pos = this.wellData.lerp_pos;
|
||||
let posList = [];
|
||||
for (let n = 0; n < lerp_pos.length; n++) {
|
||||
posList.push(
|
||||
Cesium.Cartesian3.fromRadians(
|
||||
lerp_pos[n].longitude,
|
||||
lerp_pos[n].latitude,
|
||||
this.excavateMinHeight - height
|
||||
)
|
||||
);
|
||||
}
|
||||
this.wellData.bottom_pos = posList;
|
||||
this.createWell(this.wellData);
|
||||
}
|
||||
}
|
||||
export default TerrainExcavation;
|
||||
54
src/Obj/Analysis/ViewShed/_element.js
Normal file
54
src/Obj/Analysis/ViewShed/_element.js
Normal file
@ -0,0 +1,54 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">位置拾取(起点、终点)</span>
|
||||
<button class="edit"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>拾取</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row" style="margin-bottom: 25px;">
|
||||
<div class="col">
|
||||
<span class="label">视点高度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="999999" step="0.1" @model="viewPointHeight">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row subtitle-box">
|
||||
<span class="subtitle">视域夹角</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="range-box">
|
||||
<div class="range-bg">
|
||||
<div class="range-process-box">
|
||||
<div class="range-process"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-node-box">
|
||||
<span class="range-node-text">0°</span>
|
||||
<span class="range-node-text">45°</span>
|
||||
<span class="range-node-text">90°</span>
|
||||
<span class="range-node-text">135°</span>
|
||||
<span class="range-node-text">180°</span>
|
||||
<div class="range-node-active"><span class="range-node-active-text">0°</span></div>
|
||||
</div>
|
||||
<input type="range" max="180" min="0" step="1" name="horizontalViewAngle">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
511
src/Obj/Analysis/ViewShed/_index.js
Normal file
511
src/Obj/Analysis/ViewShed/_index.js
Normal file
@ -0,0 +1,511 @@
|
||||
/*
|
||||
* @Author: Wang jianLei
|
||||
* @Date: 2022-05-17 21:49:28
|
||||
* @Last Modified by: Wang JianLei
|
||||
* @Last Modified time: 2022-05-19 22:08:14
|
||||
*/
|
||||
|
||||
let ViewShed = function (sdk, canvasEleId) {
|
||||
if (!sdk.viewer) throw new Error("no viewer object!");
|
||||
let canvasEle = document.getElementById(canvasEleId);
|
||||
if (!canvasEle) throw new Error("the canvas element is not exist");
|
||||
this.canvasEle = canvasEle;
|
||||
this.viewer = sdk.viewer;
|
||||
this.handler = undefined;
|
||||
this.lightCamera;
|
||||
this.pyramid;
|
||||
this.frustumPrimitive;
|
||||
this.viewershedPolygon;
|
||||
};
|
||||
ViewShed.prototype = {
|
||||
/**
|
||||
* 初始化handler
|
||||
*/
|
||||
initHandler() {
|
||||
if (this.handler) {
|
||||
this.handler.destroy();
|
||||
this.handler = undefined;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 开始执行视域分析
|
||||
* @param {number} precision 精度,值越大创建耗时越长,建议在10~20之间
|
||||
*/
|
||||
createViewshed: function (precision) {
|
||||
let $this = this;
|
||||
let scene = $this.viewer.scene;
|
||||
$this.initHandler();
|
||||
$this.clearAll();
|
||||
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
|
||||
$this.handler.setInputAction((event) => {
|
||||
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
|
||||
scene.screenSpaceCameraController.enableRotate = false;
|
||||
scene.screenSpaceCameraController.enableZoom = false;
|
||||
scene.globe.depthTestAgainstTerrain = true;
|
||||
let earthPosition = scene.pickPosition(event.position);
|
||||
let pos = $this.cartesian3ToDegree(earthPosition);
|
||||
$this.handler.setInputAction(function (event) {
|
||||
let newPosition = scene.pickPosition(event.endPosition);
|
||||
if (Cesium.defined(newPosition)) {
|
||||
let pos1 = $this.cartesian3ToDegree(newPosition);
|
||||
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
|
||||
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
|
||||
let pitch = $this.getPitch(earthPosition, newPosition);
|
||||
$this.ViewShedOptions = {
|
||||
viewPosition: earthPosition, //观测点 笛卡尔坐标
|
||||
endPosition: newPosition, //目标点 笛卡尔坐标
|
||||
direction: angle, //观测方位角 默认为`0`,范围`0~360`
|
||||
pitch: pitch, //俯仰角,radius,默认为`0`
|
||||
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
|
||||
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
|
||||
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
|
||||
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
|
||||
visualRange: distance, //距离,单位`米`
|
||||
};
|
||||
$this.updateViewShed();
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
|
||||
|
||||
$this.handler.setInputAction(() => {
|
||||
$this.initHandler();
|
||||
// 开启地球旋转和缩放
|
||||
scene.screenSpaceCameraController.enableRotate = true;
|
||||
scene.screenSpaceCameraController.enableZoom = true;
|
||||
$this.drawViewershed(precision);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_UP);
|
||||
},
|
||||
|
||||
ReturnDistance(pos0, pos1) {
|
||||
let distance = 0;
|
||||
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
|
||||
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
|
||||
/**根据经纬度计算出距离**/
|
||||
let geodesic = new Cesium.EllipsoidGeodesic();
|
||||
geodesic.setEndPoints(point1cartographic, point2cartographic);
|
||||
let s = geodesic.surfaceDistance;
|
||||
return s;
|
||||
},
|
||||
getHeight(x, y, objectsToExclude) {
|
||||
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
|
||||
let endHeight = this.viewer.scene.sampleHeight(
|
||||
endCartographic,
|
||||
objectsToExclude
|
||||
);
|
||||
return endHeight;
|
||||
},
|
||||
|
||||
cartesian3ToDegree: function (Cartesian3) {
|
||||
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
|
||||
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
|
||||
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
|
||||
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
|
||||
let _alt = _cartographic.height;
|
||||
return [_lng, _lat, _alt];
|
||||
},
|
||||
getAngle: function (lng1, lat1, lng2, lat2) {
|
||||
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
|
||||
if (lng2 >= lng1) {
|
||||
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
|
||||
} else {
|
||||
dRotateAngle =
|
||||
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
|
||||
}
|
||||
dRotateAngle = (dRotateAngle * 180) / Math.PI;
|
||||
return dRotateAngle;
|
||||
},
|
||||
|
||||
getPitch(pointA, pointB) {
|
||||
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
|
||||
const vector = Cesium.Cartesian3.subtract(
|
||||
pointB,
|
||||
pointA,
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
let direction = Cesium.Matrix4.multiplyByPointAsVector(
|
||||
Cesium.Matrix4.inverse(transfrom, transfrom),
|
||||
vector,
|
||||
vector
|
||||
);
|
||||
Cesium.Cartesian3.normalize(direction, direction);
|
||||
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
|
||||
},
|
||||
|
||||
updateViewShed: function () {
|
||||
this.clear();
|
||||
this.setLightCamera();
|
||||
this.addVisualPyramid();
|
||||
this.createFrustum();
|
||||
},
|
||||
clear: function () {
|
||||
if (this.pyramid) {
|
||||
this.viewer.entities.removeById(this.pyramid.id);
|
||||
this.pyramid = undefined;
|
||||
}
|
||||
if (this.frustumPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.frustumPrimitive);
|
||||
this.frustumPrimitive = undefined;
|
||||
}
|
||||
if (this.debugModelMatrixPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
|
||||
this.debugModelMatrixPrimitive = undefined;
|
||||
}
|
||||
},
|
||||
clearAll: function () {
|
||||
this.clear();
|
||||
if (this.viewershedPolygon) {
|
||||
this.viewer.scene.primitives.remove(this.viewershedPolygon);
|
||||
this.viewershedPolygon = undefined;
|
||||
}
|
||||
},
|
||||
addVisualPyramid: function () {
|
||||
let options = this.ViewShedOptions;
|
||||
let position = options.viewPosition;
|
||||
let visualRange = Number(options.visualRange);
|
||||
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
|
||||
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.DebugModelMatrixPrimitive({
|
||||
modelMatrix: transform,
|
||||
length: 5.0,
|
||||
})
|
||||
);
|
||||
const halfClock = options.horizontalViewAngle / 2;
|
||||
const halfCone = options.verticalViewAngle / 2;
|
||||
const pitch = Cesium.Math.toDegrees(options.pitch);
|
||||
const ellipsoid = new Cesium.EllipsoidGraphics({
|
||||
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
|
||||
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
|
||||
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
|
||||
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
|
||||
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
|
||||
fill: false,
|
||||
outline: true,
|
||||
subdivisions: 256,
|
||||
stackPartitions: 64,
|
||||
slicePartitions: 64,
|
||||
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
|
||||
});
|
||||
const pyramidEntity = new Cesium.Entity({
|
||||
position: position,
|
||||
ellipsoid,
|
||||
});
|
||||
this.pyramid = this.viewer.entities.add(pyramidEntity);
|
||||
},
|
||||
setLightCamera: function () {
|
||||
if (!this.lightCamera) {
|
||||
this.lightCamera = new Cesium.Camera(this.viewer.scene);
|
||||
}
|
||||
let options = this.ViewShedOptions;
|
||||
let visualRange = Number(options.visualRange);
|
||||
this.lightCamera.position = options.viewPosition;
|
||||
this.lightCamera.frustum.near = 0.1;
|
||||
this.lightCamera.frustum.far = visualRange;
|
||||
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
|
||||
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
|
||||
this.lightCamera.frustum.aspectRatio =
|
||||
(visualRange * Math.tan(hr / 2) * 2) /
|
||||
(visualRange * Math.tan(vr / 2) * 2);
|
||||
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
|
||||
this.lightCamera.setView({
|
||||
destination: options.viewPosition,
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(options.direction || 0),
|
||||
pitch: options.pitch || 0,
|
||||
roll: 0,
|
||||
},
|
||||
});
|
||||
},
|
||||
createFrustum: function () {
|
||||
const scratchRight = new Cesium.Cartesian3();
|
||||
const scratchRotation = new Cesium.Matrix3();
|
||||
const scratchOrientation = new Cesium.Quaternion();
|
||||
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 instanceOutline = new Cesium.GeometryInstance({
|
||||
geometry: new Cesium.FrustumOutlineGeometry({
|
||||
frustum: this.lightCamera.frustum,
|
||||
origin: this.ViewShedOptions.viewPosition,
|
||||
orientation: orientation,
|
||||
}),
|
||||
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true),
|
||||
},
|
||||
});
|
||||
this.frustumPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.Primitive({
|
||||
geometryInstances: instanceOutline,
|
||||
appearance: new Cesium.PerInstanceColorAppearance({
|
||||
flat: true,
|
||||
translucent: false,
|
||||
closed: true,
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
createPoint: function (firstPos, secondPos) {
|
||||
let entity4FirstPos = new Cesium.Entity({
|
||||
name: "firstPos",
|
||||
show: true,
|
||||
position: firstPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 20,
|
||||
color: Cesium.Color.RED,
|
||||
outlineColor: Cesium.Color.YELLOW,
|
||||
outlineWidth: 5,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体起点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4FirstPos);
|
||||
let entity4SecondPos = new Cesium.Entity({
|
||||
name: "secondPos",
|
||||
show: true,
|
||||
position: secondPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 30,
|
||||
color: Cesium.Color.YELLOW,
|
||||
outlineColor: Cesium.Color.RED,
|
||||
outlineWidth: 8,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体视角终点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4SecondPos);
|
||||
},
|
||||
|
||||
//绘制可视域
|
||||
add(positionArr) {
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
Cesium.Cartesian3.fromDegreesArray(positionArr)
|
||||
),
|
||||
height: 0.0,
|
||||
extrudedHeight: 0.0,
|
||||
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
|
||||
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
|
||||
ellipsoid: Cesium.Ellipsoid.WGS84,
|
||||
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
|
||||
perPositionHeight: false, // 每个位置点使用的高度
|
||||
closeTop: true,
|
||||
closeBottom: true,
|
||||
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
|
||||
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
|
||||
});
|
||||
|
||||
let polygonInstance = new Cesium.GeometryInstance({
|
||||
geometry: polygon,
|
||||
name: "ViewershedPolygon",
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.BLUE.withAlpha(0.6)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
},
|
||||
});
|
||||
this.viewershedPolygon = this.viewer.scene.primitives.add(
|
||||
new Cesium.GroundPrimitive({
|
||||
geometryInstances: polygonInstance,
|
||||
appearance: new Cesium.EllipsoidSurfaceAppearance({
|
||||
aboveGround: true,
|
||||
material: new Cesium.Material({
|
||||
fabric: {
|
||||
type: "Image",
|
||||
uniforms: {
|
||||
image: this.returnImgae(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
drawViewershed(precision) {
|
||||
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
|
||||
const radius = this.ViewShedOptions.visualRange;
|
||||
const direction = this.ViewShedOptions.direction;
|
||||
let boundary = this.computeBoundaryOptions(pos, radius, direction);
|
||||
const bbox = boundary.bbox;
|
||||
let mask = turf.polygon([boundary.boundaryPoints]);
|
||||
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
|
||||
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
|
||||
|
||||
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
|
||||
let variogram = kriging.train(
|
||||
pointsResult.values,
|
||||
pointsResult.lngs,
|
||||
pointsResult.lats,
|
||||
"exponential",
|
||||
0,
|
||||
100
|
||||
);
|
||||
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
|
||||
const colors = [
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
];
|
||||
|
||||
this.canvasEle.width = 3840;
|
||||
this.canvasEle.height = 2160;
|
||||
kriging.plot(
|
||||
this.canvasEle,
|
||||
grid,
|
||||
[bbox[0], bbox[2]],
|
||||
[bbox[1], bbox[3]],
|
||||
colors
|
||||
);
|
||||
this.add(boundary.positionArr);
|
||||
},
|
||||
computeBoundaryOptions(pos, radius, angle) {
|
||||
let Ea = 6378137; // 赤道半径
|
||||
let Eb = 6356725; // 极半径
|
||||
const lng = pos[0],
|
||||
lat = pos[1];
|
||||
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
|
||||
let positionArr = [];
|
||||
let boundaryPoints = [];
|
||||
positionArr.push(lng, lat);
|
||||
boundaryPoints.push([lng, lat]);
|
||||
//正北是0°
|
||||
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
|
||||
let end = start + 90;
|
||||
for (let i = start; i <= end; i++) {
|
||||
let dx = radius * Math.sin((i * Math.PI) / 180.0);
|
||||
let dy = radius * Math.cos((i * Math.PI) / 180.0);
|
||||
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
|
||||
let ed = ec * Math.cos((lat * Math.PI) / 180);
|
||||
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
|
||||
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
|
||||
positionArr.push(BJD, BWD);
|
||||
boundaryPoints.push([BJD, BWD]);
|
||||
this.refreshBBox(bbox, BJD, BWD);
|
||||
}
|
||||
boundaryPoints.push([lng, lat]);
|
||||
return {
|
||||
positionArr,
|
||||
boundaryPoints,
|
||||
bbox,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 更新外围矩形 Bbox
|
||||
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
|
||||
* @param {Number} x 经度
|
||||
* @param {Number} y 纬度
|
||||
*/
|
||||
refreshBBox(result, x, y) {
|
||||
result[0] = x < result[0] ? x : result[0];
|
||||
result[1] = y < result[1] ? y : result[1];
|
||||
result[2] = x > result[2] ? x : result[2];
|
||||
result[3] = y > result[3] ? y : result[3];
|
||||
},
|
||||
/**
|
||||
* 插值点用射线判断通视性
|
||||
* @param {*} gridPoints 网格点
|
||||
* @param {*} step 步长,可以理解成是精度
|
||||
* @param {*} sourcePos 视域分析起点
|
||||
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
|
||||
*/
|
||||
createTargetPoints(gridPoints, step, sourcePos) {
|
||||
let positionArr = [];
|
||||
let objectsToExclude = [
|
||||
this.frustumPrimitive,
|
||||
this.pyramid,
|
||||
this.debugModelMatrixPrimitive,
|
||||
];
|
||||
let values = [],
|
||||
lngs = [],
|
||||
lats = [];
|
||||
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
|
||||
positionArr.push({
|
||||
x: sourcePos[0],
|
||||
y: sourcePos[1],
|
||||
z: height,
|
||||
});
|
||||
let viewPoint = this.ViewShedOptions.viewPosition;
|
||||
for (let index = 0; index < gridPoints.features.length; index++) {
|
||||
const feature = gridPoints.features[index];
|
||||
const coords = feature.geometry.coordinates;
|
||||
const x = coords[0],
|
||||
y = coords[1];
|
||||
let h = this.getHeight(x, y, objectsToExclude);
|
||||
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
|
||||
let direction = Cesium.Cartesian3.normalize(
|
||||
Cesium.Cartesian3.subtract(
|
||||
endPoint,
|
||||
viewPoint,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
// 建立射线
|
||||
let ray = new Cesium.Ray(viewPoint, direction);
|
||||
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
|
||||
if (result) {
|
||||
let buffer = this.ReturnDistance(endPoint, result.position);
|
||||
// let M_color = Cesium.Color.GREEN;
|
||||
if (buffer > step) {
|
||||
// M_color = Cesium.Color.RED;
|
||||
values.push(0);
|
||||
} else {
|
||||
values.push(1);
|
||||
}
|
||||
lngs.push(x);
|
||||
lats.push(y);
|
||||
// this.viewer.entities.add(
|
||||
// new Cesium.Entity({
|
||||
// name: "插值点哦",
|
||||
// show: true,
|
||||
// position: endPoint,
|
||||
// point: {
|
||||
// show: true,
|
||||
// pixelSize: 10,
|
||||
// color: M_color,
|
||||
// outlineWidth: 2,
|
||||
// outlineColor: Cesium.Color.YELLOW,
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
}
|
||||
}
|
||||
return {
|
||||
values,
|
||||
lngs,
|
||||
lats,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* canvas转image图片
|
||||
* @returns base64图片
|
||||
*/
|
||||
returnImgae() {
|
||||
return this.canvasEle.toDataURL("image/png");
|
||||
},
|
||||
};
|
||||
|
||||
export default ViewShed;
|
||||
131
src/Obj/Analysis/ViewShed/glsl.js
Normal file
131
src/Obj/Analysis/ViewShed/glsl.js
Normal file
@ -0,0 +1,131 @@
|
||||
export default `
|
||||
#define USE_CUBE_MAP_SHADOW true
|
||||
uniform sampler2D colorTexture;
|
||||
uniform sampler2D depthTexture;
|
||||
varying vec2 v_textureCoordinates;
|
||||
uniform mat4 camera_projection_matrix;
|
||||
uniform mat4 camera_view_matrix;
|
||||
uniform samplerCube shadowMap_textureCube;
|
||||
uniform mat4 shadowMap_matrix;
|
||||
uniform vec4 shadowMap_lightPositionEC;
|
||||
uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
|
||||
uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
|
||||
uniform float helsing_viewDistance;
|
||||
uniform vec4 helsing_visibleAreaColor;
|
||||
uniform vec4 helsing_invisibleAreaColor;
|
||||
|
||||
struct zx_shadowParameters
|
||||
{
|
||||
vec3 texCoords;
|
||||
float depthBias;
|
||||
float depth;
|
||||
float nDotL;
|
||||
vec2 texelStepSize;
|
||||
float normalShadingSmooth;
|
||||
float darkness;
|
||||
};
|
||||
|
||||
float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)
|
||||
{
|
||||
float depthBias = shadowParameters.depthBias;
|
||||
float depth = shadowParameters.depth;
|
||||
float nDotL = shadowParameters.nDotL;
|
||||
float normalShadingSmooth = shadowParameters.normalShadingSmooth;
|
||||
float darkness = shadowParameters.darkness;
|
||||
vec3 uvw = shadowParameters.texCoords;
|
||||
depth -= depthBias;
|
||||
float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);
|
||||
return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);
|
||||
}
|
||||
|
||||
vec4 getPositionEC(){
|
||||
return czm_windowToEyeCoordinates(gl_FragCoord);
|
||||
}
|
||||
|
||||
vec3 getNormalEC(){
|
||||
return vec3(1.);
|
||||
}
|
||||
|
||||
vec4 toEye(in vec2 uv,in float depth){
|
||||
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
|
||||
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
|
||||
posInCamera=posInCamera/posInCamera.w;
|
||||
return posInCamera;
|
||||
}
|
||||
|
||||
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
|
||||
vec3 v01=point-planeOrigin;
|
||||
float d=dot(planeNormal,v01);
|
||||
return(point-planeNormal*d);
|
||||
}
|
||||
|
||||
float getDepth(in vec4 depth){
|
||||
float z_window=czm_unpackDepth(depth);
|
||||
z_window=czm_reverseLogDepth(z_window);
|
||||
float n_range=czm_depthRange.near;
|
||||
float f_range=czm_depthRange.far;
|
||||
return(2.*z_window-n_range-f_range)/(f_range-n_range);
|
||||
}
|
||||
|
||||
float shadow(in vec4 positionEC){
|
||||
vec3 normalEC=getNormalEC();
|
||||
zx_shadowParameters shadowParameters;
|
||||
shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
|
||||
shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
|
||||
shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
|
||||
shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
|
||||
vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;
|
||||
float distance=length(directionEC);
|
||||
directionEC=normalize(directionEC);
|
||||
float radius=shadowMap_lightPositionEC.w;
|
||||
if(distance>radius)
|
||||
{
|
||||
return 2.0;
|
||||
}
|
||||
vec3 directionWC=czm_inverseViewRotation*directionEC;
|
||||
shadowParameters.depth=distance/radius-0.0003;
|
||||
shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.,1.);
|
||||
shadowParameters.texCoords=directionWC;
|
||||
float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);
|
||||
return visibility;
|
||||
}
|
||||
|
||||
bool visible(in vec4 result)
|
||||
{
|
||||
result.x/=result.w;
|
||||
result.y/=result.w;
|
||||
result.z/=result.w;
|
||||
return result.x>=-1.&&result.x<=1.
|
||||
&&result.y>=-1.&&result.y<=1.
|
||||
&&result.z>=-1.&&result.z<=1.;
|
||||
}
|
||||
|
||||
void main(){
|
||||
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
|
||||
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
|
||||
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
|
||||
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
|
||||
// 视角 = (纹理坐标, 深度)
|
||||
vec4 viewPos = toEye(v_textureCoordinates, depth);
|
||||
// 世界坐标
|
||||
vec4 wordPos = czm_inverseView * viewPos;
|
||||
// 虚拟相机中坐标
|
||||
vec4 vcPos = camera_view_matrix * wordPos;
|
||||
float near = .001 * helsing_viewDistance;
|
||||
float dis = length(vcPos.xyz);
|
||||
if(dis > near && dis < helsing_viewDistance){
|
||||
// 透视投影
|
||||
vec4 posInEye = camera_projection_matrix * vcPos;
|
||||
// 可视区颜色
|
||||
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
|
||||
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
|
||||
if(visible(posInEye)){
|
||||
float vis = shadow(viewPos);
|
||||
if(vis > 0.3){
|
||||
gl_FragColor = mix(gl_FragColor,helsing_visibleAreaColor,.5);
|
||||
} else{
|
||||
gl_FragColor = mix(gl_FragColor,helsing_invisibleAreaColor,.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
131
src/Obj/Analysis/ViewShed/glsl2.js
Normal file
131
src/Obj/Analysis/ViewShed/glsl2.js
Normal file
@ -0,0 +1,131 @@
|
||||
export default `
|
||||
#define USE_CUBE_MAP_SHADOW true
|
||||
uniform sampler2D colorTexture;
|
||||
uniform sampler2D depthTexture;
|
||||
in vec2 v_textureCoordinates;
|
||||
uniform mat4 camera_projection_matrix;
|
||||
uniform mat4 camera_view_matrix;
|
||||
uniform samplerCube shadowMap_textureCube;
|
||||
uniform mat4 shadowMap_matrix;
|
||||
uniform vec4 shadowMap_lightPositionEC;
|
||||
uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
|
||||
uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
|
||||
uniform float helsing_viewDistance;
|
||||
uniform vec4 helsing_visibleAreaColor;
|
||||
uniform vec4 helsing_invisibleAreaColor;
|
||||
|
||||
struct zx_shadowParameters
|
||||
{
|
||||
vec3 texCoords;
|
||||
float depthBias;
|
||||
float depth;
|
||||
float nDotL;
|
||||
vec2 texelStepSize;
|
||||
float normalShadingSmooth;
|
||||
float darkness;
|
||||
};
|
||||
|
||||
float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)
|
||||
{
|
||||
float depthBias = shadowParameters.depthBias;
|
||||
float depth = shadowParameters.depth;
|
||||
float nDotL = shadowParameters.nDotL;
|
||||
float normalShadingSmooth = shadowParameters.normalShadingSmooth;
|
||||
float darkness = shadowParameters.darkness;
|
||||
vec3 uvw = shadowParameters.texCoords;
|
||||
depth -= depthBias;
|
||||
float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);
|
||||
return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);
|
||||
}
|
||||
|
||||
vec4 getPositionEC(){
|
||||
return czm_windowToEyeCoordinates(gl_FragCoord);
|
||||
}
|
||||
|
||||
vec3 getNormalEC(){
|
||||
return vec3(1.);
|
||||
}
|
||||
|
||||
vec4 toEye(in vec2 uv,in float depth){
|
||||
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
|
||||
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
|
||||
posInCamera=posInCamera/posInCamera.w;
|
||||
return posInCamera;
|
||||
}
|
||||
|
||||
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
|
||||
vec3 v01=point-planeOrigin;
|
||||
float d=dot(planeNormal,v01);
|
||||
return(point-planeNormal*d);
|
||||
}
|
||||
|
||||
float getDepth(in vec4 depth){
|
||||
float z_window=czm_unpackDepth(depth);
|
||||
z_window=czm_reverseLogDepth(z_window);
|
||||
float n_range=czm_depthRange.near;
|
||||
float f_range=czm_depthRange.far;
|
||||
return(2.*z_window-n_range-f_range)/(f_range-n_range);
|
||||
}
|
||||
|
||||
float shadow(in vec4 positionEC){
|
||||
vec3 normalEC=getNormalEC();
|
||||
zx_shadowParameters shadowParameters;
|
||||
shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
|
||||
shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
|
||||
shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
|
||||
shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
|
||||
vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;
|
||||
float distance=length(directionEC);
|
||||
directionEC=normalize(directionEC);
|
||||
float radius=shadowMap_lightPositionEC.w;
|
||||
if(distance>radius)
|
||||
{
|
||||
return 2.0;
|
||||
}
|
||||
vec3 directionWC=czm_inverseViewRotation*directionEC;
|
||||
shadowParameters.depth=distance/radius-0.0003;
|
||||
shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.,1.);
|
||||
shadowParameters.texCoords=directionWC;
|
||||
float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);
|
||||
return visibility;
|
||||
}
|
||||
|
||||
bool visible(in vec4 result)
|
||||
{
|
||||
result.x/=result.w;
|
||||
result.y/=result.w;
|
||||
result.z/=result.w;
|
||||
return result.x>=-1.&&result.x<=1.
|
||||
&&result.y>=-1.&&result.y<=1.
|
||||
&&result.z>=-1.&&result.z<=1.;
|
||||
}
|
||||
|
||||
void main(){
|
||||
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
|
||||
out_FragColor = texture(colorTexture, v_textureCoordinates);
|
||||
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
|
||||
float depth = getDepth(texture(depthTexture, v_textureCoordinates));
|
||||
// 视角 = (纹理坐标, 深度)
|
||||
vec4 viewPos = toEye(v_textureCoordinates, depth);
|
||||
// 世界坐标
|
||||
vec4 wordPos = czm_inverseView * viewPos;
|
||||
// 虚拟相机中坐标
|
||||
vec4 vcPos = camera_view_matrix * wordPos;
|
||||
float near = .001 * helsing_viewDistance;
|
||||
float dis = length(vcPos.xyz);
|
||||
if(dis > near && dis < helsing_viewDistance){
|
||||
// 透视投影
|
||||
vec4 posInEye = camera_projection_matrix * vcPos;
|
||||
// 可视区颜色
|
||||
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
|
||||
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
|
||||
if(visible(posInEye)){
|
||||
float vis = shadow(viewPos);
|
||||
if(vis > 0.3){
|
||||
out_FragColor = mix(out_FragColor,helsing_visibleAreaColor,.5);
|
||||
} else{
|
||||
out_FragColor = mix(out_FragColor,helsing_invisibleAreaColor,.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
773
src/Obj/Analysis/ViewShed/index.js
Normal file
773
src/Obj/Analysis/ViewShed/index.js
Normal file
@ -0,0 +1,773 @@
|
||||
// ViewShed.js
|
||||
import glsl from './glsl'
|
||||
import glsl2 from './glsl2'
|
||||
import Event from "../../../Event";
|
||||
import MouseTip from "../../../MouseTip";
|
||||
import Tools from "../../../Tools";
|
||||
import EventBinding from '../../Element/Dialog/eventBinding';
|
||||
import Controller from "../../../Controller";
|
||||
import Dialog from '../../../BaseDialog';
|
||||
import { html } from "./_element";
|
||||
/**
|
||||
* @constructor
|
||||
* @description 可视域分析
|
||||
* @param sdk
|
||||
* @param {Object} options 选项。
|
||||
* @param {Cesium.Cartesian3} options.viewPosition 观测点位置。
|
||||
* @param {Cesium.Cartesian3} options.viewPositionEnd 最远观测点位置(如果设置了观测距离,这个属性可以不设置)。
|
||||
* @param {Number} options.viewPointHeight=1.8 视点高度(单位`米`)。
|
||||
* @param {Number} options.viewDistance 观测距离(单位`米`)。
|
||||
* @param {Number} options.viewHeading 航向角(单位`度`)。
|
||||
* @param {Number} options.viewPitch 俯仰角(单位`度`)。
|
||||
* @param {Number} options.horizontalViewAngle=90 可视域水平夹角(单位`度`)。
|
||||
* @param {Number} options.verticalViewAngle=60 可视域垂直夹角(单位`度`)。
|
||||
* @param {String} options.visibleAreaColor=#008000 可视区域颜色(默认值`绿色`)。
|
||||
* @param {String} options.invisibleAreaColor=#FF0000 不可视区域颜色(默认值`红色`)。
|
||||
*/
|
||||
class ViewShedStage extends Tools {
|
||||
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
super(sdk, options)
|
||||
|
||||
// if (Object.hasOwn(options.viewPosition, 'lng') && Object.hasOwn(options.viewPosition, 'lat') && Object.hasOwn(options.viewPosition, 'alt')) {
|
||||
// this.error = '请提供观测点位置!'
|
||||
// window.ELEMENT && window.ELEMENT.Message({
|
||||
// message: '请提供观测点位置!',
|
||||
// type: 'warning',
|
||||
// duration: 1500
|
||||
// });
|
||||
// return
|
||||
// }
|
||||
|
||||
this.viewer = sdk.viewer;
|
||||
this.options = {}
|
||||
this.options.viewPosition = options.viewPosition;
|
||||
this.options.viewPositionEnd = options.viewPositionEnd;
|
||||
this.options.horizontalViewAngle = (options.horizontalViewAngle || options.horizontalViewAngle === 0) ? options.horizontalViewAngle : 90.0;
|
||||
this.options.verticalViewAngle = (options.verticalViewAngle || options.verticalViewAngle === 0) ? options.verticalViewAngle : 60.0;
|
||||
this.options.visibleAreaColor = options.visibleAreaColor || '#008000';
|
||||
this.options.invisibleAreaColor = options.invisibleAreaColor || '#FF0000';
|
||||
this._elms = {};
|
||||
this.viewPointHeight = options.viewPointHeight
|
||||
// this.enabled = (typeof options.enabled === "boolean") ? options.enabled : true;
|
||||
// this.softShadows = (typeof options.softShadows === "boolean") ? options.softShadows : true;
|
||||
// this.size = options.size || 10240; // 2048
|
||||
this.ids = []
|
||||
this.Dialog = _Dialog
|
||||
this._EventBinding = new EventBinding()
|
||||
this.html = null
|
||||
YJ.Analysis.AnalysesResults.push(this)
|
||||
ViewShedStage.edit(this)
|
||||
// ViewShedStage.edit(this)
|
||||
// this.update();
|
||||
}
|
||||
|
||||
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 viewPosition() {
|
||||
return this.options.viewPosition
|
||||
}
|
||||
|
||||
set viewPosition(v) {
|
||||
this.options.viewPosition = v
|
||||
this.ids[0] && (this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
|
||||
this.update()
|
||||
// let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
|
||||
}
|
||||
|
||||
get viewPositionEnd() {
|
||||
return this.options.viewPositionEnd
|
||||
}
|
||||
|
||||
set viewPositionEnd(v) {
|
||||
this.options.viewPositionEnd = v
|
||||
this.ids[1] && (this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
|
||||
this.update()
|
||||
// let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
// this.viewDistance = this.viewPositionEnd ? Cesium.Cartesian3.distance(this.viewPosition, this.viewPositionEnd) : (options.viewDistance || 100.0);
|
||||
}
|
||||
|
||||
get horizontalViewAngle() {
|
||||
return this.options.horizontalViewAngle
|
||||
}
|
||||
|
||||
set horizontalViewAngle(v) {
|
||||
this.options.horizontalViewAngle = v
|
||||
if (this._DialogObject && this._DialogObject._element && this._DialogObject._element.content) {
|
||||
let contentElm = this._DialogObject._element.content
|
||||
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
|
||||
e_horizontalViewAngle.value = v
|
||||
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
|
||||
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
|
||||
rangeNodeActiveText.innerHTML = v + '°';
|
||||
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
|
||||
rangeProcess.style.width = v / 180 * 100 + '%'
|
||||
}
|
||||
this.update()
|
||||
}
|
||||
|
||||
get visibleAreaColor() {
|
||||
return this.options.visibleAreaColor
|
||||
}
|
||||
|
||||
set visibleAreaColor(v) {
|
||||
this.options.visibleAreaColor = v
|
||||
this.update()
|
||||
}
|
||||
|
||||
get invisibleAreaColor() {
|
||||
return this.options.invisibleAreaColor
|
||||
}
|
||||
|
||||
set invisibleAreaColor(v) {
|
||||
this.options.invisibleAreaColor = v
|
||||
this.update()
|
||||
}
|
||||
|
||||
get verticalViewAngle() {
|
||||
return this.options.verticalViewAngle
|
||||
}
|
||||
|
||||
set verticalViewAngle(v) {
|
||||
this.options.verticalViewAngle = v
|
||||
this.update()
|
||||
}
|
||||
|
||||
get viewDistance() {
|
||||
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight)
|
||||
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
let distance = Cesium.Cartesian3.distance(viewPosition3, viewPositionEnd3)
|
||||
return distance
|
||||
}
|
||||
|
||||
get viewHeading() {
|
||||
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight)
|
||||
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
let heading = getHeading(viewPosition3, viewPositionEnd3)
|
||||
return heading
|
||||
}
|
||||
|
||||
get viewPitch() {
|
||||
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight)
|
||||
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
let pitch = getPitch(viewPosition3, viewPositionEnd3)
|
||||
return pitch
|
||||
}
|
||||
|
||||
static create(that) {
|
||||
that.destroy()
|
||||
let count = 0;
|
||||
if (!YJ.Measure.GetMeasureStatus()) {
|
||||
that.event = new Event(that.sdk)
|
||||
that.tip = new MouseTip('左键选择观测点位置,右键取消', that.sdk)
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
that.event.mouse_left((movement, cartesian) => {
|
||||
if (!that.viewPosition) {
|
||||
that.options.viewPosition = that.cartesian3Towgs84(cartesian, that.viewer)
|
||||
that.ids.push(ViewShedStage.create_point(that, cartesian))
|
||||
that.tip.set_text("左键选择最远观测点位置,右键取消")
|
||||
}
|
||||
count++
|
||||
if (count === 2) {
|
||||
that.options.viewPositionEnd = that.cartesian3Towgs84(cartesian, that.viewer)
|
||||
that.ids.push(ViewShedStage.create_point(that, cartesian))
|
||||
end()
|
||||
that.update()
|
||||
}
|
||||
})
|
||||
that.event.mouse_move((movement, cartesian) => {
|
||||
that.tip.setPosition(cartesian, movement.endPosition.x, movement.endPosition.y)
|
||||
})
|
||||
that.event.mouse_right((movement, cartesian) => {
|
||||
that.ids.forEach(id => {
|
||||
that.viewer.entities.removeById(id)
|
||||
})
|
||||
that.ids = []
|
||||
end()
|
||||
})
|
||||
that.event.gesture_pinck_start((movement, cartesian) => {
|
||||
let startTime = new Date()
|
||||
that.event.gesture_pinck_end(() => {
|
||||
let endTime = new Date()
|
||||
if (endTime - startTime >= 500) {
|
||||
that.ids.forEach(id => {
|
||||
that.viewer.entities.removeById(id)
|
||||
})
|
||||
that.ids = []
|
||||
end()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
console.log('上一次测量未结束')
|
||||
}
|
||||
|
||||
function end() {
|
||||
that.ids.forEach(id => {
|
||||
let entity = that.viewer.entities.getById(id)
|
||||
entity.show = false
|
||||
})
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
that.tip.destroy()
|
||||
that.event.destroy()
|
||||
that.tip = null
|
||||
that.event = null
|
||||
}
|
||||
}
|
||||
|
||||
static create_point(that, cartesian) {
|
||||
let id = that.randomString()
|
||||
let p = that.cartesian3Towgs84(cartesian, that.viewer)
|
||||
let params = {
|
||||
id: id,
|
||||
position: Cesium.Cartesian3.fromDegrees(p.lng, p.lat, p.alt),
|
||||
billboard: {
|
||||
image: that.getSourceRootPath() + '/img/point.png',
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
color: Cesium.Color.WHITE.withAlpha(0.99)
|
||||
}
|
||||
}
|
||||
that.viewer.entities.add(
|
||||
new Cesium.Entity(params)
|
||||
)
|
||||
return id
|
||||
}
|
||||
|
||||
|
||||
add() {
|
||||
this.createLightCamera();
|
||||
this.createShadowMap();
|
||||
this.createPostStage();
|
||||
// this.drawFrustumOutline();
|
||||
this.drawSketch();
|
||||
ViewShedStage.getcanvas(this)
|
||||
}
|
||||
|
||||
update() {
|
||||
this.clear();
|
||||
this.add();
|
||||
}
|
||||
|
||||
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)
|
||||
that.editevent && that.editevent.destroy()
|
||||
that.ControllerObject && that.ControllerObject.destroy()
|
||||
that.ids.forEach(id => {
|
||||
that.viewer.entities.removeById(id)
|
||||
})
|
||||
},
|
||||
})
|
||||
await that._DialogObject.init()
|
||||
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' view-shed'
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
that._DialogObject.contentAppChild(contentElm)
|
||||
let resetBtn = that._DialogObject._element.body.getElementsByClassName('edit')[0];
|
||||
resetBtn.addEventListener('click', () => {
|
||||
that.nodeEdit()
|
||||
})
|
||||
let drawElm = document.createElement('button')
|
||||
drawElm.innerHTML = '绘制'
|
||||
drawElm.addEventListener('click', () => {
|
||||
ViewShedStage.create(that)
|
||||
})
|
||||
that._DialogObject.footAppChild(drawElm)
|
||||
that.html = contentElm
|
||||
let all_elm = contentElm.getElementsByTagName("*")
|
||||
that._EventBinding.on(that, all_elm)
|
||||
that._elms = that._EventBinding.element
|
||||
// //经度值
|
||||
// let e_lng = contentElm.querySelector("span[name='lng']")
|
||||
// e_lng.innerHTML = Number(that.options.viewPosition.lng.toFixed(8))
|
||||
|
||||
// //纬度值
|
||||
// let e_lat = contentElm.querySelector("span[name='lat']")
|
||||
// e_lat.innerHTML = Number(that.options.viewPosition.lat.toFixed(8))
|
||||
|
||||
// //高度值
|
||||
// let e_alt = contentElm.querySelector("span[name='alt']")
|
||||
// e_alt.innerHTML = Number(that.options.viewPosition.alt.toFixed(8))
|
||||
|
||||
// //偏航角
|
||||
// let e_viewHeading = contentElm.querySelector("span[name='viewHeading']")
|
||||
// e_viewHeading.innerHTML = Number(that.viewHeading.toFixed(8))
|
||||
|
||||
// //俯仰角
|
||||
// let e_viewPitch = contentElm.querySelector("span[name='viewPitch']")
|
||||
// e_viewPitch.innerHTML = Number(that.viewPitch.toFixed(8))
|
||||
|
||||
//视域夹角
|
||||
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
|
||||
e_horizontalViewAngle.value = that.options.horizontalViewAngle
|
||||
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
|
||||
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
|
||||
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
|
||||
let percentage = that.horizontalViewAngle / 180 * 100
|
||||
rangeNodeActive.style.left = percentage + '%';
|
||||
rangeProcess.style.width = percentage + '%'
|
||||
rangeNodeActiveText.innerHTML = that.horizontalViewAngle + '°';
|
||||
let timeout
|
||||
e_horizontalViewAngle.addEventListener('input', () => {
|
||||
let percentage = e_horizontalViewAngle.value / 180 * 100
|
||||
rangeNodeActive.style.left = percentage + '%';
|
||||
rangeProcess.style.width = percentage + '%';
|
||||
rangeNodeActiveText.innerHTML = e_horizontalViewAngle.value + '°';
|
||||
})
|
||||
e_horizontalViewAngle.addEventListener('change', () => {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => {
|
||||
that.horizontalViewAngle = e_horizontalViewAngle.value;
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
|
||||
static getcanvas(that) {
|
||||
if (!that.viewPosition) {
|
||||
return
|
||||
}
|
||||
if (that.viewBillboardPrimitive) {
|
||||
that.viewer.scene.primitives.remove(that.viewBillboardPrimitive)
|
||||
that.viewBillboardPrimitive = null
|
||||
}
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d')
|
||||
canvas.width = 220
|
||||
canvas.height = 180
|
||||
canvas.style.background = "#000000"
|
||||
let img = new Image();
|
||||
const data = [
|
||||
{
|
||||
images: that.getSourceRootPath() + '/img/bubble/lng.png',
|
||||
text: '经度:' + parseFloat(that.viewPosition.lng.toFixed(10)) + '°'
|
||||
},
|
||||
{
|
||||
images: that.getSourceRootPath() + '/img/bubble/lat.png',
|
||||
text: '纬度:' + parseFloat(that.viewPosition.lat.toFixed(10)) + '°'
|
||||
},
|
||||
{
|
||||
images: that.getSourceRootPath() + '/img/bubble/h.png',
|
||||
text: '高度:' + Number(((parseFloat(that.viewPosition.alt.toFixed(2)) + Number(that.viewPointHeight))).toFixed(2)) + ' m'
|
||||
},
|
||||
{
|
||||
images: that.getSourceRootPath() + '/img/bubble/heading.png',
|
||||
text: '偏航角:' + parseFloat(that.viewHeading.toFixed(10)) + '°'
|
||||
},
|
||||
{
|
||||
images: that.getSourceRootPath() + '/img/bubble/pitch.png',
|
||||
text: '俯仰角:' + parseFloat(that.viewPitch.toFixed(10)) + '°'
|
||||
}
|
||||
]
|
||||
img.src = that.getSourceRootPath() + '/img/bubble/bubble.png';
|
||||
let imagesLoaded = 0
|
||||
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) {
|
||||
that.viewBillboardPrimitive = that.viewer.scene.primitives.add(new Cesium.BillboardCollection())
|
||||
that.viewBillboardPrimitive.add({
|
||||
position: Cesium.Cartesian3.fromDegrees(that.viewPosition.lng, that.viewPosition.lat, that.viewPosition.alt + that.viewPointHeight),
|
||||
image: canvas,
|
||||
width: 200,
|
||||
height: 180,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
})
|
||||
}
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
clear() {
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
this.tip && this.tip.destroy()
|
||||
this.event && this.event.destroy()
|
||||
this.tip = null
|
||||
this.event = null
|
||||
if (this.sketch) {
|
||||
this.viewer.entities.removeById(this.sketch.id);
|
||||
this.sketch = null;
|
||||
}
|
||||
if (this.frustumOutline) {
|
||||
// this.frustumOutline.destroy();
|
||||
this.viewer.entities.removeById(this.frustumOutline.id);
|
||||
this.frustumOutline = null;
|
||||
}
|
||||
if (this.postStage) {
|
||||
this.viewer.scene.postProcessStages.remove(this.postStage);
|
||||
this.postStage = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.clear()
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
this.ids.forEach(id => {
|
||||
this.viewer.entities.removeById(id)
|
||||
})
|
||||
this.ids = []
|
||||
if (this.viewBillboardPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.viewBillboardPrimitive)
|
||||
}
|
||||
this.viewBillboardPrimitive = null
|
||||
this.options.viewPosition = null
|
||||
this.options.viewPositionEnd = null
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
if (this._originalShadowMap) {
|
||||
this.viewer.scene.shadowMap = this._originalShadowMap
|
||||
this._originalShadowMap = null
|
||||
}
|
||||
this.viewer.shadows = this.viewer._shadows
|
||||
}
|
||||
|
||||
nodeEdit() {
|
||||
if (YJ.Measure.GetMeasureStatus()) {
|
||||
console.log('上一次测量未结束')
|
||||
}
|
||||
else {
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ids.forEach(id => {
|
||||
let entity = this.viewer.entities.getById(id)
|
||||
entity.show = true
|
||||
})
|
||||
let selectPoint
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
// this.tip = new MouseTip('左键选择要操作的观测点,右键取消', this.sdk)
|
||||
this.editevent = new Event(this.sdk)
|
||||
this.editevent.mouse_left((movement, cartesian) => {
|
||||
let pick = this.viewer.scene.pick(movement.position);
|
||||
if (pick && pick.id && pick.id.id && this.ids.indexOf(pick.id.id) != -1 && (!selectPoint || selectPoint.id != pick.id.id)) {
|
||||
selectPoint = pick.id
|
||||
// this.event.destroy()
|
||||
// this.tip.destroy()
|
||||
this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(this.viewPosition.lng, this.viewPosition.lat, this.viewPosition.alt)
|
||||
this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(this.viewPositionEnd.lng, this.viewPositionEnd.lat, this.viewPositionEnd.alt)
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
this.ControllerObject = new Controller(this.sdk, { position: { ...this.cartesian3Towgs84(selectPoint.position._value, this.sdk.viewer) } })
|
||||
this.ControllerObject.controllerCallBack = (params, status) => {
|
||||
if (params.position.alt < 0) {
|
||||
params.position.alt = 0
|
||||
}
|
||||
selectPoint.position = new Cesium.Cartesian3.fromDegrees(params.position.lng, params.position.lat, params.position.alt)
|
||||
if (status) {
|
||||
if (this.ids.indexOf(pick.id.id) == 0) {
|
||||
this.viewPosition = params.position
|
||||
}
|
||||
else {
|
||||
this.viewPositionEnd = params.position
|
||||
}
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
}
|
||||
}
|
||||
this.ControllerObject.editTranslational()
|
||||
}
|
||||
})
|
||||
this.editevent.mouse_right((movement, cartesian) => {
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
this.ids.forEach(id => {
|
||||
let entity = this.viewer.entities.getById(id)
|
||||
entity.show = false
|
||||
})
|
||||
selectPoint = null
|
||||
})
|
||||
this.editevent.mouse_move((movement, cartesian) => {
|
||||
// this.tip.setPosition(
|
||||
// cartesian,
|
||||
// movement.endPosition.x,
|
||||
// movement.endPosition.y
|
||||
// )
|
||||
})
|
||||
|
||||
this.editevent.gesture_pinck_start((movement, cartesian) => {
|
||||
let startTime = new Date()
|
||||
this.editevent.gesture_pinck_end(() => {
|
||||
let endTime = new Date()
|
||||
if (endTime - startTime >= 500) {
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
this.ids.forEach(id => {
|
||||
let entity = this.viewer.entities.getById(id)
|
||||
entity.show = false
|
||||
})
|
||||
selectPoint = null
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createLightCamera() {
|
||||
if (!this.options.viewPosition) {
|
||||
return
|
||||
}
|
||||
let _this = this
|
||||
this.lightCamera = new Cesium.Camera(this.viewer.scene);
|
||||
this.lightCamera.position = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight);
|
||||
// if (this.viewPositionEnd) {
|
||||
// let direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(this.viewPositionEnd, this.viewPosition, new Cesium.Cartesian3()), new Cesium.Cartesian3());
|
||||
// this.lightCamera.direction = direction; // direction是相机面向的方向
|
||||
// }
|
||||
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: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(this.viewHeading || 0),
|
||||
pitch: Cesium.Math.toRadians(this.viewPitch || 0),
|
||||
roll: 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createShadowMap() {
|
||||
this.shadowMap = new Cesium.ShadowMap({
|
||||
context: (this.viewer.scene).context,
|
||||
lightCamera: this.lightCamera,
|
||||
enabled: true,
|
||||
isPointLight: true,
|
||||
pointLightRadius: this.viewDistance,
|
||||
cascadesEnabled: false,
|
||||
size: 2048, // 2048
|
||||
softShadows: true,
|
||||
normalOffset: false,
|
||||
fromLightSource: false
|
||||
});
|
||||
if (!this._originalShadowMap) {
|
||||
this._originalShadowMap = this.viewer.scene.shadowMap
|
||||
}
|
||||
this.viewer.scene.shadowMap = this.shadowMap;
|
||||
// setTimeout(() => {
|
||||
// this.viewer.shadows = this.viewer._shadows
|
||||
// }, 0);
|
||||
}
|
||||
|
||||
createPostStage() {
|
||||
const fs = glsl
|
||||
if (Number(Cesium.VERSION.split('.')[1]) >= 102) {
|
||||
fs = glsl2
|
||||
}
|
||||
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: Cesium.Color.fromCssColorString(this.visibleAreaColor),
|
||||
helsing_invisibleAreaColor: Cesium.Color.fromCssColorString(this.invisibleAreaColor),
|
||||
}
|
||||
});
|
||||
this.postStage = this.viewer.scene.postProcessStages.add(postStage);
|
||||
}
|
||||
|
||||
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: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
|
||||
orientation: orientation
|
||||
}),
|
||||
id: Math.random().toString(36).substr(2),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.YELLOWGREEN//new Cesium.Color(0.0, 1.0, 0.0, 1.0)
|
||||
),
|
||||
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
|
||||
})
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
drawSketch() {
|
||||
this.sketch = this.viewer.entities.add({
|
||||
name: 'sketch',
|
||||
position: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
|
||||
orientation: Cesium.Transforms.headingPitchRollQuaternion(
|
||||
Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
|
||||
Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - 90, this.viewPitch, 0.0)
|
||||
),
|
||||
ellipsoid: {
|
||||
radii: new Cesium.Cartesian3(
|
||||
this.viewDistance,
|
||||
this.viewDistance,
|
||||
this.viewDistance
|
||||
),
|
||||
// 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(90 - (this.verticalViewAngle / 2)),
|
||||
maximumCone: Cesium.Math.toRadians(90 + (this.verticalViewAngle / 2)),
|
||||
fill: false,
|
||||
outline: true,
|
||||
subdivisions: 256,
|
||||
stackPartitions: 64,
|
||||
slicePartitions: 64,
|
||||
outlineColor: Cesium.Color.YELLOWGREEN
|
||||
}
|
||||
});
|
||||
|
||||
this.frustumOutline = this.viewer.entities.add({
|
||||
name: 'sketch',
|
||||
position: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
|
||||
orientation: Cesium.Transforms.headingPitchRollQuaternion(
|
||||
Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt + this.viewPointHeight),
|
||||
Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - 90, this.viewPitch, 0.0)
|
||||
),
|
||||
ellipsoid: {
|
||||
radii: new Cesium.Cartesian3(
|
||||
this.viewDistance,
|
||||
this.viewDistance,
|
||||
this.viewDistance
|
||||
),
|
||||
innerRadii: new Cesium.Cartesian3(0.0001, 0.0001, 0.0001),
|
||||
minimumClock: Cesium.Math.toRadians(-this.horizontalViewAngle / 2),
|
||||
maximumClock: Cesium.Math.toRadians(this.horizontalViewAngle / 2),
|
||||
minimumCone: Cesium.Math.toRadians(90 - (this.verticalViewAngle / 2)),
|
||||
maximumCone: Cesium.Math.toRadians(90 + (this.verticalViewAngle / 2)),
|
||||
fill: false,
|
||||
outline: true,
|
||||
subdivisions: 256,
|
||||
stackPartitions: 1,
|
||||
slicePartitions: 1,
|
||||
outlineColor: Cesium.Color.YELLOWGREEN
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function 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));
|
||||
}
|
||||
|
||||
function 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 ViewShedStage;
|
||||
74
src/Obj/Analysis/ViewShed2/_element.js
Normal file
74
src/Obj/Analysis/ViewShed2/_element.js
Normal file
@ -0,0 +1,74 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">位置拾取(起点、终点)</span>
|
||||
<button class="edit"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>拾取</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row subtitle-box">
|
||||
<span class="subtitle">视域夹角</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="range-box">
|
||||
<div class="range-bg">
|
||||
<div class="range-process-box">
|
||||
<div class="range-process"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-node-box">
|
||||
<span class="range-node-text">0°</span>
|
||||
<span class="range-node-text">45°</span>
|
||||
<span class="range-node-text">90°</span>
|
||||
<span class="range-node-text">135°</span>
|
||||
<span class="range-node-text">180°</span>
|
||||
<div class="range-node-active"><span class="range-node-active-text">0°</span></div>
|
||||
</div>
|
||||
<input type="range" max="180" min="0" step="1" name="horizontalViewAngle">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">经度:</span>
|
||||
<span class="text-number" name="lng"></span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">偏航角:</span>
|
||||
<span class="text-number" name="viewHeading"></span>
|
||||
<span class="unit">°</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">纬度:</span>
|
||||
<span class="text-number" name="lat"></span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">俯仰角:</span>
|
||||
<span class="text-number" name="viewPitch"></span>
|
||||
<span class="unit">°</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">高度:</span>
|
||||
<span class="text-number" name="alt"></span>
|
||||
<span class="unit text-number" style="margin-left: 5px;">m</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
512
src/Obj/Analysis/ViewShed2/_index.js
Normal file
512
src/Obj/Analysis/ViewShed2/_index.js
Normal file
@ -0,0 +1,512 @@
|
||||
/*
|
||||
* @Author: Wang jianLei
|
||||
* @Date: 2022-05-17 21:49:28
|
||||
* @Last Modified by: Wang JianLei
|
||||
* @Last Modified time: 2022-05-19 22:08:14
|
||||
*/
|
||||
|
||||
let ViewShed = function (sdk, canvasEleId) {
|
||||
if (!sdk.viewer) throw new Error("no viewer object!");
|
||||
alert(canvasEleId)
|
||||
let canvasEle = document.getElementById(canvasEleId);
|
||||
if (!canvasEle) throw new Error("the canvas element is not exist");
|
||||
this.canvasEle = canvasEle;
|
||||
this.viewer = sdk.viewer;
|
||||
this.handler = undefined;
|
||||
this.lightCamera;
|
||||
this.pyramid;
|
||||
this.frustumPrimitive;
|
||||
this.viewershedPolygon;
|
||||
};
|
||||
ViewShed.prototype = {
|
||||
/**
|
||||
* 初始化handler
|
||||
*/
|
||||
initHandler() {
|
||||
if (this.handler) {
|
||||
this.handler.destroy();
|
||||
this.handler = undefined;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 开始执行视域分析
|
||||
* @param {number} precision 精度,值越大创建耗时越长,建议在10~20之间
|
||||
*/
|
||||
createViewshed: function (precision) {
|
||||
let $this = this;
|
||||
let scene = $this.viewer.scene;
|
||||
$this.initHandler();
|
||||
$this.clearAll();
|
||||
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
|
||||
$this.handler.setInputAction((event) => {
|
||||
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
|
||||
scene.screenSpaceCameraController.enableRotate = false;
|
||||
scene.screenSpaceCameraController.enableZoom = false;
|
||||
scene.globe.depthTestAgainstTerrain = true;
|
||||
let earthPosition = scene.pickPosition(event.position);
|
||||
let pos = $this.cartesian3ToDegree(earthPosition);
|
||||
$this.handler.setInputAction(function (event) {
|
||||
let newPosition = scene.pickPosition(event.endPosition);
|
||||
if (Cesium.defined(newPosition)) {
|
||||
let pos1 = $this.cartesian3ToDegree(newPosition);
|
||||
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
|
||||
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
|
||||
let pitch = $this.getPitch(earthPosition, newPosition);
|
||||
$this.ViewShedOptions = {
|
||||
viewPosition: earthPosition, //观测点 笛卡尔坐标
|
||||
endPosition: newPosition, //目标点 笛卡尔坐标
|
||||
direction: angle, //观测方位角 默认为`0`,范围`0~360`
|
||||
pitch: pitch, //俯仰角,radius,默认为`0`
|
||||
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
|
||||
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
|
||||
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
|
||||
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
|
||||
visualRange: distance, //距离,单位`米`
|
||||
};
|
||||
$this.updateViewShed();
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
|
||||
|
||||
$this.handler.setInputAction(() => {
|
||||
$this.initHandler();
|
||||
// 开启地球旋转和缩放
|
||||
scene.screenSpaceCameraController.enableRotate = true;
|
||||
scene.screenSpaceCameraController.enableZoom = true;
|
||||
$this.drawViewershed(precision);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_UP);
|
||||
},
|
||||
|
||||
ReturnDistance(pos0, pos1) {
|
||||
let distance = 0;
|
||||
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
|
||||
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
|
||||
/**根据经纬度计算出距离**/
|
||||
let geodesic = new Cesium.EllipsoidGeodesic();
|
||||
geodesic.setEndPoints(point1cartographic, point2cartographic);
|
||||
let s = geodesic.surfaceDistance;
|
||||
return s;
|
||||
},
|
||||
getHeight(x, y, objectsToExclude) {
|
||||
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
|
||||
let endHeight = this.viewer.scene.sampleHeight(
|
||||
endCartographic,
|
||||
objectsToExclude
|
||||
);
|
||||
return endHeight;
|
||||
},
|
||||
|
||||
cartesian3ToDegree: function (Cartesian3) {
|
||||
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
|
||||
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
|
||||
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
|
||||
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
|
||||
let _alt = _cartographic.height;
|
||||
return [_lng, _lat, _alt];
|
||||
},
|
||||
getAngle: function (lng1, lat1, lng2, lat2) {
|
||||
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
|
||||
if (lng2 >= lng1) {
|
||||
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
|
||||
} else {
|
||||
dRotateAngle =
|
||||
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
|
||||
}
|
||||
dRotateAngle = (dRotateAngle * 180) / Math.PI;
|
||||
return dRotateAngle;
|
||||
},
|
||||
|
||||
getPitch(pointA, pointB) {
|
||||
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
|
||||
const vector = Cesium.Cartesian3.subtract(
|
||||
pointB,
|
||||
pointA,
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
let direction = Cesium.Matrix4.multiplyByPointAsVector(
|
||||
Cesium.Matrix4.inverse(transfrom, transfrom),
|
||||
vector,
|
||||
vector
|
||||
);
|
||||
Cesium.Cartesian3.normalize(direction, direction);
|
||||
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
|
||||
},
|
||||
|
||||
updateViewShed: function () {
|
||||
this.clear();
|
||||
this.setLightCamera();
|
||||
this.addVisualPyramid();
|
||||
this.createFrustum();
|
||||
},
|
||||
clear: function () {
|
||||
if (this.pyramid) {
|
||||
this.viewer.entities.removeById(this.pyramid.id);
|
||||
this.pyramid = undefined;
|
||||
}
|
||||
if (this.frustumPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.frustumPrimitive);
|
||||
this.frustumPrimitive = undefined;
|
||||
}
|
||||
if (this.debugModelMatrixPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
|
||||
this.debugModelMatrixPrimitive = undefined;
|
||||
}
|
||||
},
|
||||
clearAll: function () {
|
||||
this.clear();
|
||||
if (this.viewershedPolygon) {
|
||||
this.viewer.scene.primitives.remove(this.viewershedPolygon);
|
||||
this.viewershedPolygon = undefined;
|
||||
}
|
||||
},
|
||||
addVisualPyramid: function () {
|
||||
let options = this.ViewShedOptions;
|
||||
let position = options.viewPosition;
|
||||
let visualRange = Number(options.visualRange);
|
||||
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
|
||||
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.DebugModelMatrixPrimitive({
|
||||
modelMatrix: transform,
|
||||
length: 5.0,
|
||||
})
|
||||
);
|
||||
const halfClock = options.horizontalViewAngle / 2;
|
||||
const halfCone = options.verticalViewAngle / 2;
|
||||
const pitch = Cesium.Math.toDegrees(options.pitch);
|
||||
const ellipsoid = new Cesium.EllipsoidGraphics({
|
||||
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
|
||||
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
|
||||
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
|
||||
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
|
||||
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
|
||||
fill: false,
|
||||
outline: true,
|
||||
subdivisions: 256,
|
||||
stackPartitions: 64,
|
||||
slicePartitions: 64,
|
||||
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
|
||||
});
|
||||
const pyramidEntity = new Cesium.Entity({
|
||||
position: position,
|
||||
ellipsoid,
|
||||
});
|
||||
this.pyramid = this.viewer.entities.add(pyramidEntity);
|
||||
},
|
||||
setLightCamera: function () {
|
||||
if (!this.lightCamera) {
|
||||
this.lightCamera = new Cesium.Camera(this.viewer.scene);
|
||||
}
|
||||
let options = this.ViewShedOptions;
|
||||
let visualRange = Number(options.visualRange);
|
||||
this.lightCamera.position = options.viewPosition;
|
||||
this.lightCamera.frustum.near = 0.1;
|
||||
this.lightCamera.frustum.far = visualRange;
|
||||
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
|
||||
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
|
||||
this.lightCamera.frustum.aspectRatio =
|
||||
(visualRange * Math.tan(hr / 2) * 2) /
|
||||
(visualRange * Math.tan(vr / 2) * 2);
|
||||
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
|
||||
this.lightCamera.setView({
|
||||
destination: options.viewPosition,
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(options.direction || 0),
|
||||
pitch: options.pitch || 0,
|
||||
roll: 0,
|
||||
},
|
||||
});
|
||||
},
|
||||
createFrustum: function () {
|
||||
const scratchRight = new Cesium.Cartesian3();
|
||||
const scratchRotation = new Cesium.Matrix3();
|
||||
const scratchOrientation = new Cesium.Quaternion();
|
||||
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 instanceOutline = new Cesium.GeometryInstance({
|
||||
geometry: new Cesium.FrustumOutlineGeometry({
|
||||
frustum: this.lightCamera.frustum,
|
||||
origin: this.ViewShedOptions.viewPosition,
|
||||
orientation: orientation,
|
||||
}),
|
||||
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true),
|
||||
},
|
||||
});
|
||||
this.frustumPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.Primitive({
|
||||
geometryInstances: instanceOutline,
|
||||
appearance: new Cesium.PerInstanceColorAppearance({
|
||||
flat: true,
|
||||
translucent: false,
|
||||
closed: true,
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
createPoint: function (firstPos, secondPos) {
|
||||
let entity4FirstPos = new Cesium.Entity({
|
||||
name: "firstPos",
|
||||
show: true,
|
||||
position: firstPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 20,
|
||||
color: Cesium.Color.RED,
|
||||
outlineColor: Cesium.Color.YELLOW,
|
||||
outlineWidth: 5,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体起点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4FirstPos);
|
||||
let entity4SecondPos = new Cesium.Entity({
|
||||
name: "secondPos",
|
||||
show: true,
|
||||
position: secondPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 30,
|
||||
color: Cesium.Color.YELLOW,
|
||||
outlineColor: Cesium.Color.RED,
|
||||
outlineWidth: 8,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体视角终点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4SecondPos);
|
||||
},
|
||||
|
||||
//绘制可视域
|
||||
add(positionArr) {
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
Cesium.Cartesian3.fromDegreesArray(positionArr)
|
||||
),
|
||||
height: 0.0,
|
||||
extrudedHeight: 0.0,
|
||||
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
|
||||
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
|
||||
ellipsoid: Cesium.Ellipsoid.WGS84,
|
||||
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
|
||||
perPositionHeight: false, // 每个位置点使用的高度
|
||||
closeTop: true,
|
||||
closeBottom: true,
|
||||
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
|
||||
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
|
||||
});
|
||||
|
||||
let polygonInstance = new Cesium.GeometryInstance({
|
||||
geometry: polygon,
|
||||
name: "ViewershedPolygon",
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.BLUE.withAlpha(0.6)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
},
|
||||
});
|
||||
this.viewershedPolygon = this.viewer.scene.primitives.add(
|
||||
new Cesium.GroundPrimitive({
|
||||
geometryInstances: polygonInstance,
|
||||
appearance: new Cesium.EllipsoidSurfaceAppearance({
|
||||
aboveGround: true,
|
||||
material: new Cesium.Material({
|
||||
fabric: {
|
||||
type: "Image",
|
||||
uniforms: {
|
||||
image: this.returnImgae(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
drawViewershed(precision) {
|
||||
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
|
||||
const radius = this.ViewShedOptions.visualRange;
|
||||
const direction = this.ViewShedOptions.direction;
|
||||
let boundary = this.computeBoundaryOptions(pos, radius, direction);
|
||||
const bbox = boundary.bbox;
|
||||
let mask = turf.polygon([boundary.boundaryPoints]);
|
||||
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
|
||||
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
|
||||
|
||||
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
|
||||
let variogram = kriging.train(
|
||||
pointsResult.values,
|
||||
pointsResult.lngs,
|
||||
pointsResult.lats,
|
||||
"exponential",
|
||||
0,
|
||||
100
|
||||
);
|
||||
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
|
||||
const colors = [
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
];
|
||||
|
||||
this.canvasEle.width = 3840;
|
||||
this.canvasEle.height = 2160;
|
||||
kriging.plot(
|
||||
this.canvasEle,
|
||||
grid,
|
||||
[bbox[0], bbox[2]],
|
||||
[bbox[1], bbox[3]],
|
||||
colors
|
||||
);
|
||||
this.add(boundary.positionArr);
|
||||
},
|
||||
computeBoundaryOptions(pos, radius, angle) {
|
||||
let Ea = 6378137; // 赤道半径
|
||||
let Eb = 6356725; // 极半径
|
||||
const lng = pos[0],
|
||||
lat = pos[1];
|
||||
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
|
||||
let positionArr = [];
|
||||
let boundaryPoints = [];
|
||||
positionArr.push(lng, lat);
|
||||
boundaryPoints.push([lng, lat]);
|
||||
//正北是0°
|
||||
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
|
||||
let end = start + 90;
|
||||
for (let i = start; i <= end; i++) {
|
||||
let dx = radius * Math.sin((i * Math.PI) / 180.0);
|
||||
let dy = radius * Math.cos((i * Math.PI) / 180.0);
|
||||
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
|
||||
let ed = ec * Math.cos((lat * Math.PI) / 180);
|
||||
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
|
||||
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
|
||||
positionArr.push(BJD, BWD);
|
||||
boundaryPoints.push([BJD, BWD]);
|
||||
this.refreshBBox(bbox, BJD, BWD);
|
||||
}
|
||||
boundaryPoints.push([lng, lat]);
|
||||
return {
|
||||
positionArr,
|
||||
boundaryPoints,
|
||||
bbox,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 更新外围矩形 Bbox
|
||||
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
|
||||
* @param {Number} x 经度
|
||||
* @param {Number} y 纬度
|
||||
*/
|
||||
refreshBBox(result, x, y) {
|
||||
result[0] = x < result[0] ? x : result[0];
|
||||
result[1] = y < result[1] ? y : result[1];
|
||||
result[2] = x > result[2] ? x : result[2];
|
||||
result[3] = y > result[3] ? y : result[3];
|
||||
},
|
||||
/**
|
||||
* 插值点用射线判断通视性
|
||||
* @param {*} gridPoints 网格点
|
||||
* @param {*} step 步长,可以理解成是精度
|
||||
* @param {*} sourcePos 视域分析起点
|
||||
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
|
||||
*/
|
||||
createTargetPoints(gridPoints, step, sourcePos) {
|
||||
let positionArr = [];
|
||||
let objectsToExclude = [
|
||||
this.frustumPrimitive,
|
||||
this.pyramid,
|
||||
this.debugModelMatrixPrimitive,
|
||||
];
|
||||
let values = [],
|
||||
lngs = [],
|
||||
lats = [];
|
||||
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
|
||||
positionArr.push({
|
||||
x: sourcePos[0],
|
||||
y: sourcePos[1],
|
||||
z: height,
|
||||
});
|
||||
let viewPoint = this.ViewShedOptions.viewPosition;
|
||||
for (let index = 0; index < gridPoints.features.length; index++) {
|
||||
const feature = gridPoints.features[index];
|
||||
const coords = feature.geometry.coordinates;
|
||||
const x = coords[0],
|
||||
y = coords[1];
|
||||
let h = this.getHeight(x, y, objectsToExclude);
|
||||
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
|
||||
let direction = Cesium.Cartesian3.normalize(
|
||||
Cesium.Cartesian3.subtract(
|
||||
endPoint,
|
||||
viewPoint,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
// 建立射线
|
||||
let ray = new Cesium.Ray(viewPoint, direction);
|
||||
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
|
||||
if (result) {
|
||||
let buffer = this.ReturnDistance(endPoint, result.position);
|
||||
// let M_color = Cesium.Color.GREEN;
|
||||
if (buffer > step) {
|
||||
// M_color = Cesium.Color.RED;
|
||||
values.push(0);
|
||||
} else {
|
||||
values.push(1);
|
||||
}
|
||||
lngs.push(x);
|
||||
lats.push(y);
|
||||
// this.viewer.entities.add(
|
||||
// new Cesium.Entity({
|
||||
// name: "插值点哦",
|
||||
// show: true,
|
||||
// position: endPoint,
|
||||
// point: {
|
||||
// show: true,
|
||||
// pixelSize: 10,
|
||||
// color: M_color,
|
||||
// outlineWidth: 2,
|
||||
// outlineColor: Cesium.Color.YELLOW,
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
}
|
||||
}
|
||||
return {
|
||||
values,
|
||||
lngs,
|
||||
lats,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* canvas转image图片
|
||||
* @returns base64图片
|
||||
*/
|
||||
returnImgae() {
|
||||
return this.canvasEle.toDataURL("image/png");
|
||||
},
|
||||
};
|
||||
|
||||
export default ViewShed;
|
||||
131
src/Obj/Analysis/ViewShed2/glsl.js
Normal file
131
src/Obj/Analysis/ViewShed2/glsl.js
Normal file
@ -0,0 +1,131 @@
|
||||
export default `
|
||||
#define USE_CUBE_MAP_SHADOW true
|
||||
uniform sampler2D colorTexture;
|
||||
uniform sampler2D depthTexture;
|
||||
varying vec2 v_textureCoordinates;
|
||||
uniform mat4 camera_projection_matrix;
|
||||
uniform mat4 camera_view_matrix;
|
||||
uniform samplerCube shadowMap_textureCube;
|
||||
uniform mat4 shadowMap_matrix;
|
||||
uniform vec4 shadowMap_lightPositionEC;
|
||||
uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
|
||||
uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
|
||||
uniform float helsing_viewDistance;
|
||||
uniform vec4 helsing_visibleAreaColor;
|
||||
uniform vec4 helsing_invisibleAreaColor;
|
||||
|
||||
struct zx_shadowParameters
|
||||
{
|
||||
vec3 texCoords;
|
||||
float depthBias;
|
||||
float depth;
|
||||
float nDotL;
|
||||
vec2 texelStepSize;
|
||||
float normalShadingSmooth;
|
||||
float darkness;
|
||||
};
|
||||
|
||||
float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)
|
||||
{
|
||||
float depthBias = shadowParameters.depthBias;
|
||||
float depth = shadowParameters.depth;
|
||||
float nDotL = shadowParameters.nDotL;
|
||||
float normalShadingSmooth = shadowParameters.normalShadingSmooth;
|
||||
float darkness = shadowParameters.darkness;
|
||||
vec3 uvw = shadowParameters.texCoords;
|
||||
depth -= depthBias;
|
||||
float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);
|
||||
return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);
|
||||
}
|
||||
|
||||
vec4 getPositionEC(){
|
||||
return czm_windowToEyeCoordinates(gl_FragCoord);
|
||||
}
|
||||
|
||||
vec3 getNormalEC(){
|
||||
return vec3(1.);
|
||||
}
|
||||
|
||||
vec4 toEye(in vec2 uv,in float depth){
|
||||
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
|
||||
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
|
||||
posInCamera=posInCamera/posInCamera.w;
|
||||
return posInCamera;
|
||||
}
|
||||
|
||||
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
|
||||
vec3 v01=point-planeOrigin;
|
||||
float d=dot(planeNormal,v01);
|
||||
return(point-planeNormal*d);
|
||||
}
|
||||
|
||||
float getDepth(in vec4 depth){
|
||||
float z_window=czm_unpackDepth(depth);
|
||||
z_window=czm_reverseLogDepth(z_window);
|
||||
float n_range=czm_depthRange.near;
|
||||
float f_range=czm_depthRange.far;
|
||||
return(2.*z_window-n_range-f_range)/(f_range-n_range);
|
||||
}
|
||||
|
||||
float shadow(in vec4 positionEC){
|
||||
vec3 normalEC=getNormalEC();
|
||||
zx_shadowParameters shadowParameters;
|
||||
shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
|
||||
shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
|
||||
shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
|
||||
shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
|
||||
vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;
|
||||
float distance=length(directionEC);
|
||||
directionEC=normalize(directionEC);
|
||||
float radius=shadowMap_lightPositionEC.w;
|
||||
if(distance>radius)
|
||||
{
|
||||
return 2.0;
|
||||
}
|
||||
vec3 directionWC=czm_inverseViewRotation*directionEC;
|
||||
shadowParameters.depth=distance/radius-0.0003;
|
||||
shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.,1.);
|
||||
shadowParameters.texCoords=directionWC;
|
||||
float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);
|
||||
return visibility;
|
||||
}
|
||||
|
||||
bool visible(in vec4 result)
|
||||
{
|
||||
result.x/=result.w;
|
||||
result.y/=result.w;
|
||||
result.z/=result.w;
|
||||
return result.x>=-1.&&result.x<=1.
|
||||
&&result.y>=-1.&&result.y<=1.
|
||||
&&result.z>=-1.&&result.z<=1.;
|
||||
}
|
||||
|
||||
void main(){
|
||||
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
|
||||
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
|
||||
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
|
||||
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
|
||||
// 视角 = (纹理坐标, 深度)
|
||||
vec4 viewPos = toEye(v_textureCoordinates, depth);
|
||||
// 世界坐标
|
||||
vec4 wordPos = czm_inverseView * viewPos;
|
||||
// 虚拟相机中坐标
|
||||
vec4 vcPos = camera_view_matrix * wordPos;
|
||||
float near = .001 * helsing_viewDistance;
|
||||
float dis = length(vcPos.xyz);
|
||||
if(dis > near && dis < helsing_viewDistance){
|
||||
// 透视投影
|
||||
vec4 posInEye = camera_projection_matrix * vcPos;
|
||||
// 可视区颜色
|
||||
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
|
||||
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
|
||||
if(visible(posInEye)){
|
||||
float vis = shadow(viewPos);
|
||||
if(vis > 0.3){
|
||||
gl_FragColor = mix(gl_FragColor,helsing_visibleAreaColor,.5);
|
||||
} else {
|
||||
gl_FragColor = mix(gl_FragColor,helsing_invisibleAreaColor,.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
721
src/Obj/Analysis/ViewShed2/index.js
Normal file
721
src/Obj/Analysis/ViewShed2/index.js
Normal file
@ -0,0 +1,721 @@
|
||||
// ViewShed.js
|
||||
import glsl from './glsl'
|
||||
import Event from "../../../Event";
|
||||
import MouseTip from "../../../MouseTip";
|
||||
import Tools from "../../../Tools";
|
||||
import Controller from "../../../Controller";
|
||||
import Dialog from '../../../BaseDialog';
|
||||
import { html } from "./_element";
|
||||
/**
|
||||
* @constructor
|
||||
* @description 可视域分析(测试中)
|
||||
* @param sdk
|
||||
* @param {Object} options 选项。
|
||||
* @param {Cesium.Cartesian3} options.viewPosition 观测点位置。
|
||||
* @param {Cesium.Cartesian3} options.viewPositionEnd 最远观测点位置(如果设置了观测距离,这个属性可以不设置)。
|
||||
* @param {Number} options.viewDistance 观测距离(单位`米`)。
|
||||
* @param {Number} options.viewHeading 航向角(单位`度`)。
|
||||
* @param {Number} options.viewPitch 俯仰角(单位`度`)。
|
||||
* @param {Number} options.horizontalViewAngle=90 可视域水平夹角(单位`度`)。
|
||||
* @param {Number} options.verticalViewAngle=60 可视域垂直夹角(单位`度`)。
|
||||
* @param {String} options.visibleAreaColor=#008000 可视区域颜色(默认值`绿色`)。
|
||||
* @param {String} options.invisibleAreaColor=#FF0000 不可视区域颜色(默认值`红色`)。
|
||||
*/
|
||||
class ViewShedStage extends Tools {
|
||||
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
super(sdk, options)
|
||||
|
||||
// if (Object.hasOwn(options.viewPosition, 'lng') && Object.hasOwn(options.viewPosition, 'lat') && Object.hasOwn(options.viewPosition, 'alt')) {
|
||||
// this.error = '请提供观测点位置!'
|
||||
// window.ELEMENT && window.ELEMENT.Message({
|
||||
// message: '请提供观测点位置!',
|
||||
// type: 'warning',
|
||||
// duration: 1500
|
||||
// });
|
||||
// return
|
||||
// }
|
||||
|
||||
this.viewer = sdk.viewer;
|
||||
this.options = {}
|
||||
this.options.viewPosition = options.viewPosition;
|
||||
this.options.viewPositionEnd = options.viewPositionEnd;
|
||||
this.options.horizontalViewAngle = (options.horizontalViewAngle || options.horizontalViewAngle === 0) ? options.horizontalViewAngle : 90.0;
|
||||
this.options.verticalViewAngle = (options.verticalViewAngle || options.verticalViewAngle === 0) ? options.verticalViewAngle : 60.0;
|
||||
this.options.visibleAreaColor = options.visibleAreaColor || '#008000';
|
||||
this.options.invisibleAreaColor = options.invisibleAreaColor || '#FF0000';
|
||||
// this.enabled = (typeof options.enabled === "boolean") ? options.enabled : true;
|
||||
// this.softShadows = (typeof options.softShadows === "boolean") ? options.softShadows : true;
|
||||
// this.size = options.size || 10240; // 2048
|
||||
this.ids = []
|
||||
this.Dialog = _Dialog
|
||||
this.html = null
|
||||
YJ.Analysis.AnalysesResults.push(this)
|
||||
ViewShedStage.create(this)
|
||||
// ViewShedStage.edit(this)
|
||||
// this.update();
|
||||
}
|
||||
|
||||
get viewPosition() {
|
||||
return this.options.viewPosition
|
||||
}
|
||||
|
||||
set viewPosition(v) {
|
||||
this.options.viewPosition = v
|
||||
this.ids[0] && (this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
|
||||
this.update()
|
||||
// let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
|
||||
}
|
||||
|
||||
get viewPositionEnd() {
|
||||
return this.options.viewPositionEnd
|
||||
}
|
||||
|
||||
set viewPositionEnd(v) {
|
||||
this.options.viewPositionEnd = v
|
||||
this.ids[1] && (this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
|
||||
this.update()
|
||||
// let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
// this.viewDistance = this.viewPositionEnd ? Cesium.Cartesian3.distance(this.viewPosition, this.viewPositionEnd) : (options.viewDistance || 100.0);
|
||||
}
|
||||
|
||||
get horizontalViewAngle() {
|
||||
return this.options.horizontalViewAngle
|
||||
}
|
||||
|
||||
set horizontalViewAngle(v) {
|
||||
this.options.horizontalViewAngle = v
|
||||
if (this._DialogObject && this._DialogObject._element && this._DialogObject._element.content) {
|
||||
let contentElm = this._DialogObject._element.content
|
||||
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
|
||||
e_horizontalViewAngle.value = v
|
||||
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
|
||||
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
|
||||
rangeNodeActiveText.innerHTML = v + '°';
|
||||
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
|
||||
rangeProcess.style.width = v / 180 * 100 + '%'
|
||||
}
|
||||
this.update()
|
||||
}
|
||||
|
||||
get visibleAreaColor() {
|
||||
return this.options.visibleAreaColor
|
||||
}
|
||||
|
||||
set visibleAreaColor(v) {
|
||||
this.options.visibleAreaColor = v
|
||||
this.update()
|
||||
}
|
||||
|
||||
get invisibleAreaColor() {
|
||||
return this.options.invisibleAreaColor
|
||||
}
|
||||
|
||||
set invisibleAreaColor(v) {
|
||||
this.options.invisibleAreaColor = v
|
||||
this.update()
|
||||
}
|
||||
|
||||
get verticalViewAngle() {
|
||||
return this.options.verticalViewAngle
|
||||
}
|
||||
|
||||
set verticalViewAngle(v) {
|
||||
this.options.verticalViewAngle = v
|
||||
this.update()
|
||||
}
|
||||
|
||||
get viewDistance() {
|
||||
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
|
||||
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
let distance = Cesium.Cartesian3.distance(viewPosition3, viewPositionEnd3)
|
||||
return distance
|
||||
}
|
||||
|
||||
get viewHeading() {
|
||||
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
|
||||
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
let heading = getHeading(viewPosition3, viewPositionEnd3)
|
||||
if (this.html) {
|
||||
let e_viewHeading = this.html.querySelector("span[name='viewHeading']")
|
||||
e_viewHeading.innerHTML = Number(heading.toFixed(8))
|
||||
}
|
||||
return heading
|
||||
}
|
||||
|
||||
get viewPitch() {
|
||||
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
|
||||
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
let pitch = getPitch(viewPosition3, viewPositionEnd3)
|
||||
if (this.html) {
|
||||
let e_viewPitch = this.html.querySelector("span[name='viewPitch']")
|
||||
e_viewPitch.innerHTML = Number(pitch.toFixed(8))
|
||||
}
|
||||
return pitch
|
||||
}
|
||||
|
||||
static create(that) {
|
||||
let count = 0;
|
||||
if (!YJ.Measure.GetMeasureStatus()) {
|
||||
that.event = new Event(that.sdk)
|
||||
that.tip = new MouseTip('左键选择观测点位置,右键取消', that.sdk)
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
that.event.mouse_left((movement, cartesian) => {
|
||||
if (!that.viewPosition) {
|
||||
that.options.viewPosition = that.cartesian3Towgs84(cartesian, that.viewer)
|
||||
that.ids.push(ViewShedStage.create_point(that, cartesian))
|
||||
that.tip.set_text("左键选择最远观测点位置,右键取消")
|
||||
}
|
||||
count++
|
||||
if (count === 2) {
|
||||
that.options.viewPositionEnd = that.cartesian3Towgs84(cartesian, that.viewer)
|
||||
that.ids.push(ViewShedStage.create_point(that, cartesian))
|
||||
end()
|
||||
that.update()
|
||||
}
|
||||
})
|
||||
that.event.mouse_move((movement, cartesian) => {
|
||||
that.tip.setPosition(cartesian, movement.endPosition.x, movement.endPosition.y)
|
||||
})
|
||||
that.event.mouse_right((movement, cartesian) => {
|
||||
that.ids.forEach(id => {
|
||||
that.viewer.entities.removeById(id)
|
||||
})
|
||||
that.ids = []
|
||||
end()
|
||||
})
|
||||
that.event.gesture_pinck_start((movement, cartesian) => {
|
||||
let startTime = new Date()
|
||||
that.event.gesture_pinck_end(() => {
|
||||
let endTime = new Date()
|
||||
if (endTime - startTime >= 500) {
|
||||
that.ids.forEach(id => {
|
||||
that.viewer.entities.removeById(id)
|
||||
})
|
||||
that.ids = []
|
||||
end()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
console.log('上一次测量未结束')
|
||||
}
|
||||
|
||||
function end() {
|
||||
that.ids.forEach(id => {
|
||||
let entity = that.viewer.entities.getById(id)
|
||||
entity.show = false
|
||||
})
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
that.tip.destroy()
|
||||
that.event.destroy()
|
||||
that.tip = null
|
||||
that.event = null
|
||||
if (count === 2) {
|
||||
ViewShedStage.edit(that)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static create_point(that, cartesian) {
|
||||
let id = that.randomString()
|
||||
let p = that.cartesian3Towgs84(cartesian, that.viewer)
|
||||
let params = {
|
||||
id: id,
|
||||
position: Cesium.Cartesian3.fromDegrees(p.lng, p.lat, p.alt),
|
||||
billboard: {
|
||||
image: that.getSourceRootPath() + '/img/point.png',
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
color: Cesium.Color.WHITE.withAlpha(0.99)
|
||||
}
|
||||
}
|
||||
that.viewer.entities.add(
|
||||
new Cesium.Entity(params)
|
||||
)
|
||||
return id
|
||||
}
|
||||
|
||||
|
||||
add() {
|
||||
this.createLightCamera();
|
||||
this.createShadowMap();
|
||||
this.createPostStage();
|
||||
this.drawFrustumOutline();
|
||||
this.drawSketch();
|
||||
}
|
||||
|
||||
update() {
|
||||
this.clear();
|
||||
this.add();
|
||||
}
|
||||
|
||||
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)
|
||||
that.editevent && that.editevent.destroy()
|
||||
that.ControllerObject && that.ControllerObject.destroy()
|
||||
that.ids.forEach(id => {
|
||||
that.viewer.entities.removeById(id)
|
||||
})
|
||||
},
|
||||
})
|
||||
await that._DialogObject.init()
|
||||
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' view-shed'
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
that._DialogObject.contentAppChild(contentElm)
|
||||
let resetBtn = that._DialogObject._element.body.getElementsByClassName('edit')[0];
|
||||
resetBtn.addEventListener('click', () => {
|
||||
that.nodeEdit()
|
||||
})
|
||||
that.html = contentElm
|
||||
//经度值
|
||||
let e_lng = contentElm.querySelector("span[name='lng']")
|
||||
e_lng.innerHTML = Number(that.options.viewPosition.lng.toFixed(8))
|
||||
|
||||
//纬度值
|
||||
let e_lat = contentElm.querySelector("span[name='lat']")
|
||||
e_lat.innerHTML = Number(that.options.viewPosition.lat.toFixed(8))
|
||||
|
||||
//高度值
|
||||
let e_alt = contentElm.querySelector("span[name='alt']")
|
||||
e_alt.innerHTML = Number(that.options.viewPosition.alt.toFixed(8))
|
||||
|
||||
//偏航角
|
||||
let e_viewHeading = contentElm.querySelector("span[name='viewHeading']")
|
||||
e_viewHeading.innerHTML = Number(that.viewHeading.toFixed(8))
|
||||
|
||||
//俯仰角
|
||||
let e_viewPitch = contentElm.querySelector("span[name='viewPitch']")
|
||||
e_viewPitch.innerHTML = Number(that.viewPitch.toFixed(8))
|
||||
|
||||
//视域夹角
|
||||
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
|
||||
e_horizontalViewAngle.value = that.options.horizontalViewAngle
|
||||
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
|
||||
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
|
||||
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
|
||||
let percentage = that.horizontalViewAngle / 180 * 100
|
||||
rangeNodeActive.style.left = percentage + '%';
|
||||
rangeProcess.style.width = percentage + '%'
|
||||
rangeNodeActiveText.innerHTML = that.horizontalViewAngle + '°';
|
||||
let timeout
|
||||
e_horizontalViewAngle.addEventListener('input', () => {
|
||||
let percentage = e_horizontalViewAngle.value / 180 * 100
|
||||
rangeNodeActive.style.left = percentage + '%';
|
||||
rangeProcess.style.width = percentage + '%';
|
||||
rangeNodeActiveText.innerHTML = e_horizontalViewAngle.value + '°';
|
||||
})
|
||||
e_horizontalViewAngle.addEventListener('change', () => {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => {
|
||||
that.horizontalViewAngle = e_horizontalViewAngle.value;
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
|
||||
clear() {
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
this.tip && this.tip.destroy()
|
||||
this.event && this.event.destroy()
|
||||
this.tip = null
|
||||
this.event = null
|
||||
if (this.sketch) {
|
||||
this.viewer.entities.removeById(this.sketch.id);
|
||||
this.sketch = null;
|
||||
}
|
||||
if (this.frustumOutline) {
|
||||
this.frustumOutline.destroy();
|
||||
this.frustumOutline = null;
|
||||
}
|
||||
if (this.FrustumBottomSurface) {
|
||||
this.FrustumBottomSurface.destroy();
|
||||
this.FrustumBottomSurface = null;
|
||||
}
|
||||
if (this.postStage) {
|
||||
this.viewer.scene.postProcessStages.remove(this.postStage);
|
||||
this.postStage = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.clear()
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
this.ids.forEach(id => {
|
||||
this.viewer.entities.removeById(id)
|
||||
})
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
}
|
||||
|
||||
nodeEdit() {
|
||||
if (YJ.Measure.GetMeasureStatus()) {
|
||||
console.log('上一次测量未结束')
|
||||
}
|
||||
else {
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ids.forEach(id => {
|
||||
let entity = this.viewer.entities.getById(id)
|
||||
entity.show = true
|
||||
})
|
||||
let selectPoint
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
// this.tip = new MouseTip('左键选择要操作的观测点,右键取消', this.sdk)
|
||||
this.editevent = new Event(this.sdk)
|
||||
this.editevent.mouse_left((movement, cartesian) => {
|
||||
let pick = this.viewer.scene.pick(movement.position);
|
||||
if (pick && pick.id && pick.id.id && this.ids.indexOf(pick.id.id) != -1 && (!selectPoint || selectPoint.id != pick.id.id)) {
|
||||
selectPoint = pick.id
|
||||
// this.event.destroy()
|
||||
// this.tip.destroy()
|
||||
this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(this.viewPosition.lng, this.viewPosition.lat, this.viewPosition.alt)
|
||||
this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(this.viewPositionEnd.lng, this.viewPositionEnd.lat, this.viewPositionEnd.alt)
|
||||
this.viewPosition
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
console.log(this.cartesian3Towgs84(selectPoint.position._value, this.sdk.viewer))
|
||||
this.ControllerObject = new Controller(this.sdk, { position: { ...this.cartesian3Towgs84(selectPoint.position._value, this.sdk.viewer) } })
|
||||
this.ControllerObject.controllerCallBack = (params, status) => {
|
||||
if (params.position.alt < 0) {
|
||||
params.position.alt = 0
|
||||
}
|
||||
selectPoint.position = new Cesium.Cartesian3.fromDegrees(params.position.lng, params.position.lat, params.position.alt)
|
||||
if (status) {
|
||||
if (this.ids.indexOf(pick.id.id) == 0) {
|
||||
this.viewPosition = params.position
|
||||
}
|
||||
else {
|
||||
this.viewPositionEnd = params.position
|
||||
}
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
}
|
||||
}
|
||||
this.ControllerObject.editTranslational()
|
||||
}
|
||||
})
|
||||
this.editevent.mouse_right((movement, cartesian) => {
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
this.ids.forEach(id => {
|
||||
let entity = this.viewer.entities.getById(id)
|
||||
entity.show = false
|
||||
})
|
||||
selectPoint = null
|
||||
})
|
||||
this.editevent.mouse_move((movement, cartesian) => {
|
||||
// this.tip.setPosition(
|
||||
// cartesian,
|
||||
// movement.endPosition.x,
|
||||
// movement.endPosition.y
|
||||
// )
|
||||
})
|
||||
|
||||
this.editevent.gesture_pinck_start((movement, cartesian) => {
|
||||
let startTime = new Date()
|
||||
this.editevent.gesture_pinck_end(() => {
|
||||
let endTime = new Date()
|
||||
if (endTime - startTime >= 500) {
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
this.ids.forEach(id => {
|
||||
let entity = this.viewer.entities.getById(id)
|
||||
entity.show = false
|
||||
})
|
||||
selectPoint = null
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createLightCamera() {
|
||||
let _this = this
|
||||
this.lightCamera = new Cesium.Camera(this.viewer.scene);
|
||||
this.lightCamera.position = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt);
|
||||
// if (this.viewPositionEnd) {
|
||||
// let direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(this.viewPositionEnd, this.viewPosition, new Cesium.Cartesian3()), new Cesium.Cartesian3());
|
||||
// this.lightCamera.direction = direction; // direction是相机面向的方向
|
||||
// }
|
||||
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: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(this.viewHeading || 0),
|
||||
pitch: Cesium.Math.toRadians(this.viewPitch || 0),
|
||||
roll: 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createShadowMap() {
|
||||
this.shadowMap = new Cesium.ShadowMap({
|
||||
context: (this.viewer.scene).context,
|
||||
lightCamera: this.lightCamera,
|
||||
enabled: true,
|
||||
isPointLight: true,
|
||||
pointLightRadius: this.viewDistance,
|
||||
cascadesEnabled: false,
|
||||
size: 2048, // 2048
|
||||
softShadows: true,
|
||||
normalOffset: false,
|
||||
fromLightSource: false
|
||||
});
|
||||
this.viewer.scene.shadowMap = this.shadowMap;
|
||||
}
|
||||
|
||||
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: Cesium.Color.fromCssColorString(this.visibleAreaColor),
|
||||
helsing_invisibleAreaColor: Cesium.Color.fromCssColorString(this.invisibleAreaColor),
|
||||
shadowMap: this.shadowMap,
|
||||
far: () => {
|
||||
return this.viewDistance;
|
||||
},
|
||||
}
|
||||
});
|
||||
this.postStage = this.viewer.scene.postProcessStages.add(postStage);
|
||||
}
|
||||
|
||||
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: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
|
||||
orientation: orientation
|
||||
}),
|
||||
id: Math.random().toString(36).substr(2),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.YELLOWGREEN//new Cesium.Color(0.0, 1.0, 0.0, 1.0)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true)
|
||||
}
|
||||
});
|
||||
|
||||
let frustum = this.lightCamera.frustum.clone()
|
||||
frustum.near = frustum.far - 100
|
||||
let instance2 = new Cesium.GeometryInstance({
|
||||
geometry: new Cesium.FrustumGeometry({
|
||||
frustum: frustum,
|
||||
origin: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
|
||||
orientation: orientation,
|
||||
vertexFormat: Cesium.VertexFormat.POSITION_ONLY,
|
||||
}),
|
||||
id: Math.random().toString(36).substr(2),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.YELLOWGREEN//new Cesium.Color(0.0, 1.0, 0.0, 1.0)
|
||||
),
|
||||
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
|
||||
})
|
||||
})
|
||||
);
|
||||
|
||||
const radius = this.viewDistance;
|
||||
const angle = this.viewHeading;
|
||||
|
||||
let Ea = 6378137; // 赤道半径
|
||||
let Eb = 6356725; // 极半径
|
||||
const lng = this.options.viewPosition.lng,
|
||||
lat = this.options.viewPosition.lat;
|
||||
let positionArr = [];
|
||||
let boundaryPoints = [];
|
||||
positionArr.push(lng, lat);
|
||||
boundaryPoints.push([lng, lat]);
|
||||
//正北是0°
|
||||
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
|
||||
let end = start + 90;
|
||||
for (let i = start; i <= end; i++) {
|
||||
let dx = radius * Math.sin((i * Math.PI) / 180.0);
|
||||
let dy = radius * Math.cos((i * Math.PI) / 180.0);
|
||||
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
|
||||
let ed = ec * Math.cos((lat * Math.PI) / 180);
|
||||
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
|
||||
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
|
||||
positionArr.push(BJD, BWD);
|
||||
}
|
||||
|
||||
|
||||
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
Cesium.Cartesian3.fromDegreesArray(positionArr)
|
||||
),
|
||||
height: 0.0,
|
||||
extrudedHeight: 0.0,
|
||||
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
|
||||
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
|
||||
ellipsoid: Cesium.Ellipsoid.WGS84,
|
||||
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
|
||||
perPositionHeight: false, // 每个位置点使用的高度
|
||||
closeTop: true,
|
||||
closeBottom: true,
|
||||
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
|
||||
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
|
||||
});
|
||||
let polygonInstance = new Cesium.GeometryInstance({
|
||||
geometry: polygon,
|
||||
name: "ViewershedPolygon",
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.BLUE
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
},
|
||||
});
|
||||
// this.FrustumBottomSurface = this.viewer.scene.primitives.add(
|
||||
// new Cesium.GroundPrimitive({
|
||||
// geometryInstances: polygonInstance,
|
||||
// appearance: new Cesium.PerInstanceColorAppearance({
|
||||
// translucent: true, //false时透明度无效
|
||||
// closed: false,
|
||||
// }),
|
||||
// })
|
||||
// );
|
||||
}
|
||||
|
||||
drawSketch() {
|
||||
this.sketch = this.viewer.entities.add({
|
||||
name: 'sketch',
|
||||
position: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
|
||||
orientation: Cesium.Transforms.headingPitchRollQuaternion(
|
||||
Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
|
||||
Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - 90, this.viewPitch, 0.0)
|
||||
),
|
||||
ellipsoid: {
|
||||
radii: new Cesium.Cartesian3(
|
||||
this.viewDistance,
|
||||
this.viewDistance,
|
||||
this.viewDistance
|
||||
),
|
||||
// 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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function 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));
|
||||
}
|
||||
|
||||
function 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 ViewShedStage;
|
||||
74
src/Obj/Analysis/ViewShed3/_element.js
Normal file
74
src/Obj/Analysis/ViewShed3/_element.js
Normal file
@ -0,0 +1,74 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">位置拾取(起点、终点)</span>
|
||||
<button class="edit"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>拾取</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row subtitle-box">
|
||||
<span class="subtitle">视域夹角</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="range-box">
|
||||
<div class="range-bg">
|
||||
<div class="range-process-box">
|
||||
<div class="range-process"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-node-box">
|
||||
<span class="range-node-text">0°</span>
|
||||
<span class="range-node-text">45°</span>
|
||||
<span class="range-node-text">90°</span>
|
||||
<span class="range-node-text">135°</span>
|
||||
<span class="range-node-text">180°</span>
|
||||
<div class="range-node-active"><span class="range-node-active-text">0°</span></div>
|
||||
</div>
|
||||
<input type="range" max="180" min="0" step="1" name="horizontalViewAngle">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">经度:</span>
|
||||
<span class="text-number" name="lng"></span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">偏航角:</span>
|
||||
<span class="text-number" name="viewHeading"></span>
|
||||
<span class="unit">°</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">纬度:</span>
|
||||
<span class="text-number" name="lat"></span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">俯仰角:</span>
|
||||
<span class="text-number" name="viewPitch"></span>
|
||||
<span class="unit">°</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">高度:</span>
|
||||
<span class="text-number" name="alt"></span>
|
||||
<span class="unit text-number" style="margin-left: 5px;">m</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
512
src/Obj/Analysis/ViewShed3/_index.js
Normal file
512
src/Obj/Analysis/ViewShed3/_index.js
Normal file
@ -0,0 +1,512 @@
|
||||
/*
|
||||
* @Author: Wang jianLei
|
||||
* @Date: 2022-05-17 21:49:28
|
||||
* @Last Modified by: Wang JianLei
|
||||
* @Last Modified time: 2022-05-19 22:08:14
|
||||
*/
|
||||
|
||||
let ViewShed = function (sdk, canvasEleId) {
|
||||
if (!sdk.viewer) throw new Error("no viewer object!");
|
||||
alert(canvasEleId)
|
||||
let canvasEle = document.getElementById(canvasEleId);
|
||||
if (!canvasEle) throw new Error("the canvas element is not exist");
|
||||
this.canvasEle = canvasEle;
|
||||
this.viewer = sdk.viewer;
|
||||
this.handler = undefined;
|
||||
this.lightCamera;
|
||||
this.pyramid;
|
||||
this.frustumPrimitive;
|
||||
this.viewershedPolygon;
|
||||
};
|
||||
ViewShed.prototype = {
|
||||
/**
|
||||
* 初始化handler
|
||||
*/
|
||||
initHandler() {
|
||||
if (this.handler) {
|
||||
this.handler.destroy();
|
||||
this.handler = undefined;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 开始执行视域分析
|
||||
* @param {number} precision 精度,值越大创建耗时越长,建议在10~20之间
|
||||
*/
|
||||
createViewshed: function (precision) {
|
||||
let $this = this;
|
||||
let scene = $this.viewer.scene;
|
||||
$this.initHandler();
|
||||
$this.clearAll();
|
||||
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
|
||||
$this.handler.setInputAction((event) => {
|
||||
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
|
||||
scene.screenSpaceCameraController.enableRotate = false;
|
||||
scene.screenSpaceCameraController.enableZoom = false;
|
||||
scene.globe.depthTestAgainstTerrain = true;
|
||||
let earthPosition = scene.pickPosition(event.position);
|
||||
let pos = $this.cartesian3ToDegree(earthPosition);
|
||||
$this.handler.setInputAction(function (event) {
|
||||
let newPosition = scene.pickPosition(event.endPosition);
|
||||
if (Cesium.defined(newPosition)) {
|
||||
let pos1 = $this.cartesian3ToDegree(newPosition);
|
||||
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
|
||||
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
|
||||
let pitch = $this.getPitch(earthPosition, newPosition);
|
||||
$this.ViewShedOptions = {
|
||||
viewPosition: earthPosition, //观测点 笛卡尔坐标
|
||||
endPosition: newPosition, //目标点 笛卡尔坐标
|
||||
direction: angle, //观测方位角 默认为`0`,范围`0~360`
|
||||
pitch: pitch, //俯仰角,radius,默认为`0`
|
||||
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
|
||||
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
|
||||
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
|
||||
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
|
||||
visualRange: distance, //距离,单位`米`
|
||||
};
|
||||
$this.updateViewShed();
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
|
||||
|
||||
$this.handler.setInputAction(() => {
|
||||
$this.initHandler();
|
||||
// 开启地球旋转和缩放
|
||||
scene.screenSpaceCameraController.enableRotate = true;
|
||||
scene.screenSpaceCameraController.enableZoom = true;
|
||||
$this.drawViewershed(precision);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_UP);
|
||||
},
|
||||
|
||||
ReturnDistance(pos0, pos1) {
|
||||
let distance = 0;
|
||||
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
|
||||
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
|
||||
/**根据经纬度计算出距离**/
|
||||
let geodesic = new Cesium.EllipsoidGeodesic();
|
||||
geodesic.setEndPoints(point1cartographic, point2cartographic);
|
||||
let s = geodesic.surfaceDistance;
|
||||
return s;
|
||||
},
|
||||
getHeight(x, y, objectsToExclude) {
|
||||
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
|
||||
let endHeight = this.viewer.scene.sampleHeight(
|
||||
endCartographic,
|
||||
objectsToExclude
|
||||
);
|
||||
return endHeight;
|
||||
},
|
||||
|
||||
cartesian3ToDegree: function (Cartesian3) {
|
||||
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
|
||||
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
|
||||
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
|
||||
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
|
||||
let _alt = _cartographic.height;
|
||||
return [_lng, _lat, _alt];
|
||||
},
|
||||
getAngle: function (lng1, lat1, lng2, lat2) {
|
||||
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
|
||||
if (lng2 >= lng1) {
|
||||
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
|
||||
} else {
|
||||
dRotateAngle =
|
||||
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
|
||||
}
|
||||
dRotateAngle = (dRotateAngle * 180) / Math.PI;
|
||||
return dRotateAngle;
|
||||
},
|
||||
|
||||
getPitch(pointA, pointB) {
|
||||
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
|
||||
const vector = Cesium.Cartesian3.subtract(
|
||||
pointB,
|
||||
pointA,
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
let direction = Cesium.Matrix4.multiplyByPointAsVector(
|
||||
Cesium.Matrix4.inverse(transfrom, transfrom),
|
||||
vector,
|
||||
vector
|
||||
);
|
||||
Cesium.Cartesian3.normalize(direction, direction);
|
||||
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
|
||||
},
|
||||
|
||||
updateViewShed: function () {
|
||||
this.clear();
|
||||
this.setLightCamera();
|
||||
this.addVisualPyramid();
|
||||
this.createFrustum();
|
||||
},
|
||||
clear: function () {
|
||||
if (this.pyramid) {
|
||||
this.viewer.entities.removeById(this.pyramid.id);
|
||||
this.pyramid = undefined;
|
||||
}
|
||||
if (this.frustumPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.frustumPrimitive);
|
||||
this.frustumPrimitive = undefined;
|
||||
}
|
||||
if (this.debugModelMatrixPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
|
||||
this.debugModelMatrixPrimitive = undefined;
|
||||
}
|
||||
},
|
||||
clearAll: function () {
|
||||
this.clear();
|
||||
if (this.viewershedPolygon) {
|
||||
this.viewer.scene.primitives.remove(this.viewershedPolygon);
|
||||
this.viewershedPolygon = undefined;
|
||||
}
|
||||
},
|
||||
addVisualPyramid: function () {
|
||||
let options = this.ViewShedOptions;
|
||||
let position = options.viewPosition;
|
||||
let visualRange = Number(options.visualRange);
|
||||
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
|
||||
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.DebugModelMatrixPrimitive({
|
||||
modelMatrix: transform,
|
||||
length: 5.0,
|
||||
})
|
||||
);
|
||||
const halfClock = options.horizontalViewAngle / 2;
|
||||
const halfCone = options.verticalViewAngle / 2;
|
||||
const pitch = Cesium.Math.toDegrees(options.pitch);
|
||||
const ellipsoid = new Cesium.EllipsoidGraphics({
|
||||
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
|
||||
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
|
||||
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
|
||||
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
|
||||
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
|
||||
fill: false,
|
||||
outline: true,
|
||||
subdivisions: 256,
|
||||
stackPartitions: 64,
|
||||
slicePartitions: 64,
|
||||
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
|
||||
});
|
||||
const pyramidEntity = new Cesium.Entity({
|
||||
position: position,
|
||||
ellipsoid,
|
||||
});
|
||||
this.pyramid = this.viewer.entities.add(pyramidEntity);
|
||||
},
|
||||
setLightCamera: function () {
|
||||
if (!this.lightCamera) {
|
||||
this.lightCamera = new Cesium.Camera(this.viewer.scene);
|
||||
}
|
||||
let options = this.ViewShedOptions;
|
||||
let visualRange = Number(options.visualRange);
|
||||
this.lightCamera.position = options.viewPosition;
|
||||
this.lightCamera.frustum.near = 0.1;
|
||||
this.lightCamera.frustum.far = visualRange;
|
||||
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
|
||||
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
|
||||
this.lightCamera.frustum.aspectRatio =
|
||||
(visualRange * Math.tan(hr / 2) * 2) /
|
||||
(visualRange * Math.tan(vr / 2) * 2);
|
||||
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
|
||||
this.lightCamera.setView({
|
||||
destination: options.viewPosition,
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(options.direction || 0),
|
||||
pitch: options.pitch || 0,
|
||||
roll: 0,
|
||||
},
|
||||
});
|
||||
},
|
||||
createFrustum: function () {
|
||||
const scratchRight = new Cesium.Cartesian3();
|
||||
const scratchRotation = new Cesium.Matrix3();
|
||||
const scratchOrientation = new Cesium.Quaternion();
|
||||
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 instanceOutline = new Cesium.GeometryInstance({
|
||||
geometry: new Cesium.FrustumOutlineGeometry({
|
||||
frustum: this.lightCamera.frustum,
|
||||
origin: this.ViewShedOptions.viewPosition,
|
||||
orientation: orientation,
|
||||
}),
|
||||
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true),
|
||||
},
|
||||
});
|
||||
this.frustumPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.Primitive({
|
||||
geometryInstances: instanceOutline,
|
||||
appearance: new Cesium.PerInstanceColorAppearance({
|
||||
flat: true,
|
||||
translucent: false,
|
||||
closed: true,
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
createPoint: function (firstPos, secondPos) {
|
||||
let entity4FirstPos = new Cesium.Entity({
|
||||
name: "firstPos",
|
||||
show: true,
|
||||
position: firstPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 20,
|
||||
color: Cesium.Color.RED,
|
||||
outlineColor: Cesium.Color.YELLOW,
|
||||
outlineWidth: 5,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体起点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4FirstPos);
|
||||
let entity4SecondPos = new Cesium.Entity({
|
||||
name: "secondPos",
|
||||
show: true,
|
||||
position: secondPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 30,
|
||||
color: Cesium.Color.YELLOW,
|
||||
outlineColor: Cesium.Color.RED,
|
||||
outlineWidth: 8,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体视角终点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4SecondPos);
|
||||
},
|
||||
|
||||
//绘制可视域
|
||||
add(positionArr) {
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
Cesium.Cartesian3.fromDegreesArray(positionArr)
|
||||
),
|
||||
height: 0.0,
|
||||
extrudedHeight: 0.0,
|
||||
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
|
||||
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
|
||||
ellipsoid: Cesium.Ellipsoid.WGS84,
|
||||
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
|
||||
perPositionHeight: false, // 每个位置点使用的高度
|
||||
closeTop: true,
|
||||
closeBottom: true,
|
||||
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
|
||||
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
|
||||
});
|
||||
|
||||
let polygonInstance = new Cesium.GeometryInstance({
|
||||
geometry: polygon,
|
||||
name: "ViewershedPolygon",
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.BLUE.withAlpha(0.6)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
},
|
||||
});
|
||||
this.viewershedPolygon = this.viewer.scene.primitives.add(
|
||||
new Cesium.GroundPrimitive({
|
||||
geometryInstances: polygonInstance,
|
||||
appearance: new Cesium.EllipsoidSurfaceAppearance({
|
||||
aboveGround: true,
|
||||
material: new Cesium.Material({
|
||||
fabric: {
|
||||
type: "Image",
|
||||
uniforms: {
|
||||
image: this.returnImgae(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
drawViewershed(precision) {
|
||||
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
|
||||
const radius = this.ViewShedOptions.visualRange;
|
||||
const direction = this.ViewShedOptions.direction;
|
||||
let boundary = this.computeBoundaryOptions(pos, radius, direction);
|
||||
const bbox = boundary.bbox;
|
||||
let mask = turf.polygon([boundary.boundaryPoints]);
|
||||
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
|
||||
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
|
||||
|
||||
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
|
||||
let variogram = kriging.train(
|
||||
pointsResult.values,
|
||||
pointsResult.lngs,
|
||||
pointsResult.lats,
|
||||
"exponential",
|
||||
0,
|
||||
100
|
||||
);
|
||||
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
|
||||
const colors = [
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
];
|
||||
|
||||
this.canvasEle.width = 3840;
|
||||
this.canvasEle.height = 2160;
|
||||
kriging.plot(
|
||||
this.canvasEle,
|
||||
grid,
|
||||
[bbox[0], bbox[2]],
|
||||
[bbox[1], bbox[3]],
|
||||
colors
|
||||
);
|
||||
this.add(boundary.positionArr);
|
||||
},
|
||||
computeBoundaryOptions(pos, radius, angle) {
|
||||
let Ea = 6378137; // 赤道半径
|
||||
let Eb = 6356725; // 极半径
|
||||
const lng = pos[0],
|
||||
lat = pos[1];
|
||||
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
|
||||
let positionArr = [];
|
||||
let boundaryPoints = [];
|
||||
positionArr.push(lng, lat);
|
||||
boundaryPoints.push([lng, lat]);
|
||||
//正北是0°
|
||||
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
|
||||
let end = start + 90;
|
||||
for (let i = start; i <= end; i++) {
|
||||
let dx = radius * Math.sin((i * Math.PI) / 180.0);
|
||||
let dy = radius * Math.cos((i * Math.PI) / 180.0);
|
||||
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
|
||||
let ed = ec * Math.cos((lat * Math.PI) / 180);
|
||||
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
|
||||
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
|
||||
positionArr.push(BJD, BWD);
|
||||
boundaryPoints.push([BJD, BWD]);
|
||||
this.refreshBBox(bbox, BJD, BWD);
|
||||
}
|
||||
boundaryPoints.push([lng, lat]);
|
||||
return {
|
||||
positionArr,
|
||||
boundaryPoints,
|
||||
bbox,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 更新外围矩形 Bbox
|
||||
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
|
||||
* @param {Number} x 经度
|
||||
* @param {Number} y 纬度
|
||||
*/
|
||||
refreshBBox(result, x, y) {
|
||||
result[0] = x < result[0] ? x : result[0];
|
||||
result[1] = y < result[1] ? y : result[1];
|
||||
result[2] = x > result[2] ? x : result[2];
|
||||
result[3] = y > result[3] ? y : result[3];
|
||||
},
|
||||
/**
|
||||
* 插值点用射线判断通视性
|
||||
* @param {*} gridPoints 网格点
|
||||
* @param {*} step 步长,可以理解成是精度
|
||||
* @param {*} sourcePos 视域分析起点
|
||||
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
|
||||
*/
|
||||
createTargetPoints(gridPoints, step, sourcePos) {
|
||||
let positionArr = [];
|
||||
let objectsToExclude = [
|
||||
this.frustumPrimitive,
|
||||
this.pyramid,
|
||||
this.debugModelMatrixPrimitive,
|
||||
];
|
||||
let values = [],
|
||||
lngs = [],
|
||||
lats = [];
|
||||
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
|
||||
positionArr.push({
|
||||
x: sourcePos[0],
|
||||
y: sourcePos[1],
|
||||
z: height,
|
||||
});
|
||||
let viewPoint = this.ViewShedOptions.viewPosition;
|
||||
for (let index = 0; index < gridPoints.features.length; index++) {
|
||||
const feature = gridPoints.features[index];
|
||||
const coords = feature.geometry.coordinates;
|
||||
const x = coords[0],
|
||||
y = coords[1];
|
||||
let h = this.getHeight(x, y, objectsToExclude);
|
||||
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
|
||||
let direction = Cesium.Cartesian3.normalize(
|
||||
Cesium.Cartesian3.subtract(
|
||||
endPoint,
|
||||
viewPoint,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
// 建立射线
|
||||
let ray = new Cesium.Ray(viewPoint, direction);
|
||||
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
|
||||
if (result) {
|
||||
let buffer = this.ReturnDistance(endPoint, result.position);
|
||||
// let M_color = Cesium.Color.GREEN;
|
||||
if (buffer > step) {
|
||||
// M_color = Cesium.Color.RED;
|
||||
values.push(0);
|
||||
} else {
|
||||
values.push(1);
|
||||
}
|
||||
lngs.push(x);
|
||||
lats.push(y);
|
||||
// this.viewer.entities.add(
|
||||
// new Cesium.Entity({
|
||||
// name: "插值点哦",
|
||||
// show: true,
|
||||
// position: endPoint,
|
||||
// point: {
|
||||
// show: true,
|
||||
// pixelSize: 10,
|
||||
// color: M_color,
|
||||
// outlineWidth: 2,
|
||||
// outlineColor: Cesium.Color.YELLOW,
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
}
|
||||
}
|
||||
return {
|
||||
values,
|
||||
lngs,
|
||||
lats,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* canvas转image图片
|
||||
* @returns base64图片
|
||||
*/
|
||||
returnImgae() {
|
||||
return this.canvasEle.toDataURL("image/png");
|
||||
},
|
||||
};
|
||||
|
||||
export default ViewShed;
|
||||
131
src/Obj/Analysis/ViewShed3/glsl.js
Normal file
131
src/Obj/Analysis/ViewShed3/glsl.js
Normal file
@ -0,0 +1,131 @@
|
||||
export default `
|
||||
#define USE_CUBE_MAP_SHADOW true
|
||||
uniform sampler2D colorTexture;
|
||||
uniform sampler2D depthTexture;
|
||||
varying vec2 v_textureCoordinates;
|
||||
uniform mat4 camera_projection_matrix;
|
||||
uniform mat4 camera_view_matrix;
|
||||
uniform samplerCube shadowMap_textureCube;
|
||||
uniform mat4 shadowMap_matrix;
|
||||
uniform vec4 shadowMap_lightPositionEC;
|
||||
uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
|
||||
uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
|
||||
uniform float helsing_viewDistance;
|
||||
uniform vec4 helsing_visibleAreaColor;
|
||||
uniform vec4 helsing_invisibleAreaColor;
|
||||
|
||||
struct zx_shadowParameters
|
||||
{
|
||||
vec3 texCoords;
|
||||
float depthBias;
|
||||
float depth;
|
||||
float nDotL;
|
||||
vec2 texelStepSize;
|
||||
float normalShadingSmooth;
|
||||
float darkness;
|
||||
};
|
||||
|
||||
float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)
|
||||
{
|
||||
float depthBias = shadowParameters.depthBias;
|
||||
float depth = shadowParameters.depth;
|
||||
float nDotL = shadowParameters.nDotL;
|
||||
float normalShadingSmooth = shadowParameters.normalShadingSmooth;
|
||||
float darkness = shadowParameters.darkness;
|
||||
vec3 uvw = shadowParameters.texCoords;
|
||||
depth -= depthBias;
|
||||
float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);
|
||||
return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);
|
||||
}
|
||||
|
||||
vec4 getPositionEC(){
|
||||
return czm_windowToEyeCoordinates(gl_FragCoord);
|
||||
}
|
||||
|
||||
vec3 getNormalEC(){
|
||||
return vec3(1.);
|
||||
}
|
||||
|
||||
vec4 toEye(in vec2 uv,in float depth){
|
||||
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
|
||||
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
|
||||
posInCamera=posInCamera/posInCamera.w;
|
||||
return posInCamera;
|
||||
}
|
||||
|
||||
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
|
||||
vec3 v01=point-planeOrigin;
|
||||
float d=dot(planeNormal,v01);
|
||||
return(point-planeNormal*d);
|
||||
}
|
||||
|
||||
float getDepth(in vec4 depth){
|
||||
float z_window=czm_unpackDepth(depth);
|
||||
z_window=czm_reverseLogDepth(z_window);
|
||||
float n_range=czm_depthRange.near;
|
||||
float f_range=czm_depthRange.far;
|
||||
return(2.*z_window-n_range-f_range)/(f_range-n_range);
|
||||
}
|
||||
|
||||
float shadow(in vec4 positionEC){
|
||||
vec3 normalEC=getNormalEC();
|
||||
zx_shadowParameters shadowParameters;
|
||||
shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
|
||||
shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
|
||||
shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
|
||||
shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
|
||||
vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;
|
||||
float distance=length(directionEC);
|
||||
directionEC=normalize(directionEC);
|
||||
float radius=shadowMap_lightPositionEC.w;
|
||||
if(distance>radius)
|
||||
{
|
||||
return 2.0;
|
||||
}
|
||||
vec3 directionWC=czm_inverseViewRotation*directionEC;
|
||||
shadowParameters.depth=distance/radius-0.0003;
|
||||
shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.,1.);
|
||||
shadowParameters.texCoords=directionWC;
|
||||
float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);
|
||||
return visibility;
|
||||
}
|
||||
|
||||
bool visible(in vec4 result)
|
||||
{
|
||||
result.x/=result.w;
|
||||
result.y/=result.w;
|
||||
result.z/=result.w;
|
||||
return result.x>=-1.&&result.x<=1.
|
||||
&&result.y>=-1.&&result.y<=1.
|
||||
&&result.z>=-1.&&result.z<=1.;
|
||||
}
|
||||
|
||||
void main(){
|
||||
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
|
||||
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
|
||||
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
|
||||
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
|
||||
// 视角 = (纹理坐标, 深度)
|
||||
vec4 viewPos = toEye(v_textureCoordinates, depth);
|
||||
// 世界坐标
|
||||
vec4 wordPos = czm_inverseView * viewPos;
|
||||
// 虚拟相机中坐标
|
||||
vec4 vcPos = camera_view_matrix * wordPos;
|
||||
float near = .001 * helsing_viewDistance;
|
||||
float dis = length(vcPos.xyz);
|
||||
if(dis > near && dis < helsing_viewDistance){
|
||||
// 透视投影
|
||||
vec4 posInEye = camera_projection_matrix * vcPos;
|
||||
// 可视区颜色
|
||||
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
|
||||
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
|
||||
if(visible(posInEye)){
|
||||
float vis = shadow(viewPos);
|
||||
if(vis > 0.3){
|
||||
// gl_FragColor = mix(gl_FragColor,helsing_visibleAreaColor,.5);
|
||||
} else {
|
||||
gl_FragColor = mix(gl_FragColor,helsing_invisibleAreaColor,.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
302
src/Obj/Analysis/ViewShed3/index.js
Normal file
302
src/Obj/Analysis/ViewShed3/index.js
Normal file
@ -0,0 +1,302 @@
|
||||
// ViewShed.js
|
||||
import glsl from './glsl'
|
||||
import Event from "../../../Event";
|
||||
import MouseTip from "../../../MouseTip";
|
||||
import Tools from "../../../Tools";
|
||||
import Controller from "../../../Controller";
|
||||
import Dialog from '../../../BaseDialog';
|
||||
import { html } from "./_element";
|
||||
/**
|
||||
* @constructor
|
||||
* @description 圆形可视域分析(--方块)
|
||||
* @param sdk
|
||||
* @param {Object} options 选项。
|
||||
* @param {String} options.visibleAreaColor=#008000 可视区域颜色(默认值`绿色`)。
|
||||
* @param {String} options.invisibleAreaColor=#FF0000 不可视区域颜色(默认值`红色`)。
|
||||
*/
|
||||
class ViewShedStage extends Tools {
|
||||
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
super(sdk, options)
|
||||
|
||||
this.viewer = sdk.viewer;
|
||||
this.options = {}
|
||||
let precision = Math.floor(options.precision)
|
||||
if (isNaN(precision)) {
|
||||
this.precision = 40
|
||||
}
|
||||
else if (precision < 10) {
|
||||
this.precision = 10
|
||||
}
|
||||
else {
|
||||
this.precision = precision
|
||||
}
|
||||
this.options.visibleAreaColor = options.visibleAreaColor || '#008000';
|
||||
this.options.invisibleAreaColor = options.invisibleAreaColor || '#FF0000';
|
||||
this.ids = []
|
||||
this.primitives = []
|
||||
this.viewpointPrimitive = null
|
||||
this.Dialog = _Dialog
|
||||
this.html = null
|
||||
YJ.Analysis.AnalysesResults.push(this)
|
||||
ViewShedStage.create(this)
|
||||
}
|
||||
|
||||
|
||||
static create(that) {
|
||||
let count = 0;
|
||||
if (!YJ.Measure.GetMeasureStatus()) {
|
||||
let Draw = new YJ.Draw.DrawCircle(that.sdk)
|
||||
Draw.start((a, options) => {
|
||||
that.center = options.center
|
||||
that.radius = options.radius
|
||||
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 + ' view-shed'
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
that._DialogObject.contentAppChild(contentElm)
|
||||
}
|
||||
|
||||
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' } });
|
||||
let pointPrimitives = null;// 申明点渲染集合
|
||||
this.viewpointPrimitive = this.viewer.scene.primitives.add(new Cesium.PointPrimitiveCollection());
|
||||
|
||||
console.log('circle', circle)
|
||||
let bbox = turf.bbox(circle);
|
||||
console.log(bbox)
|
||||
let bboxPolygon = turf.bboxPolygon(bbox);
|
||||
console.log(bboxPolygon)
|
||||
|
||||
let cellSide = radius / this.precision;
|
||||
let grid = turf.pointGrid(bbox, cellSide, { units: 'kilometers' });
|
||||
|
||||
console.log(grid)
|
||||
let ptsWithin = turf.pointsWithinPolygon(grid, circle);
|
||||
console.log('ptsWithin', ptsWithin)
|
||||
|
||||
|
||||
let viewPoint = Cesium.Cartesian3.fromDegrees(this.center.lng, this.center.lat, this.center.alt + 2);
|
||||
this.viewpointPrimitive.add({
|
||||
position: viewPoint,
|
||||
color: Cesium.Color.AQUA.withAlpha(1),
|
||||
pixelSize: 6
|
||||
})
|
||||
|
||||
let instances = []
|
||||
let xp = (bbox[2] - bbox[0]) / (this.precision * 4)
|
||||
let yp = (bbox[3] - bbox[1]) / (this.precision * 4)
|
||||
|
||||
let _this = this
|
||||
let item = 200
|
||||
let m = 0
|
||||
let total = ptsWithin.features.length
|
||||
let intervalEvent = setInterval(() => {
|
||||
if (m >= ptsWithin.features.length) {
|
||||
clearInterval(intervalEvent)
|
||||
return
|
||||
}
|
||||
else {
|
||||
InBatches(m)
|
||||
m += 200
|
||||
}
|
||||
|
||||
}, 30);
|
||||
function InBatches(k) {
|
||||
let instances = []
|
||||
let length = k + 200
|
||||
if (length >= ptsWithin.features.length) {
|
||||
length = ptsWithin.features.length
|
||||
}
|
||||
for (let i = k; i < length; i++) {
|
||||
let positionArr = []
|
||||
let pt = ptsWithin.features[i].geometry.coordinates;
|
||||
let cartographic = Cesium.Cartographic.fromDegrees(pt[0], pt[1]);
|
||||
let height = _this.viewer.scene.globe.getHeight(cartographic)
|
||||
let pt3d = Cesium.Cartesian3.fromDegrees(pt[0], pt[1], height);
|
||||
// let position = this.viewer.scene.clampToHeight(pt3d)
|
||||
|
||||
let targetPoint = pt3d;
|
||||
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);
|
||||
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 - pt3d.x) > 0.01 && Math.abs(result.position.y - pt3d.y) > 0.01 && Math.abs(result.position.z - pt3d.z) > 0.01) {
|
||||
color = Cesium.Color.RED
|
||||
}
|
||||
positionArr.push(pt[0] - xp, pt[1] + yp, pt[0] + xp, pt[1] + yp, pt[0] + xp, pt[1] - yp, pt[0] - xp, pt[1] - yp)
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
Cesium.Cartesian3.fromDegreesArray(positionArr)
|
||||
),
|
||||
height: 0.0,
|
||||
extrudedHeight: 0.0,
|
||||
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
|
||||
ellipsoid: Cesium.Ellipsoid.WGS84,
|
||||
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
|
||||
perPositionHeight: false, // 每个位置点使用的高度
|
||||
closeTop: true,
|
||||
closeBottom: true,
|
||||
});
|
||||
let polygonInstance = new Cesium.GeometryInstance({
|
||||
geometry: polygon,
|
||||
name: "ViewershedPolygon",
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
color.withAlpha(0.6)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
},
|
||||
});
|
||||
instances.push(polygonInstance)
|
||||
}
|
||||
_this.primitives.push(_this.viewer.scene.primitives.add(
|
||||
new Cesium.GroundPrimitive({
|
||||
geometryInstances: instances,
|
||||
appearance: new Cesium.PerInstanceColorAppearance({
|
||||
translucent: true, //false时透明度无效
|
||||
closed: false,
|
||||
}),
|
||||
})
|
||||
));
|
||||
}
|
||||
// for (let i = 0; i < ptsWithin.features.length; i++) {
|
||||
// let positionArr = []
|
||||
// let pt = ptsWithin.features[i].geometry.coordinates;
|
||||
// let cartographic = Cesium.Cartographic.fromDegrees(pt[0], pt[1]);
|
||||
// let height = this.viewer.scene.globe.getHeight(cartographic)
|
||||
// let pt3d = Cesium.Cartesian3.fromDegrees(pt[0], pt[1], height);
|
||||
// // let position = this.viewer.scene.clampToHeight(pt3d)
|
||||
|
||||
// let targetPoint = pt3d;
|
||||
// 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);
|
||||
// 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 - pt3d.x) > 1 && Math.abs(result.position.y - pt3d.y) > 1 && Math.abs(result.position.z - pt3d.z) > 1) {
|
||||
// color = Cesium.Color.RED
|
||||
// // this.viewer.entities.add({
|
||||
// // polyline: {
|
||||
// // positions: [viewPoint, result.position],
|
||||
// // material: Cesium.Color.GREEN.withAlpha(0.1),
|
||||
// // // clampToGround: true,
|
||||
// // width: 1,
|
||||
// // zIndex: 99999999
|
||||
// // },
|
||||
// // });
|
||||
// // this.viewer.entities.add({
|
||||
// // polyline: {
|
||||
// // positions: [result.position, targetPoint],
|
||||
// // material: Cesium.Color.RED.withAlpha(0.1),
|
||||
// // // clampToGround: true,
|
||||
// // width: 1,
|
||||
// // zIndex: 99999999
|
||||
// // },
|
||||
// // });
|
||||
// // pointPrimitives.add({
|
||||
// // position: result.position,
|
||||
// // color: Cesium.Color.AQUA.withAlpha(0.5),
|
||||
// // pixelSize: 6
|
||||
// // })
|
||||
// }
|
||||
// positionArr.push(pt[0] - xp, pt[1] + yp, pt[0] + xp, pt[1] + yp, pt[0] + xp, pt[1] - yp, pt[0] - xp, pt[1] - yp)
|
||||
// let polygon = new Cesium.PolygonGeometry({
|
||||
// polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
// Cesium.Cartesian3.fromDegreesArray(positionArr)
|
||||
// ),
|
||||
// height: 0.0,
|
||||
// extrudedHeight: 0.0,
|
||||
// vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
|
||||
// ellipsoid: Cesium.Ellipsoid.WGS84,
|
||||
// granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
|
||||
// perPositionHeight: false, // 每个位置点使用的高度
|
||||
// closeTop: true,
|
||||
// closeBottom: true,
|
||||
// });
|
||||
// let polygonInstance = new Cesium.GeometryInstance({
|
||||
// geometry: polygon,
|
||||
// name: "ViewershedPolygon",
|
||||
// attributes: {
|
||||
// color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
// color.withAlpha(0.5)
|
||||
// ),
|
||||
// show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
// },
|
||||
// });
|
||||
// instances.push(polygonInstance)
|
||||
// }
|
||||
// this.viewer.scene.primitives.add(
|
||||
// new Cesium.GroundPrimitive({
|
||||
// geometryInstances: instances,
|
||||
// appearance: new Cesium.PerInstanceColorAppearance({
|
||||
// translucent: true, //false时透明度无效
|
||||
// closed: false,
|
||||
// }),
|
||||
// })
|
||||
// );
|
||||
}
|
||||
|
||||
destroy() {
|
||||
for (let i = 0; i < this.primitives.length; i++) {
|
||||
this.viewer.scene.primitives.remove(this.primitives[i])
|
||||
}
|
||||
this.primitives = []
|
||||
this.viewpointPrimitive && this.viewer.scene.primitives.remove(this.viewpointPrimitive)
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default ViewShedStage;
|
||||
19
src/Obj/Analysis/Visibility/_element.js
Normal file
19
src/Obj/Analysis/Visibility/_element.js
Normal file
@ -0,0 +1,19 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">视点高度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="999999" step="0.1" @model="viewPointHeight">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
359
src/Obj/Analysis/Visibility/index.js
Normal file
359
src/Obj/Analysis/Visibility/index.js
Normal file
@ -0,0 +1,359 @@
|
||||
/*
|
||||
*通视分析
|
||||
* @Author: Wang jianLei
|
||||
* @Date: 2022-04-17 22:04:52
|
||||
* @Last Modified by: Wang JianLei
|
||||
* @Last Modified time: 2022-04-17 22:05:13
|
||||
*/
|
||||
import Tools from "../../../Tools";
|
||||
import Event from "../../../Event";
|
||||
import MouseTip from "../../../MouseTip";
|
||||
import EventBinding from '../../Element/Dialog/eventBinding';
|
||||
import Dialog from '../../../BaseDialog';
|
||||
import { html } from "./_element";
|
||||
class VisibilityAnalysis extends Tools {
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
super(sdk, options)
|
||||
this.viewer = sdk.viewer;
|
||||
this.resultObject = {
|
||||
viewPoint: undefined, //通视分析起点
|
||||
targetPoints: [], //通视分析目标点集合
|
||||
targetPoint: undefined, //当前目标点
|
||||
objectExclude: [], //射线排除集合
|
||||
entities: [], //创建的Entity对象
|
||||
};
|
||||
this.options = {}
|
||||
this._elms = {};
|
||||
this.viewPointHeight = options.viewPointHeight
|
||||
this.Dialog = _Dialog
|
||||
this._EventBinding = new EventBinding()
|
||||
YJ.Analysis.AnalysesResults.push(this)
|
||||
VisibilityAnalysis.edit(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
|
||||
})
|
||||
}
|
||||
|
||||
static create(that) {
|
||||
if (!YJ.Measure.GetMeasureStatus()) {
|
||||
if (that._DialogObject && that._DialogObject.close) {
|
||||
that._DialogObject.close()
|
||||
that._DialogObject = null
|
||||
}
|
||||
that.event = new Event(that.sdk)
|
||||
that.tip = new MouseTip('左键点击创建视角起点', that.sdk)
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
let count = 0;
|
||||
that.event.mouse_left(async (movement, cartesian) => {
|
||||
that.tip.set_text("左键创建视角终点,右键结束通视分析")
|
||||
if (!that.resultObject.viewPoint) {
|
||||
let pos84 = that.cartesian3Towgs84(cartesian, that.viewer)
|
||||
let positions
|
||||
if(that.sdk.viewer.terrainProvider.availability)
|
||||
{
|
||||
positions = await Cesium.sampleTerrainMostDetailed(
|
||||
that.sdk.viewer.terrainProvider,
|
||||
[Cesium.Cartographic.fromDegrees(pos84.lng, pos84.lat)]
|
||||
);
|
||||
}
|
||||
if (positions && positions[0].height > pos84.alt) {
|
||||
pos84.alt = positions[0].height
|
||||
}
|
||||
pos84.alt = pos84.alt + that.viewPointHeight
|
||||
let pos = Cesium.Cartesian3.fromDegrees(pos84.lng, pos84.lat, pos84.alt)
|
||||
that.resultObject.viewPoint = pos;
|
||||
let pointEntity = that.viewer.entities.add({
|
||||
position: pos,
|
||||
point: {
|
||||
color: Cesium.Color.YELLOW,
|
||||
pixelSize: 5,
|
||||
},
|
||||
});
|
||||
that.resultObject.objectExclude.push(pointEntity);
|
||||
that.resultObject.entities.push(pointEntity);
|
||||
} else {
|
||||
that.resultObject.targetPoint = cartesian;
|
||||
let pointEntity = that.viewer.entities.add({
|
||||
position: cartesian,
|
||||
point: {
|
||||
color: Cesium.Color.YELLOW,
|
||||
pixelSize: 5,
|
||||
},
|
||||
});
|
||||
that.resultObject.objectExclude.push(pointEntity);
|
||||
that.resultObject.entities.push(pointEntity);
|
||||
let direction = Cesium.Cartesian3.normalize(
|
||||
Cesium.Cartesian3.subtract(
|
||||
that.resultObject.targetPoint,
|
||||
that.resultObject.viewPoint,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
|
||||
let ray = new Cesium.Ray(that.resultObject.viewPoint, direction);
|
||||
let pickedObjects = that.viewer.scene.drillPickFromRay(ray);
|
||||
let result = {}
|
||||
for (let i = 0; i < pickedObjects.length; i++) {
|
||||
if (pickedObjects[i].position) {
|
||||
result = pickedObjects[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
// let result = that.viewer.scene.pickFromRay(
|
||||
// ray,
|
||||
// that.resultObject.objectExclude
|
||||
// ); // 计算交互点,返回第一个
|
||||
if (result) {
|
||||
let dis0 = VisibilityAnalysis.distance(
|
||||
that.resultObject.viewPoint,
|
||||
that.resultObject.targetPoint
|
||||
);
|
||||
let dis1 = VisibilityAnalysis.distance(
|
||||
that.resultObject.viewPoint,
|
||||
result.position || cartesian
|
||||
);
|
||||
let dis2 = VisibilityAnalysis.distance(
|
||||
result.position || cartesian,
|
||||
that.resultObject.targetPoint
|
||||
);
|
||||
if (dis0 > dis1) {
|
||||
let _poly0 = that.viewer.entities.add({
|
||||
polyline: {
|
||||
positions: [that.resultObject.viewPoint, result.position],
|
||||
material: Cesium.Color.GREEN,
|
||||
width: 3,
|
||||
zIndex: 99999999
|
||||
},
|
||||
});
|
||||
that.resultObject.entities.push(_poly0);
|
||||
let _poly1 = that.viewer.entities.add({
|
||||
polyline: {
|
||||
positions: [result.position, that.resultObject.targetPoint],
|
||||
material: Cesium.Color.RED,
|
||||
width: 3,
|
||||
zIndex: 99999999
|
||||
},
|
||||
});
|
||||
that.resultObject.entities.push(_poly1);
|
||||
that.resultObject.targetPoints.push({
|
||||
targetPoint: cartesian,
|
||||
visual: false, //如果dis2足够小,其实他是可视的
|
||||
distance: [dis0, dis1, dis2], //[初始点和终点,初始点和交点,交点和终点]
|
||||
});
|
||||
} else {
|
||||
let _poly2 = that.viewer.entities.add({
|
||||
polyline: {
|
||||
positions: [
|
||||
that.resultObject.viewPoint,
|
||||
that.resultObject.targetPoint,
|
||||
],
|
||||
material: Cesium.Color.GREEN,
|
||||
width: 3,
|
||||
zIndex: 99999999
|
||||
},
|
||||
});
|
||||
that.resultObject.entities.push(_poly2);
|
||||
that.resultObject.targetPoints.push({
|
||||
targetPoint: cartesian,
|
||||
visual: true, //如果dis2足够小,其实他是可视的
|
||||
distance: [dis0, dis1, dis2], //[初始点和终点,初始点和交点,交点和终点]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
that.event.mouse_move((movement, cartesian) => {
|
||||
that.tip.setPosition(cartesian, movement.endPosition.x, movement.endPosition.y)
|
||||
})
|
||||
that.event.mouse_right((movement, cartesian) => {
|
||||
end()
|
||||
})
|
||||
that.event.gesture_pinck_start((movement, cartesian) => {
|
||||
let startTime = new Date()
|
||||
that.event.gesture_pinck_end(() => {
|
||||
let endTime = new Date()
|
||||
if (endTime - startTime >= 500) {
|
||||
end()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
console.log('上一次测量未结束')
|
||||
}
|
||||
|
||||
function end() {
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
that.tip.destroy()
|
||||
that.event.destroy()
|
||||
that.tip = null
|
||||
that.event = null
|
||||
}
|
||||
}
|
||||
|
||||
// static update(that) {
|
||||
// if (!that.resultObject.viewPoint) {
|
||||
// return
|
||||
// }
|
||||
// for (let i = that.resultObject.entities.length - 1; i >= 0; i--) {
|
||||
// if (that.resultObject.entities[i].point) {
|
||||
// that.viewer.entities.remove(that.resultObject.entities[i]);
|
||||
// that.resultObject.entities.splice(i, 1)
|
||||
// }
|
||||
// }
|
||||
// setTimeout(() => {
|
||||
// for (let i = 0; i < that.resultObject.targetPoints.length; i++) {
|
||||
// that.resultObject.targetPoint = that.resultObject.targetPoints[i].targetPoint;
|
||||
// let direction = Cesium.Cartesian3.normalize(
|
||||
// Cesium.Cartesian3.subtract(
|
||||
// that.resultObject.targetPoint,
|
||||
// that.resultObject.viewPoint,
|
||||
// new Cesium.Cartesian3()
|
||||
// ),
|
||||
// new Cesium.Cartesian3()
|
||||
// );
|
||||
// let ray = new Cesium.Ray(that.resultObject.viewPoint, direction);
|
||||
// let pickedObjects = that.viewer.scene.drillPickFromRay(ray);
|
||||
// let result = {}
|
||||
// for (let i = 0; i < pickedObjects.length; i++) {
|
||||
// if (pickedObjects[i].position) {
|
||||
// result = pickedObjects[i]
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// // let result = that.viewer.scene.pickFromRay(
|
||||
// // ray,
|
||||
// // that.resultObject.objectExclude
|
||||
// // ); // 计算交互点,返回第一个
|
||||
// if (result) {
|
||||
// let dis0 = VisibilityAnalysis.distance(
|
||||
// that.resultObject.viewPoint,
|
||||
// that.resultObject.targetPoint
|
||||
// );
|
||||
// let dis1 = VisibilityAnalysis.distance(
|
||||
// that.resultObject.viewPoint,
|
||||
// result.position || cartesian
|
||||
// );
|
||||
// let dis2 = VisibilityAnalysis.distance(
|
||||
// result.position || cartesian,
|
||||
// that.resultObject.targetPoint
|
||||
// );
|
||||
// if (dis0 > dis1) {
|
||||
// let _poly0 = that.viewer.entities.add({
|
||||
// polyline: {
|
||||
// positions: [that.resultObject.viewPoint, result.position],
|
||||
// material: Cesium.Color.GREEN,
|
||||
// width: 3,
|
||||
// zIndex: 99999999
|
||||
// },
|
||||
// });
|
||||
// that.resultObject.entities.push(_poly0);
|
||||
// let _poly1 = that.viewer.entities.add({
|
||||
// polyline: {
|
||||
// positions: [result.position, that.resultObject.targetPoint],
|
||||
// material: Cesium.Color.RED,
|
||||
// width: 3,
|
||||
// zIndex: 99999999
|
||||
// },
|
||||
// });
|
||||
// that.resultObject.entities.push(_poly1);
|
||||
// } else {
|
||||
// let _poly2 = that.viewer.entities.add({
|
||||
// polyline: {
|
||||
// positions: [
|
||||
// that.resultObject.viewPoint,
|
||||
// that.resultObject.targetPoint,
|
||||
// ],
|
||||
// material: Cesium.Color.GREEN,
|
||||
// width: 3,
|
||||
// zIndex: 99999999
|
||||
// },
|
||||
// });
|
||||
// that.resultObject.entities.push(_poly2);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }, 1000);
|
||||
// }
|
||||
|
||||
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 + ' visibility'
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
that._DialogObject.contentAppChild(contentElm)
|
||||
|
||||
let drawElm = document.createElement('button')
|
||||
drawElm.innerHTML = '绘制'
|
||||
drawElm.addEventListener('click', () => {
|
||||
VisibilityAnalysis.create(that)
|
||||
})
|
||||
that._DialogObject.footAppChild(drawElm)
|
||||
|
||||
let all_elm = contentElm.getElementsByTagName("*")
|
||||
that._EventBinding.on(that, all_elm)
|
||||
that._elms = that._EventBinding.element
|
||||
}
|
||||
|
||||
//空间两点间距离
|
||||
static distance(point1, point2) {
|
||||
let point1cartographic = Cesium.Cartographic.fromCartesian(point1);
|
||||
let point2cartographic = Cesium.Cartographic.fromCartesian(point2);
|
||||
/**根据经纬度计算出距离**/
|
||||
let geodesic = new Cesium.EllipsoidGeodesic();
|
||||
geodesic.setEndPoints(point1cartographic, point2cartographic);
|
||||
let s = geodesic.surfaceDistance;
|
||||
//返回两点之间的距离
|
||||
s = Math.sqrt(
|
||||
Math.pow(s, 2) +
|
||||
Math.pow(point2cartographic.height - point1cartographic.height, 2)
|
||||
);
|
||||
return s;
|
||||
}
|
||||
destroy() {
|
||||
this.resultObject.entities.forEach((element) => {
|
||||
this.viewer.entities.remove(element);
|
||||
});
|
||||
this.resultObject = {
|
||||
viewPoint: undefined, //通视分析起点
|
||||
targetPoints: [], //通视分析目标点集合
|
||||
targetPoint: undefined, //当前目标点
|
||||
objectExclude: [], //射线排除集合
|
||||
entities: [], //创建的Entity对象
|
||||
};
|
||||
this.tip && this.tip.destroy()
|
||||
this.event && this.event.destroy()
|
||||
this.tip = null
|
||||
this.event = null
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
}
|
||||
}
|
||||
|
||||
export default VisibilityAnalysis;
|
||||
14
src/Obj/Analysis/clear.js
Normal file
14
src/Obj/Analysis/clear.js
Normal file
@ -0,0 +1,14 @@
|
||||
function AnalysisClear() {
|
||||
YJ.Analysis.AnalysesResults.forEach(m => {
|
||||
m.destroy()
|
||||
})
|
||||
// YJ.Analysis.AnalysesResults = []
|
||||
}
|
||||
|
||||
function SectionClear() {
|
||||
YJ.Analysis.SectionResults.forEach(m => {
|
||||
m.destroy()
|
||||
})
|
||||
}
|
||||
|
||||
export {AnalysisClear, SectionClear}
|
||||
74
src/Obj/Analysis/test/_element.js
Normal file
74
src/Obj/Analysis/test/_element.js
Normal file
@ -0,0 +1,74 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">位置拾取(起点、终点)</span>
|
||||
<button class="edit"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>拾取</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row subtitle-box">
|
||||
<span class="subtitle">视域夹角</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="range-box">
|
||||
<div class="range-bg">
|
||||
<div class="range-process-box">
|
||||
<div class="range-process"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-node-box">
|
||||
<span class="range-node-text">0°</span>
|
||||
<span class="range-node-text">45°</span>
|
||||
<span class="range-node-text">90°</span>
|
||||
<span class="range-node-text">135°</span>
|
||||
<span class="range-node-text">180°</span>
|
||||
<div class="range-node-active"><span class="range-node-active-text">0°</span></div>
|
||||
</div>
|
||||
<input type="range" max="180" min="0" step="1" name="horizontalViewAngle">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">经度:</span>
|
||||
<span class="text-number" name="lng"></span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">偏航角:</span>
|
||||
<span class="text-number" name="viewHeading"></span>
|
||||
<span class="unit">°</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">纬度:</span>
|
||||
<span class="text-number" name="lat"></span>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">俯仰角:</span>
|
||||
<span class="text-number" name="viewPitch"></span>
|
||||
<span class="unit">°</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">高度:</span>
|
||||
<span class="text-number" name="alt"></span>
|
||||
<span class="unit text-number" style="margin-left: 5px;">m</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
511
src/Obj/Analysis/test/_index.js
Normal file
511
src/Obj/Analysis/test/_index.js
Normal file
@ -0,0 +1,511 @@
|
||||
/*
|
||||
* @Author: Wang jianLei
|
||||
* @Date: 2022-05-17 21:49:28
|
||||
* @Last Modified by: Wang JianLei
|
||||
* @Last Modified time: 2022-05-19 22:08:14
|
||||
*/
|
||||
|
||||
let ViewShed = function (sdk, canvasEleId) {
|
||||
if (!sdk.viewer) throw new Error("no viewer object!");
|
||||
let canvasEle = document.getElementById(canvasEleId);
|
||||
if (!canvasEle) throw new Error("the canvas element is not exist");
|
||||
this.canvasEle = canvasEle;
|
||||
this.viewer = sdk.viewer;
|
||||
this.handler = undefined;
|
||||
this.lightCamera;
|
||||
this.pyramid;
|
||||
this.frustumPrimitive;
|
||||
this.viewershedPolygon;
|
||||
};
|
||||
ViewShed.prototype = {
|
||||
/**
|
||||
* 初始化handler
|
||||
*/
|
||||
initHandler() {
|
||||
if (this.handler) {
|
||||
this.handler.destroy();
|
||||
this.handler = undefined;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 开始执行视域分析
|
||||
* @param {number} precision 精度,值越大创建耗时越长,建议在10~20之间
|
||||
*/
|
||||
createViewshed: function (precision) {
|
||||
let $this = this;
|
||||
let scene = $this.viewer.scene;
|
||||
$this.initHandler();
|
||||
$this.clearAll();
|
||||
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
|
||||
$this.handler.setInputAction((event) => {
|
||||
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
|
||||
scene.screenSpaceCameraController.enableRotate = false;
|
||||
scene.screenSpaceCameraController.enableZoom = false;
|
||||
scene.globe.depthTestAgainstTerrain = true;
|
||||
let earthPosition = scene.pickPosition(event.position);
|
||||
let pos = $this.cartesian3ToDegree(earthPosition);
|
||||
$this.handler.setInputAction(function (event) {
|
||||
let newPosition = scene.pickPosition(event.endPosition);
|
||||
if (Cesium.defined(newPosition)) {
|
||||
let pos1 = $this.cartesian3ToDegree(newPosition);
|
||||
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
|
||||
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
|
||||
let pitch = $this.getPitch(earthPosition, newPosition);
|
||||
$this.ViewShedOptions = {
|
||||
viewPosition: earthPosition, //观测点 笛卡尔坐标
|
||||
endPosition: newPosition, //目标点 笛卡尔坐标
|
||||
direction: angle, //观测方位角 默认为`0`,范围`0~360`
|
||||
pitch: pitch, //俯仰角,radius,默认为`0`
|
||||
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
|
||||
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
|
||||
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
|
||||
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
|
||||
visualRange: distance, //距离,单位`米`
|
||||
};
|
||||
$this.updateViewShed();
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
|
||||
|
||||
$this.handler.setInputAction(() => {
|
||||
$this.initHandler();
|
||||
// 开启地球旋转和缩放
|
||||
scene.screenSpaceCameraController.enableRotate = true;
|
||||
scene.screenSpaceCameraController.enableZoom = true;
|
||||
$this.drawViewershed(precision);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_UP);
|
||||
},
|
||||
|
||||
ReturnDistance(pos0, pos1) {
|
||||
let distance = 0;
|
||||
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
|
||||
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
|
||||
/**根据经纬度计算出距离**/
|
||||
let geodesic = new Cesium.EllipsoidGeodesic();
|
||||
geodesic.setEndPoints(point1cartographic, point2cartographic);
|
||||
let s = geodesic.surfaceDistance;
|
||||
return s;
|
||||
},
|
||||
getHeight(x, y, objectsToExclude) {
|
||||
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
|
||||
let endHeight = this.viewer.scene.sampleHeight(
|
||||
endCartographic,
|
||||
objectsToExclude
|
||||
);
|
||||
return endHeight;
|
||||
},
|
||||
|
||||
cartesian3ToDegree: function (Cartesian3) {
|
||||
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
|
||||
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
|
||||
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
|
||||
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
|
||||
let _alt = _cartographic.height;
|
||||
return [_lng, _lat, _alt];
|
||||
},
|
||||
getAngle: function (lng1, lat1, lng2, lat2) {
|
||||
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
|
||||
if (lng2 >= lng1) {
|
||||
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
|
||||
} else {
|
||||
dRotateAngle =
|
||||
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
|
||||
}
|
||||
dRotateAngle = (dRotateAngle * 180) / Math.PI;
|
||||
return dRotateAngle;
|
||||
},
|
||||
|
||||
getPitch(pointA, pointB) {
|
||||
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
|
||||
const vector = Cesium.Cartesian3.subtract(
|
||||
pointB,
|
||||
pointA,
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
let direction = Cesium.Matrix4.multiplyByPointAsVector(
|
||||
Cesium.Matrix4.inverse(transfrom, transfrom),
|
||||
vector,
|
||||
vector
|
||||
);
|
||||
Cesium.Cartesian3.normalize(direction, direction);
|
||||
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
|
||||
},
|
||||
|
||||
updateViewShed: function () {
|
||||
this.clear();
|
||||
this.setLightCamera();
|
||||
this.addVisualPyramid();
|
||||
this.createFrustum();
|
||||
},
|
||||
clear: function () {
|
||||
if (this.pyramid) {
|
||||
this.viewer.entities.removeById(this.pyramid.id);
|
||||
this.pyramid = undefined;
|
||||
}
|
||||
if (this.frustumPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.frustumPrimitive);
|
||||
this.frustumPrimitive = undefined;
|
||||
}
|
||||
if (this.debugModelMatrixPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
|
||||
this.debugModelMatrixPrimitive = undefined;
|
||||
}
|
||||
},
|
||||
clearAll: function () {
|
||||
this.clear();
|
||||
if (this.viewershedPolygon) {
|
||||
this.viewer.scene.primitives.remove(this.viewershedPolygon);
|
||||
this.viewershedPolygon = undefined;
|
||||
}
|
||||
},
|
||||
addVisualPyramid: function () {
|
||||
let options = this.ViewShedOptions;
|
||||
let position = options.viewPosition;
|
||||
let visualRange = Number(options.visualRange);
|
||||
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
|
||||
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.DebugModelMatrixPrimitive({
|
||||
modelMatrix: transform,
|
||||
length: 5.0,
|
||||
})
|
||||
);
|
||||
const halfClock = options.horizontalViewAngle / 2;
|
||||
const halfCone = options.verticalViewAngle / 2;
|
||||
const pitch = Cesium.Math.toDegrees(options.pitch);
|
||||
const ellipsoid = new Cesium.EllipsoidGraphics({
|
||||
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
|
||||
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
|
||||
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
|
||||
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
|
||||
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
|
||||
fill: false,
|
||||
outline: true,
|
||||
subdivisions: 256,
|
||||
stackPartitions: 64,
|
||||
slicePartitions: 64,
|
||||
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
|
||||
});
|
||||
const pyramidEntity = new Cesium.Entity({
|
||||
position: position,
|
||||
ellipsoid,
|
||||
});
|
||||
this.pyramid = this.viewer.entities.add(pyramidEntity);
|
||||
},
|
||||
setLightCamera: function () {
|
||||
if (!this.lightCamera) {
|
||||
this.lightCamera = new Cesium.Camera(this.viewer.scene);
|
||||
}
|
||||
let options = this.ViewShedOptions;
|
||||
let visualRange = Number(options.visualRange);
|
||||
this.lightCamera.position = options.viewPosition;
|
||||
this.lightCamera.frustum.near = 0.1;
|
||||
this.lightCamera.frustum.far = visualRange;
|
||||
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
|
||||
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
|
||||
this.lightCamera.frustum.aspectRatio =
|
||||
(visualRange * Math.tan(hr / 2) * 2) /
|
||||
(visualRange * Math.tan(vr / 2) * 2);
|
||||
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
|
||||
this.lightCamera.setView({
|
||||
destination: options.viewPosition,
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(options.direction || 0),
|
||||
pitch: options.pitch || 0,
|
||||
roll: 0,
|
||||
},
|
||||
});
|
||||
},
|
||||
createFrustum: function () {
|
||||
const scratchRight = new Cesium.Cartesian3();
|
||||
const scratchRotation = new Cesium.Matrix3();
|
||||
const scratchOrientation = new Cesium.Quaternion();
|
||||
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 instanceOutline = new Cesium.GeometryInstance({
|
||||
geometry: new Cesium.FrustumOutlineGeometry({
|
||||
frustum: this.lightCamera.frustum,
|
||||
origin: this.ViewShedOptions.viewPosition,
|
||||
orientation: orientation,
|
||||
}),
|
||||
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true),
|
||||
},
|
||||
});
|
||||
this.frustumPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.Primitive({
|
||||
geometryInstances: instanceOutline,
|
||||
appearance: new Cesium.PerInstanceColorAppearance({
|
||||
flat: true,
|
||||
translucent: false,
|
||||
closed: true,
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
createPoint: function (firstPos, secondPos) {
|
||||
let entity4FirstPos = new Cesium.Entity({
|
||||
name: "firstPos",
|
||||
show: true,
|
||||
position: firstPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 20,
|
||||
color: Cesium.Color.RED,
|
||||
outlineColor: Cesium.Color.YELLOW,
|
||||
outlineWidth: 5,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体起点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4FirstPos);
|
||||
let entity4SecondPos = new Cesium.Entity({
|
||||
name: "secondPos",
|
||||
show: true,
|
||||
position: secondPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 30,
|
||||
color: Cesium.Color.YELLOW,
|
||||
outlineColor: Cesium.Color.RED,
|
||||
outlineWidth: 8,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体视角终点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4SecondPos);
|
||||
},
|
||||
|
||||
//绘制可视域
|
||||
add(positionArr) {
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
Cesium.Cartesian3.fromDegreesArray(positionArr)
|
||||
),
|
||||
height: 0.0,
|
||||
extrudedHeight: 0.0,
|
||||
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
|
||||
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
|
||||
ellipsoid: Cesium.Ellipsoid.WGS84,
|
||||
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
|
||||
perPositionHeight: false, // 每个位置点使用的高度
|
||||
closeTop: true,
|
||||
closeBottom: true,
|
||||
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
|
||||
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
|
||||
});
|
||||
|
||||
let polygonInstance = new Cesium.GeometryInstance({
|
||||
geometry: polygon,
|
||||
name: "ViewershedPolygon",
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.BLUE.withAlpha(0.6)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
},
|
||||
});
|
||||
this.viewershedPolygon = this.viewer.scene.primitives.add(
|
||||
new Cesium.GroundPrimitive({
|
||||
geometryInstances: polygonInstance,
|
||||
appearance: new Cesium.EllipsoidSurfaceAppearance({
|
||||
aboveGround: true,
|
||||
material: new Cesium.Material({
|
||||
fabric: {
|
||||
type: "Image",
|
||||
uniforms: {
|
||||
image: this.returnImgae(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
drawViewershed(precision) {
|
||||
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
|
||||
const radius = this.ViewShedOptions.visualRange;
|
||||
const direction = this.ViewShedOptions.direction;
|
||||
let boundary = this.computeBoundaryOptions(pos, radius, direction);
|
||||
const bbox = boundary.bbox;
|
||||
let mask = turf.polygon([boundary.boundaryPoints]);
|
||||
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
|
||||
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
|
||||
|
||||
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
|
||||
let variogram = kriging.train(
|
||||
pointsResult.values,
|
||||
pointsResult.lngs,
|
||||
pointsResult.lats,
|
||||
"exponential",
|
||||
0,
|
||||
100
|
||||
);
|
||||
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
|
||||
const colors = [
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
];
|
||||
|
||||
this.canvasEle.width = 3840;
|
||||
this.canvasEle.height = 2160;
|
||||
kriging.plot(
|
||||
this.canvasEle,
|
||||
grid,
|
||||
[bbox[0], bbox[2]],
|
||||
[bbox[1], bbox[3]],
|
||||
colors
|
||||
);
|
||||
this.add(boundary.positionArr);
|
||||
},
|
||||
computeBoundaryOptions(pos, radius, angle) {
|
||||
let Ea = 6378137; // 赤道半径
|
||||
let Eb = 6356725; // 极半径
|
||||
const lng = pos[0],
|
||||
lat = pos[1];
|
||||
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
|
||||
let positionArr = [];
|
||||
let boundaryPoints = [];
|
||||
positionArr.push(lng, lat);
|
||||
boundaryPoints.push([lng, lat]);
|
||||
//正北是0°
|
||||
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
|
||||
let end = start + 90;
|
||||
for (let i = start; i <= end; i++) {
|
||||
let dx = radius * Math.sin((i * Math.PI) / 180.0);
|
||||
let dy = radius * Math.cos((i * Math.PI) / 180.0);
|
||||
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
|
||||
let ed = ec * Math.cos((lat * Math.PI) / 180);
|
||||
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
|
||||
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
|
||||
positionArr.push(BJD, BWD);
|
||||
boundaryPoints.push([BJD, BWD]);
|
||||
this.refreshBBox(bbox, BJD, BWD);
|
||||
}
|
||||
boundaryPoints.push([lng, lat]);
|
||||
return {
|
||||
positionArr,
|
||||
boundaryPoints,
|
||||
bbox,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 更新外围矩形 Bbox
|
||||
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
|
||||
* @param {Number} x 经度
|
||||
* @param {Number} y 纬度
|
||||
*/
|
||||
refreshBBox(result, x, y) {
|
||||
result[0] = x < result[0] ? x : result[0];
|
||||
result[1] = y < result[1] ? y : result[1];
|
||||
result[2] = x > result[2] ? x : result[2];
|
||||
result[3] = y > result[3] ? y : result[3];
|
||||
},
|
||||
/**
|
||||
* 插值点用射线判断通视性
|
||||
* @param {*} gridPoints 网格点
|
||||
* @param {*} step 步长,可以理解成是精度
|
||||
* @param {*} sourcePos 视域分析起点
|
||||
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
|
||||
*/
|
||||
createTargetPoints(gridPoints, step, sourcePos) {
|
||||
let positionArr = [];
|
||||
let objectsToExclude = [
|
||||
this.frustumPrimitive,
|
||||
this.pyramid,
|
||||
this.debugModelMatrixPrimitive,
|
||||
];
|
||||
let values = [],
|
||||
lngs = [],
|
||||
lats = [];
|
||||
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
|
||||
positionArr.push({
|
||||
x: sourcePos[0],
|
||||
y: sourcePos[1],
|
||||
z: height,
|
||||
});
|
||||
let viewPoint = this.ViewShedOptions.viewPosition;
|
||||
for (let index = 0; index < gridPoints.features.length; index++) {
|
||||
const feature = gridPoints.features[index];
|
||||
const coords = feature.geometry.coordinates;
|
||||
const x = coords[0],
|
||||
y = coords[1];
|
||||
let h = this.getHeight(x, y, objectsToExclude);
|
||||
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
|
||||
let direction = Cesium.Cartesian3.normalize(
|
||||
Cesium.Cartesian3.subtract(
|
||||
endPoint,
|
||||
viewPoint,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
// 建立射线
|
||||
let ray = new Cesium.Ray(viewPoint, direction);
|
||||
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
|
||||
if (result) {
|
||||
let buffer = this.ReturnDistance(endPoint, result.position);
|
||||
// let M_color = Cesium.Color.GREEN;
|
||||
if (buffer > step) {
|
||||
// M_color = Cesium.Color.RED;
|
||||
values.push(0);
|
||||
} else {
|
||||
values.push(1);
|
||||
}
|
||||
lngs.push(x);
|
||||
lats.push(y);
|
||||
// this.viewer.entities.add(
|
||||
// new Cesium.Entity({
|
||||
// name: "插值点哦",
|
||||
// show: true,
|
||||
// position: endPoint,
|
||||
// point: {
|
||||
// show: true,
|
||||
// pixelSize: 10,
|
||||
// color: M_color,
|
||||
// outlineWidth: 2,
|
||||
// outlineColor: Cesium.Color.YELLOW,
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
}
|
||||
}
|
||||
return {
|
||||
values,
|
||||
lngs,
|
||||
lats,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* canvas转image图片
|
||||
* @returns base64图片
|
||||
*/
|
||||
returnImgae() {
|
||||
return this.canvasEle.toDataURL("image/png");
|
||||
},
|
||||
};
|
||||
|
||||
export default ViewShed;
|
||||
72
src/Obj/Analysis/test/glsl.js
Normal file
72
src/Obj/Analysis/test/glsl.js
Normal file
@ -0,0 +1,72 @@
|
||||
export default `
|
||||
#define USE_CUBE_MAP_SHADOW true
|
||||
uniform sampler2D colorTexture;
|
||||
uniform sampler2D depthTexture;
|
||||
varying vec2 v_textureCoordinates;
|
||||
uniform mat4 camera_projection_matrix;
|
||||
uniform mat4 camera_view_matrix;
|
||||
uniform vec4 helsing_visibleAreaColor;
|
||||
uniform vec4 helsing_invisibleAreaColor;
|
||||
uniform sampler2D helsing_texture;
|
||||
|
||||
vec4 getPositionEC(){
|
||||
return czm_windowToEyeCoordinates(gl_FragCoord);
|
||||
}
|
||||
|
||||
vec3 getNormalEC(){
|
||||
return vec3(1.);
|
||||
}
|
||||
|
||||
vec4 toEye(in vec2 uv,in float depth){
|
||||
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
|
||||
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
|
||||
posInCamera=posInCamera/posInCamera.w;
|
||||
return posInCamera;
|
||||
}
|
||||
|
||||
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
|
||||
vec3 v01=point-planeOrigin;
|
||||
float d=dot(planeNormal,v01);
|
||||
return(point-planeNormal*d);
|
||||
}
|
||||
|
||||
float getDepth(in vec4 depth){
|
||||
float z_window=czm_unpackDepth(depth);
|
||||
z_window=czm_reverseLogDepth(z_window);
|
||||
float n_range=czm_depthRange.near;
|
||||
float f_range=czm_depthRange.far;
|
||||
return(2.*z_window-n_range-f_range)/(f_range-n_range);
|
||||
}
|
||||
|
||||
bool visible(in vec4 result)
|
||||
{
|
||||
result.x/=result.w;
|
||||
result.y/=result.w;
|
||||
result.z/=result.w;
|
||||
return result.x>=-1.&&result.x<=1.
|
||||
&&result.y>=-1.&&result.y<=1.
|
||||
&&result.z>=-1.&&result.z<=1.;
|
||||
}
|
||||
|
||||
void main(){
|
||||
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
|
||||
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
|
||||
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
|
||||
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
|
||||
// 视角 = (纹理坐标, 深度)
|
||||
vec4 positionEC = toEye(v_textureCoordinates, depth);
|
||||
// 世界坐标
|
||||
vec4 wordPos = czm_inverseView * positionEC;
|
||||
// 虚拟相机中坐标
|
||||
vec4 vcPos = camera_view_matrix * wordPos;
|
||||
vec4 videoColor = texture2D(helsing_texture, v_textureCoordinates);
|
||||
float dis = length(vcPos.xyz);
|
||||
vec4 posInEye = camera_projection_matrix * vcPos;
|
||||
// 可视区颜色
|
||||
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
|
||||
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
|
||||
if(visible(posInEye)){
|
||||
vec4 out_FragColor = helsing_visibleAreaColor;
|
||||
gl_FragColor = mix(gl_FragColor,videoColor,1.0);
|
||||
}
|
||||
}`;
|
||||
787
src/Obj/Analysis/test/index.js
Normal file
787
src/Obj/Analysis/test/index.js
Normal file
@ -0,0 +1,787 @@
|
||||
// ViewShed.js
|
||||
import glsl from './glsl'
|
||||
import Event from "../../../Event";
|
||||
import MouseTip from "../../../MouseTip";
|
||||
import Tools from "../../../Tools";
|
||||
import Controller from "../../../Controller";
|
||||
import Dialog from '../../../BaseDialog';
|
||||
import { html } from "./_element";
|
||||
/**
|
||||
* @constructor
|
||||
* @description 可视域分析
|
||||
* @param sdk
|
||||
* @param {Object} options 选项。
|
||||
* @param {Cesium.Cartesian3} options.viewPosition 观测点位置。
|
||||
* @param {Cesium.Cartesian3} options.viewPositionEnd 最远观测点位置(如果设置了观测距离,这个属性可以不设置)。
|
||||
* @param {Number} options.viewDistance 观测距离(单位`米`)。
|
||||
* @param {Number} options.viewHeading 航向角(单位`度`)。
|
||||
* @param {Number} options.viewPitch 俯仰角(单位`度`)。
|
||||
* @param {Number} options.horizontalViewAngle=90 可视域水平夹角(单位`度`)。
|
||||
* @param {Number} options.verticalViewAngle=60 可视域垂直夹角(单位`度`)。
|
||||
* @param {String} options.visibleAreaColor=#008000 可视区域颜色(默认值`绿色`)。
|
||||
* @param {String} options.invisibleAreaColor=#FF0000 不可视区域颜色(默认值`红色`)。
|
||||
*/
|
||||
class Test extends Tools {
|
||||
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
super(sdk, options)
|
||||
|
||||
// if (Object.hasOwn(options.viewPosition, 'lng') && Object.hasOwn(options.viewPosition, 'lat') && Object.hasOwn(options.viewPosition, 'alt')) {
|
||||
// this.error = '请提供观测点位置!'
|
||||
// window.ELEMENT && window.ELEMENT.Message({
|
||||
// message: '请提供观测点位置!',
|
||||
// type: 'warning',
|
||||
// duration: 1500
|
||||
// });
|
||||
// return
|
||||
// }
|
||||
|
||||
this.viewer = sdk.viewer;
|
||||
this.options = {}
|
||||
this.options.viewPosition = options.viewPosition;
|
||||
this.options.viewPositionEnd = options.viewPositionEnd;
|
||||
this.options.horizontalViewAngle = (options.horizontalViewAngle || options.horizontalViewAngle === 0) ? options.horizontalViewAngle : 90.0;
|
||||
this.options.verticalViewAngle = (options.verticalViewAngle || options.verticalViewAngle === 0) ? options.verticalViewAngle : 60.0;
|
||||
this.options.visibleAreaColor = options.visibleAreaColor || '#008000';
|
||||
this.options.invisibleAreaColor = options.invisibleAreaColor || '#FF0000';
|
||||
// this.enabled = (typeof options.enabled === "boolean") ? options.enabled : true;
|
||||
// this.softShadows = (typeof options.softShadows === "boolean") ? options.softShadows : true;
|
||||
// this.size = options.size || 10240; // 2048
|
||||
this.ids = []
|
||||
this.Dialog = _Dialog
|
||||
this.html = null
|
||||
YJ.Analysis.AnalysesResults.push(this)
|
||||
Test.create(this)
|
||||
// Test.edit(this)
|
||||
// this.update();
|
||||
}
|
||||
|
||||
get viewPosition() {
|
||||
return this.options.viewPosition
|
||||
}
|
||||
|
||||
set viewPosition(v) {
|
||||
this.options.viewPosition = v
|
||||
this.ids[0] && (this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
|
||||
this.update()
|
||||
// let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
|
||||
}
|
||||
|
||||
get viewPositionEnd() {
|
||||
return this.options.viewPositionEnd
|
||||
}
|
||||
|
||||
set viewPositionEnd(v) {
|
||||
this.options.viewPositionEnd = v
|
||||
this.ids[1] && (this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(v.lng, v.lat, v.alt))
|
||||
this.update()
|
||||
// let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
// this.viewDistance = this.viewPositionEnd ? Cesium.Cartesian3.distance(this.viewPosition, this.viewPositionEnd) : (options.viewDistance || 100.0);
|
||||
}
|
||||
|
||||
get horizontalViewAngle() {
|
||||
return this.options.horizontalViewAngle
|
||||
}
|
||||
|
||||
set horizontalViewAngle(v) {
|
||||
this.options.horizontalViewAngle = v
|
||||
if (this._DialogObject && this._DialogObject._element && this._DialogObject._element.content) {
|
||||
let contentElm = this._DialogObject._element.content
|
||||
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
|
||||
e_horizontalViewAngle.value = v
|
||||
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
|
||||
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
|
||||
rangeNodeActiveText.innerHTML = v + '°';
|
||||
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
|
||||
rangeProcess.style.width = v / 180 * 100 + '%'
|
||||
}
|
||||
this.update()
|
||||
}
|
||||
|
||||
get visibleAreaColor() {
|
||||
return this.options.visibleAreaColor
|
||||
}
|
||||
|
||||
set visibleAreaColor(v) {
|
||||
this.options.visibleAreaColor = v
|
||||
this.update()
|
||||
}
|
||||
|
||||
get invisibleAreaColor() {
|
||||
return this.options.invisibleAreaColor
|
||||
}
|
||||
|
||||
set invisibleAreaColor(v) {
|
||||
this.options.invisibleAreaColor = v
|
||||
this.update()
|
||||
}
|
||||
|
||||
get verticalViewAngle() {
|
||||
return this.options.verticalViewAngle
|
||||
}
|
||||
|
||||
set verticalViewAngle(v) {
|
||||
this.options.verticalViewAngle = v
|
||||
this.update()
|
||||
}
|
||||
|
||||
get viewDistance() {
|
||||
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
|
||||
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
let distance = Cesium.Cartesian3.distance(viewPosition3, viewPositionEnd3)
|
||||
return distance
|
||||
}
|
||||
|
||||
get viewHeading() {
|
||||
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
|
||||
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
let heading = getHeading(viewPosition3, viewPositionEnd3)
|
||||
if (this.html) {
|
||||
let e_viewHeading = this.html.querySelector("span[name='viewHeading']")
|
||||
e_viewHeading.innerHTML = Number(heading.toFixed(8))
|
||||
}
|
||||
return heading
|
||||
}
|
||||
|
||||
get viewPitch() {
|
||||
let viewPosition3 = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt)
|
||||
let viewPositionEnd3 = Cesium.Cartesian3.fromDegrees(this.options.viewPositionEnd.lng, this.options.viewPositionEnd.lat, this.options.viewPositionEnd.alt)
|
||||
let pitch = getPitch(viewPosition3, viewPositionEnd3)
|
||||
if (this.html) {
|
||||
let e_viewPitch = this.html.querySelector("span[name='viewPitch']")
|
||||
e_viewPitch.innerHTML = Number(pitch.toFixed(8))
|
||||
}
|
||||
return pitch
|
||||
}
|
||||
|
||||
static create(that) {
|
||||
let count = 0;
|
||||
if (!YJ.Measure.GetMeasureStatus()) {
|
||||
that.event = new Event(that.sdk)
|
||||
that.tip = new MouseTip('左键选择观测点位置,右键取消', that.sdk)
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
that.event.mouse_left((movement, cartesian) => {
|
||||
if (!that.viewPosition) {
|
||||
that.options.viewPosition = that.cartesian3Towgs84(cartesian, that.viewer)
|
||||
that.ids.push(Test.create_point(that, cartesian))
|
||||
that.tip.set_text("左键选择最远观测点位置,右键取消")
|
||||
}
|
||||
count++
|
||||
if (count === 2) {
|
||||
that.options.viewPositionEnd = that.cartesian3Towgs84(cartesian, that.viewer)
|
||||
that.ids.push(Test.create_point(that, cartesian))
|
||||
end()
|
||||
that.update()
|
||||
}
|
||||
})
|
||||
that.event.mouse_move((movement, cartesian) => {
|
||||
that.tip.setPosition(cartesian, movement.endPosition.x, movement.endPosition.y)
|
||||
})
|
||||
that.event.mouse_right((movement, cartesian) => {
|
||||
that.ids.forEach(id => {
|
||||
that.viewer.entities.removeById(id)
|
||||
})
|
||||
that.ids = []
|
||||
end()
|
||||
})
|
||||
that.event.gesture_pinck_start((movement, cartesian) => {
|
||||
let startTime = new Date()
|
||||
that.event.gesture_pinck_end(() => {
|
||||
let endTime = new Date()
|
||||
if (endTime - startTime >= 500) {
|
||||
that.ids.forEach(id => {
|
||||
that.viewer.entities.removeById(id)
|
||||
})
|
||||
that.ids = []
|
||||
end()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
console.log('上一次测量未结束')
|
||||
}
|
||||
|
||||
function end() {
|
||||
that.ids.forEach(id => {
|
||||
let entity = that.viewer.entities.getById(id)
|
||||
entity.show = false
|
||||
})
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
that.tip.destroy()
|
||||
that.event.destroy()
|
||||
that.tip = null
|
||||
that.event = null
|
||||
if (count === 2) {
|
||||
Test.edit(that)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static create_point(that, cartesian) {
|
||||
let id = that.randomString()
|
||||
let p = that.cartesian3Towgs84(cartesian, that.viewer)
|
||||
let params = {
|
||||
id: id,
|
||||
position: Cesium.Cartesian3.fromDegrees(p.lng, p.lat, p.alt),
|
||||
billboard: {
|
||||
image: that.getSourceRootPath() + '/img/point.png',
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
color: Cesium.Color.WHITE.withAlpha(0.99)
|
||||
}
|
||||
}
|
||||
that.viewer.entities.add(
|
||||
new Cesium.Entity(params)
|
||||
)
|
||||
return id
|
||||
}
|
||||
|
||||
|
||||
add() {
|
||||
this.createLightCamera();
|
||||
this.createShadowMap();
|
||||
this.createPostStage();
|
||||
this.drawFrustumOutline();
|
||||
// this.drawSketch();
|
||||
}
|
||||
|
||||
update() {
|
||||
this.clear();
|
||||
this.add();
|
||||
}
|
||||
|
||||
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)
|
||||
that.editevent && that.editevent.destroy()
|
||||
that.ControllerObject && that.ControllerObject.destroy()
|
||||
that.ids.forEach(id => {
|
||||
that.viewer.entities.removeById(id)
|
||||
})
|
||||
},
|
||||
})
|
||||
await that._DialogObject.init()
|
||||
that._DialogObject._element.body.className = that._DialogObject._element.body.className + ' view-shed'
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
that._DialogObject.contentAppChild(contentElm)
|
||||
let resetBtn = that._DialogObject._element.body.getElementsByClassName('edit')[0];
|
||||
resetBtn.addEventListener('click', () => {
|
||||
that.nodeEdit()
|
||||
})
|
||||
that.html = contentElm
|
||||
//经度值
|
||||
let e_lng = contentElm.querySelector("span[name='lng']")
|
||||
e_lng.innerHTML = Number(that.options.viewPosition.lng.toFixed(8))
|
||||
|
||||
//纬度值
|
||||
let e_lat = contentElm.querySelector("span[name='lat']")
|
||||
e_lat.innerHTML = Number(that.options.viewPosition.lat.toFixed(8))
|
||||
|
||||
//高度值
|
||||
let e_alt = contentElm.querySelector("span[name='alt']")
|
||||
e_alt.innerHTML = Number(that.options.viewPosition.alt.toFixed(8))
|
||||
|
||||
//偏航角
|
||||
let e_viewHeading = contentElm.querySelector("span[name='viewHeading']")
|
||||
e_viewHeading.innerHTML = Number(that.viewHeading.toFixed(8))
|
||||
|
||||
//俯仰角
|
||||
let e_viewPitch = contentElm.querySelector("span[name='viewPitch']")
|
||||
e_viewPitch.innerHTML = Number(that.viewPitch.toFixed(8))
|
||||
|
||||
//视域夹角
|
||||
let e_horizontalViewAngle = contentElm.querySelector("input[name='horizontalViewAngle']")
|
||||
e_horizontalViewAngle.value = that.options.horizontalViewAngle
|
||||
let rangeNodeActive = contentElm.getElementsByClassName('range-node-active')[0]
|
||||
let rangeNodeActiveText = rangeNodeActive.getElementsByClassName('range-node-active-text')[0]
|
||||
let rangeProcess = contentElm.getElementsByClassName('range-process')[0]
|
||||
let percentage = that.horizontalViewAngle / 180 * 100
|
||||
rangeNodeActive.style.left = percentage + '%';
|
||||
rangeProcess.style.width = percentage + '%'
|
||||
rangeNodeActiveText.innerHTML = that.horizontalViewAngle + '°';
|
||||
let timeout
|
||||
e_horizontalViewAngle.addEventListener('input', () => {
|
||||
let percentage = e_horizontalViewAngle.value / 180 * 100
|
||||
rangeNodeActive.style.left = percentage + '%';
|
||||
rangeProcess.style.width = percentage + '%';
|
||||
rangeNodeActiveText.innerHTML = e_horizontalViewAngle.value + '°';
|
||||
})
|
||||
e_horizontalViewAngle.addEventListener('change', () => {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => {
|
||||
that.horizontalViewAngle = e_horizontalViewAngle.value;
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
|
||||
clear() {
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
this.tip && this.tip.destroy()
|
||||
this.event && this.event.destroy()
|
||||
this.tip = null
|
||||
this.event = null
|
||||
if (this.sketch) {
|
||||
this.viewer.entities.removeById(this.sketch.id);
|
||||
this.sketch = null;
|
||||
}
|
||||
if (this.frustumOutline) {
|
||||
this.frustumOutline.destroy();
|
||||
this.frustumOutline = null;
|
||||
}
|
||||
if (this.FrustumBottomSurface) {
|
||||
this.FrustumBottomSurface.destroy();
|
||||
this.FrustumBottomSurface = null;
|
||||
}
|
||||
if (this.postStage) {
|
||||
this.viewer.scene.postProcessStages.remove(this.postStage);
|
||||
this.postStage = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.clear()
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
this.ids.forEach(id => {
|
||||
this.viewer.entities.removeById(id)
|
||||
})
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
}
|
||||
|
||||
nodeEdit() {
|
||||
if (YJ.Measure.GetMeasureStatus()) {
|
||||
console.log('上一次测量未结束')
|
||||
}
|
||||
else {
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ids.forEach(id => {
|
||||
let entity = this.viewer.entities.getById(id)
|
||||
entity.show = true
|
||||
})
|
||||
let selectPoint
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
// this.tip = new MouseTip('左键选择要操作的观测点,右键取消', this.sdk)
|
||||
this.editevent = new Event(this.sdk)
|
||||
this.editevent.mouse_left((movement, cartesian) => {
|
||||
let pick = this.viewer.scene.pick(movement.position);
|
||||
if (pick && pick.id && pick.id.id && this.ids.indexOf(pick.id.id) != -1 && (!selectPoint || selectPoint.id != pick.id.id)) {
|
||||
selectPoint = pick.id
|
||||
// this.event.destroy()
|
||||
// this.tip.destroy()
|
||||
this.viewer.entities.getById(this.ids[0]).position = new Cesium.Cartesian3.fromDegrees(this.viewPosition.lng, this.viewPosition.lat, this.viewPosition.alt)
|
||||
this.viewer.entities.getById(this.ids[1]).position = new Cesium.Cartesian3.fromDegrees(this.viewPositionEnd.lng, this.viewPositionEnd.lat, this.viewPositionEnd.alt)
|
||||
this.viewPosition
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
console.log(this.cartesian3Towgs84(selectPoint.position._value, this.sdk.viewer))
|
||||
this.ControllerObject = new Controller(this.sdk, { position: { ...this.cartesian3Towgs84(selectPoint.position._value, this.sdk.viewer) } })
|
||||
this.ControllerObject.controllerCallBack = (params, status) => {
|
||||
if (params.position.alt < 0) {
|
||||
params.position.alt = 0
|
||||
}
|
||||
selectPoint.position = new Cesium.Cartesian3.fromDegrees(params.position.lng, params.position.lat, params.position.alt)
|
||||
if (status) {
|
||||
if (this.ids.indexOf(pick.id.id) == 0) {
|
||||
this.viewPosition = params.position
|
||||
}
|
||||
else {
|
||||
this.viewPositionEnd = params.position
|
||||
}
|
||||
YJ.Measure.SetMeasureStatus(true)
|
||||
}
|
||||
}
|
||||
this.ControllerObject.editTranslational()
|
||||
}
|
||||
})
|
||||
this.editevent.mouse_right((movement, cartesian) => {
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
this.ids.forEach(id => {
|
||||
let entity = this.viewer.entities.getById(id)
|
||||
entity.show = false
|
||||
})
|
||||
selectPoint = null
|
||||
})
|
||||
this.editevent.mouse_move((movement, cartesian) => {
|
||||
// this.tip.setPosition(
|
||||
// cartesian,
|
||||
// movement.endPosition.x,
|
||||
// movement.endPosition.y
|
||||
// )
|
||||
})
|
||||
|
||||
this.editevent.gesture_pinck_start((movement, cartesian) => {
|
||||
let startTime = new Date()
|
||||
this.editevent.gesture_pinck_end(() => {
|
||||
let endTime = new Date()
|
||||
if (endTime - startTime >= 500) {
|
||||
YJ.Measure.SetMeasureStatus(false)
|
||||
this.editevent && this.editevent.destroy()
|
||||
this.ControllerObject && this.ControllerObject.destroy()
|
||||
this.ids.forEach(id => {
|
||||
let entity = this.viewer.entities.getById(id)
|
||||
entity.show = false
|
||||
})
|
||||
selectPoint = null
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createLightCamera() {
|
||||
let _this = this
|
||||
this.lightCamera = new Cesium.Camera(this.viewer.scene);
|
||||
this.lightCamera.position = Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt);
|
||||
// if (this.viewPositionEnd) {
|
||||
// let direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(this.viewPositionEnd, this.viewPosition, new Cesium.Cartesian3()), new Cesium.Cartesian3());
|
||||
// this.lightCamera.direction = direction; // direction是相机面向的方向
|
||||
// }
|
||||
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: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(this.viewHeading || 0),
|
||||
pitch: Cesium.Math.toRadians(this.viewPitch || 0),
|
||||
roll: 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createShadowMap() {
|
||||
this.shadowMap = new Cesium.ShadowMap({
|
||||
context: (this.viewer.scene).context,
|
||||
lightCamera: this.lightCamera,
|
||||
enabled: true,
|
||||
isPointLight: true,
|
||||
pointLightRadius: this.viewDistance,
|
||||
cascadesEnabled: false,
|
||||
size: 2048, // 2048
|
||||
softShadows: true,
|
||||
normalOffset: false,
|
||||
fromLightSource: false
|
||||
});
|
||||
this.viewer.scene.shadowMap = this.shadowMap;
|
||||
}
|
||||
|
||||
createPostStage() {
|
||||
const video = document.getElementsByTagName('video')[0];
|
||||
const img = document.getElementById('img')[0];
|
||||
const that = this;
|
||||
if (video /*&& !video.paused*/) {
|
||||
this.activeVideoListener || (this.activeVideoListener = function () {
|
||||
that.texture && that.texture.destroy();
|
||||
that.texture = new Cesium.Texture({
|
||||
context: that.viewer.scene.context,
|
||||
source: video
|
||||
});
|
||||
});
|
||||
that.viewer.clock.onTick.addEventListener(this.activeVideoListener);
|
||||
}
|
||||
const fs = glsl
|
||||
const postStage = new Cesium.PostProcessStage({
|
||||
vertexShaderText: `
|
||||
attribute vec3 position3DHigh;
|
||||
attribute vec3 position3DLow;
|
||||
attribute vec3 normal;
|
||||
attribute vec2 st;
|
||||
attribute float batchId;
|
||||
|
||||
uniform mat3 rotation;
|
||||
uniform vec3 scale;
|
||||
uniform vec3 translation;
|
||||
uniform mat4 modelViewProjection;
|
||||
|
||||
varying vec2 v_st;
|
||||
|
||||
void main() {
|
||||
v_st = st;
|
||||
vec3 positionHigh = position3DHigh + normal * scale;
|
||||
vec3 positionLow = position3DLow + normal * scale;
|
||||
vec4 positionWC = vec4(positionHigh + (positionLow + translation) / 65536.0, 1.0);
|
||||
gl_Position = modelViewProjection * positionWC;
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
precision highp float;
|
||||
varying vec2 v_st;
|
||||
uniform sampler2D colorTexture;
|
||||
|
||||
void main() {
|
||||
vec2 stripeMultiplier = vec2(1.0, 1.0);
|
||||
// 水平贴合效果
|
||||
stripeMultiplier.y *= abs(fract(v_st.y * 100.0) - 0.5) * 2.0;
|
||||
gl_FragColor = texture2D(colorTexture, v_st * stripeMultiplier);
|
||||
}
|
||||
`
|
||||
,
|
||||
uniforms: {
|
||||
helsing_texture: function () {
|
||||
return that.texture;
|
||||
},
|
||||
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_texture: () => {
|
||||
this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
|
||||
return Reflect.get(this.shadowMap, "_shadowMapTexture");
|
||||
},
|
||||
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_visibleAreaColor: Cesium.Color.fromCssColorString(this.visibleAreaColor),
|
||||
helsing_invisibleAreaColor: Cesium.Color.fromCssColorString(this.invisibleAreaColor),
|
||||
}
|
||||
});
|
||||
this.postStage = this.viewer.scene.postProcessStages.add(postStage);
|
||||
}
|
||||
|
||||
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: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
|
||||
orientation: orientation
|
||||
}),
|
||||
id: Math.random().toString(36).substr(2),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.YELLOWGREEN//new Cesium.Color(0.0, 1.0, 0.0, 1.0)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true)
|
||||
}
|
||||
});
|
||||
console.log('this.lightCamera.frustum', this.lightCamera.frustum)
|
||||
let frustum = this.lightCamera.frustum.clone()
|
||||
// frustum.near = frustum.far-1
|
||||
let instance2 = new Cesium.GeometryInstance({
|
||||
geometry: new Cesium.FrustumGeometry({
|
||||
frustum: frustum,
|
||||
origin: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
|
||||
orientation: orientation,
|
||||
vertexFormat: Cesium.VertexFormat.POSITION_ONLY,
|
||||
}),
|
||||
id: Math.random().toString(36).substr(2),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.YELLOWGREEN.withAlpha(0.1)//new Cesium.Color(0.0, 1.0, 0.0, 1.0)
|
||||
),
|
||||
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
|
||||
})
|
||||
})
|
||||
);
|
||||
this.FrustumBottomSurface = this.viewer.scene.primitives.add(
|
||||
new Cesium.Primitive({
|
||||
geometryInstances: [instance2],
|
||||
appearance: new Cesium.PerInstanceColorAppearance({
|
||||
flat: false,
|
||||
translucent: true
|
||||
})
|
||||
})
|
||||
);
|
||||
|
||||
const radius = this.viewDistance;
|
||||
const angle = this.viewHeading;
|
||||
|
||||
let Ea = 6378137; // 赤道半径
|
||||
let Eb = 6356725; // 极半径
|
||||
const lng = this.options.viewPosition.lng,
|
||||
lat = this.options.viewPosition.lat;
|
||||
let positionArr = [];
|
||||
let boundaryPoints = [];
|
||||
positionArr.push(lng, lat);
|
||||
boundaryPoints.push([lng, lat]);
|
||||
//正北是0°
|
||||
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
|
||||
let end = start + 90;
|
||||
for (let i = start; i <= end; i++) {
|
||||
let dx = radius * Math.sin((i * Math.PI) / 180.0);
|
||||
let dy = radius * Math.cos((i * Math.PI) / 180.0);
|
||||
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
|
||||
let ed = ec * Math.cos((lat * Math.PI) / 180);
|
||||
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
|
||||
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
|
||||
positionArr.push(BJD, BWD);
|
||||
}
|
||||
|
||||
|
||||
|
||||
console.log('positionArr', positionArr)
|
||||
// let polygon = new Cesium.PolygonGeometry({
|
||||
// polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
// Cesium.Cartesian3.fromDegreesArray(positionArr)
|
||||
// ),
|
||||
// height: 0.0,
|
||||
// extrudedHeight: 0.0,
|
||||
// vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
|
||||
// stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
|
||||
// ellipsoid: Cesium.Ellipsoid.WGS84,
|
||||
// granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
|
||||
// perPositionHeight: false, // 每个位置点使用的高度
|
||||
// closeTop: true,
|
||||
// closeBottom: true,
|
||||
// // NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
|
||||
// arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
|
||||
// });
|
||||
// let polygonInstance = new Cesium.GeometryInstance({
|
||||
// geometry: polygon,
|
||||
// name: "ViewershedPolygon",
|
||||
// attributes: {
|
||||
// color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
// Cesium.Color.BLUE.withAlpha(0.6)
|
||||
// ),
|
||||
// show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
// },
|
||||
// });
|
||||
|
||||
// this.FrustumBottomSurface = this.viewer.scene.primitives.add(
|
||||
// new Cesium.GroundPrimitive({
|
||||
// geometryInstances: polygonInstance,
|
||||
// appearance: new Cesium.PerInstanceColorAppearance({
|
||||
// flat: false,
|
||||
// translucent: false
|
||||
// })
|
||||
// })
|
||||
// );
|
||||
// this.FrustumBottomSurface.shadows = 1
|
||||
// console.log(Cesium.GeometryPipeline.toWireframe(polygon))
|
||||
// var model = this.viewer.scene.primitives.add(new Cesium.Model({
|
||||
// // 通过GeometryPipeline.toWireframeGeometry将多边形转换为线框模型
|
||||
// geometry: Cesium.GeometryPipeline.toWireframe(polygon),
|
||||
// // 设置一个简单的材质
|
||||
// material: Cesium.Material.fromType('Color')
|
||||
// }));
|
||||
}
|
||||
|
||||
drawSketch() {
|
||||
this.sketch = this.viewer.entities.add({
|
||||
name: 'sketch',
|
||||
position: Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
|
||||
orientation: Cesium.Transforms.headingPitchRollQuaternion(
|
||||
Cesium.Cartesian3.fromDegrees(this.options.viewPosition.lng, this.options.viewPosition.lat, this.options.viewPosition.alt),
|
||||
Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - 90, this.viewPitch, 0.0)
|
||||
),
|
||||
ellipsoid: {
|
||||
radii: new Cesium.Cartesian3(
|
||||
this.viewDistance,
|
||||
this.viewDistance,
|
||||
this.viewDistance
|
||||
),
|
||||
// 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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function 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));
|
||||
}
|
||||
|
||||
function 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 Test;
|
||||
117
src/Obj/Analysis/test2/TestMaterialProperty.js
Normal file
117
src/Obj/Analysis/test2/TestMaterialProperty.js
Normal file
@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @description 测试
|
||||
*/
|
||||
import MaterialProperty from "../../Materail/MaterialProperty";
|
||||
|
||||
|
||||
|
||||
class TestMaterialProperty extends MaterialProperty {
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
/**
|
||||
* 定义Cesium材质对象
|
||||
*/
|
||||
Cesium.Material.TestMaterialPropertyType = "TestMaterialProperty";
|
||||
Cesium.Material._materialCache.addMaterial(
|
||||
Cesium.Material.TestMaterialPropertyType,
|
||||
{
|
||||
fabric: {
|
||||
type: Cesium.Material.TestMaterialPropertyType,
|
||||
uniforms: {
|
||||
color: new Cesium.Color(1.0, 0.0, 0.0, 0.1),
|
||||
image: Cesium.Material.DefaultImageId,
|
||||
tmask: Cesium.Material.DefaultImageId,
|
||||
speed: 1,
|
||||
repeat: new Cesium.Cartesian2(1, 1),
|
||||
rotate: 0
|
||||
},
|
||||
source: `uniform sampler2D image;
|
||||
uniform float speed;
|
||||
uniform vec4 color;
|
||||
uniform vec2 repeat;
|
||||
|
||||
czm_material czm_getMaterial(czm_materialInput materialInput){
|
||||
czm_material material=czm_getDefaultMaterial(materialInput);
|
||||
mat2 rotationMatrix = mat2(cos(radians(-rotate)), sin(radians(-rotate)), -sin(radians(-rotate)), cos(radians(-rotate)));
|
||||
vec2 st=repeat*materialInput.st*rotationMatrix;
|
||||
float time=fract(czm_frameNumber);
|
||||
vec4 colorImage = texture2D(image,vec2(fract(st.s-time),st.t));
|
||||
vec4 maskImage = texture2D(tmask, vec2(st.s, st.t));
|
||||
material.alpha=colorImage.a*maskImage.r;
|
||||
material.diffuse=colorImage.rgb;
|
||||
return material;
|
||||
}`,
|
||||
},
|
||||
isTranslucent: function () {
|
||||
return true;
|
||||
},
|
||||
}
|
||||
);
|
||||
// Object.defineProperties(PolylineImageTrailMaterialProperty.prototype, {
|
||||
// color: Cesium.createPropertyDescriptor("color"),
|
||||
// speed: Cesium.createPropertyDescriptor("speed"),
|
||||
// image: Cesium.createPropertyDescriptor("image"),
|
||||
// repeat: Cesium.createPropertyDescriptor("repeat"),
|
||||
// });
|
||||
this._image = undefined;
|
||||
this._tmask = undefined;
|
||||
this._imageSubscription = undefined;
|
||||
this._repeat = undefined;
|
||||
this._repeatSubscription = undefined;
|
||||
this.image = options.image;
|
||||
this.tmask = options.tmask;
|
||||
this.repeat = new Cesium.Cartesian2(
|
||||
options.repeat?.x || 1,
|
||||
options.repeat?.y || 1
|
||||
);
|
||||
this.rotate = options.rotate
|
||||
let i=1
|
||||
// setInterval(() => {
|
||||
// this.repeat = new Cesium.Cartesian2(
|
||||
// i++,
|
||||
// options.repeat?.y || 1
|
||||
// );
|
||||
// console.log(this.repeat)
|
||||
// }, 1000);
|
||||
// setInterval(() => {
|
||||
// this.rotate ++
|
||||
// }, 100);
|
||||
}
|
||||
|
||||
getType() {
|
||||
return Cesium.Material.TestMaterialPropertyType;
|
||||
}
|
||||
|
||||
getValue(time, result) {
|
||||
if (!result) {
|
||||
result = {};
|
||||
}
|
||||
|
||||
|
||||
result.color = this.color;
|
||||
result.image = this.image;
|
||||
result.tmask = this.tmask;
|
||||
result.repeat = this.repeat;
|
||||
result.speed = this.speed;
|
||||
result.rotate = this.rotate
|
||||
// console.log(result.repeat)
|
||||
return result;
|
||||
}
|
||||
|
||||
equals(other) {
|
||||
return (
|
||||
this === other ||
|
||||
(other instanceof TestMaterialProperty &&
|
||||
Cesium.Property.equals(this.color, other._color) &&
|
||||
Cesium.Property.equals(this.image, other._image) &&
|
||||
Cesium.Property.equals(this.tmask, other._tmask) &&
|
||||
Cesium.Property.equals(this.repeat, other._repeat) &&
|
||||
Cesium.Property.equals(this.speed, other._speed) &&
|
||||
Cesium.Property.equals(this.rotate, other._rotate))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default TestMaterialProperty;
|
||||
78
src/Obj/Analysis/test2/_element.js
Normal file
78
src/Obj/Analysis/test2/_element.js
Normal file
@ -0,0 +1,78 @@
|
||||
import { attributeElm, labelStyleElm1, labelStyleElm2 } from '../../Element/elm_html'
|
||||
|
||||
function html(that) {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row" style="align-items: flex-start;">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" maxlength="40" type="text" @model="name">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 60%;">
|
||||
<div class="row">
|
||||
<div class="col input-select-unit-box">
|
||||
<span class="label" style="margin-right: 0px;">占地面积:</span>
|
||||
<input class="input input-text" readonly="readonly" type="text" @model="area">
|
||||
<div class="input-select-unit"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<DIV-cy-tabs id="polygon-object-edit-tabs">
|
||||
<DIV-cy-tab-pane label="属性信息">
|
||||
${attributeElm(that)}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="面风格">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">面颜色</span>
|
||||
<div class="color"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">描边颜色</span>
|
||||
<div class="lineColor"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">描边宽度</span>
|
||||
<div class="input-number input-number-unit-2">
|
||||
<input class="input" type="number" title="" min="0" max="99" @model="lineWidth">
|
||||
<span class="unit">px</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">面贴地</span>
|
||||
<input class="btn-switch" type="checkbox" @model="ground">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 165px;">
|
||||
<span class="label">面高度</span>
|
||||
<div class="input-number input-number-unit-2">
|
||||
<input class="input height" type="number" title="" min="-999999" max="9999999" style="width: 120px;">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标注风格">
|
||||
${labelStyleElm1()}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标签风格">
|
||||
${labelStyleElm2()}
|
||||
</DIV-cy-tab-pane>
|
||||
</DIV-cy-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
511
src/Obj/Analysis/test2/_index.js
Normal file
511
src/Obj/Analysis/test2/_index.js
Normal file
@ -0,0 +1,511 @@
|
||||
/*
|
||||
* @Author: Wang jianLei
|
||||
* @Date: 2022-05-17 21:49:28
|
||||
* @Last Modified by: Wang JianLei
|
||||
* @Last Modified time: 2022-05-19 22:08:14
|
||||
*/
|
||||
|
||||
let ViewShed = function (sdk, canvasEleId) {
|
||||
if (!sdk.viewer) throw new Error("no viewer object!");
|
||||
let canvasEle = document.getElementById(canvasEleId);
|
||||
if (!canvasEle) throw new Error("the canvas element is not exist");
|
||||
this.canvasEle = canvasEle;
|
||||
this.viewer = sdk.viewer;
|
||||
this.handler = undefined;
|
||||
this.lightCamera;
|
||||
this.pyramid;
|
||||
this.frustumPrimitive;
|
||||
this.viewershedPolygon;
|
||||
};
|
||||
ViewShed.prototype = {
|
||||
/**
|
||||
* 初始化handler
|
||||
*/
|
||||
initHandler() {
|
||||
if (this.handler) {
|
||||
this.handler.destroy();
|
||||
this.handler = undefined;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 开始执行视域分析
|
||||
* @param {number} precision 精度,值越大创建耗时越长,建议在10~20之间
|
||||
*/
|
||||
createViewshed: function (precision) {
|
||||
let $this = this;
|
||||
let scene = $this.viewer.scene;
|
||||
$this.initHandler();
|
||||
$this.clearAll();
|
||||
$this.handler = new Cesium.ScreenSpaceEventHandler($this.viewer.canvas);
|
||||
$this.handler.setInputAction((event) => {
|
||||
// 禁止地球旋转和缩放,地球的旋转会对鼠标移动监听有影响,所以需要禁止
|
||||
scene.screenSpaceCameraController.enableRotate = false;
|
||||
scene.screenSpaceCameraController.enableZoom = false;
|
||||
scene.globe.depthTestAgainstTerrain = true;
|
||||
let earthPosition = scene.pickPosition(event.position);
|
||||
let pos = $this.cartesian3ToDegree(earthPosition);
|
||||
$this.handler.setInputAction(function (event) {
|
||||
let newPosition = scene.pickPosition(event.endPosition);
|
||||
if (Cesium.defined(newPosition)) {
|
||||
let pos1 = $this.cartesian3ToDegree(newPosition);
|
||||
let distance = Cesium.Cartesian3.distance(newPosition, earthPosition);
|
||||
let angle = $this.getAngle(pos[0], pos[1], pos1[0], pos1[1]);
|
||||
let pitch = $this.getPitch(earthPosition, newPosition);
|
||||
$this.ViewShedOptions = {
|
||||
viewPosition: earthPosition, //观测点 笛卡尔坐标
|
||||
endPosition: newPosition, //目标点 笛卡尔坐标
|
||||
direction: angle, //观测方位角 默认为`0`,范围`0~360`
|
||||
pitch: pitch, //俯仰角,radius,默认为`0`
|
||||
horizontalViewAngle: 90, //可视域水平夹角,默认为 `90`,范围`0~360`
|
||||
verticalViewAngle: 60, //可视域垂直夹角,默认为`60`,范围`0~180`
|
||||
visibleAreaColor: Cesium.Color.GREEN, //可视区域颜色,默认为`green`
|
||||
invisibleAreaColor: Cesium.Color.RED, //不可见区域颜色,默认为`red`
|
||||
visualRange: distance, //距离,单位`米`
|
||||
};
|
||||
$this.updateViewShed();
|
||||
}
|
||||
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
|
||||
|
||||
$this.handler.setInputAction(() => {
|
||||
$this.initHandler();
|
||||
// 开启地球旋转和缩放
|
||||
scene.screenSpaceCameraController.enableRotate = true;
|
||||
scene.screenSpaceCameraController.enableZoom = true;
|
||||
$this.drawViewershed(precision);
|
||||
}, Cesium.ScreenSpaceEventType.LEFT_UP);
|
||||
},
|
||||
|
||||
ReturnDistance(pos0, pos1) {
|
||||
let distance = 0;
|
||||
let point1cartographic = Cesium.Cartographic.fromCartesian(pos0);
|
||||
let point2cartographic = Cesium.Cartographic.fromCartesian(pos1);
|
||||
/**根据经纬度计算出距离**/
|
||||
let geodesic = new Cesium.EllipsoidGeodesic();
|
||||
geodesic.setEndPoints(point1cartographic, point2cartographic);
|
||||
let s = geodesic.surfaceDistance;
|
||||
return s;
|
||||
},
|
||||
getHeight(x, y, objectsToExclude) {
|
||||
let endCartographic = Cesium.Cartographic.fromDegrees(x, y);
|
||||
let endHeight = this.viewer.scene.sampleHeight(
|
||||
endCartographic,
|
||||
objectsToExclude
|
||||
);
|
||||
return endHeight;
|
||||
},
|
||||
|
||||
cartesian3ToDegree: function (Cartesian3) {
|
||||
let _ellipsoid = this.viewer.scene.globe.ellipsoid;
|
||||
let _cartographic = _ellipsoid.cartesianToCartographic(Cartesian3);
|
||||
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
|
||||
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
|
||||
let _alt = _cartographic.height;
|
||||
return [_lng, _lat, _alt];
|
||||
},
|
||||
getAngle: function (lng1, lat1, lng2, lat2) {
|
||||
let dRotateAngle = Math.atan2(Math.abs(lng1 - lng2), Math.abs(lat1 - lat2));
|
||||
if (lng2 >= lng1) {
|
||||
dRotateAngle = lat2 < lat1 ? Math.PI - dRotateAngle : dRotateAngle;
|
||||
} else {
|
||||
dRotateAngle =
|
||||
lat2 >= lat1 ? 2 * Math.PI - dRotateAngle : Math.PI + dRotateAngle;
|
||||
}
|
||||
dRotateAngle = (dRotateAngle * 180) / Math.PI;
|
||||
return dRotateAngle;
|
||||
},
|
||||
|
||||
getPitch(pointA, pointB) {
|
||||
let transfrom = Cesium.Transforms.eastNorthUpToFixedFrame(pointA);
|
||||
const vector = Cesium.Cartesian3.subtract(
|
||||
pointB,
|
||||
pointA,
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
let direction = Cesium.Matrix4.multiplyByPointAsVector(
|
||||
Cesium.Matrix4.inverse(transfrom, transfrom),
|
||||
vector,
|
||||
vector
|
||||
);
|
||||
Cesium.Cartesian3.normalize(direction, direction);
|
||||
return Cesium.Math.PI_OVER_TWO - Cesium.Math.acosClamped(direction.z);
|
||||
},
|
||||
|
||||
updateViewShed: function () {
|
||||
this.clear();
|
||||
this.setLightCamera();
|
||||
this.addVisualPyramid();
|
||||
this.createFrustum();
|
||||
},
|
||||
clear: function () {
|
||||
if (this.pyramid) {
|
||||
this.viewer.entities.removeById(this.pyramid.id);
|
||||
this.pyramid = undefined;
|
||||
}
|
||||
if (this.frustumPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.frustumPrimitive);
|
||||
this.frustumPrimitive = undefined;
|
||||
}
|
||||
if (this.debugModelMatrixPrimitive) {
|
||||
this.viewer.scene.primitives.remove(this.debugModelMatrixPrimitive);
|
||||
this.debugModelMatrixPrimitive = undefined;
|
||||
}
|
||||
},
|
||||
clearAll: function () {
|
||||
this.clear();
|
||||
if (this.viewershedPolygon) {
|
||||
this.viewer.scene.primitives.remove(this.viewershedPolygon);
|
||||
this.viewershedPolygon = undefined;
|
||||
}
|
||||
},
|
||||
addVisualPyramid: function () {
|
||||
let options = this.ViewShedOptions;
|
||||
let position = options.viewPosition;
|
||||
let visualRange = Number(options.visualRange);
|
||||
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
|
||||
this.debugModelMatrixPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.DebugModelMatrixPrimitive({
|
||||
modelMatrix: transform,
|
||||
length: 5.0,
|
||||
})
|
||||
);
|
||||
const halfClock = options.horizontalViewAngle / 2;
|
||||
const halfCone = options.verticalViewAngle / 2;
|
||||
const pitch = Cesium.Math.toDegrees(options.pitch);
|
||||
const ellipsoid = new Cesium.EllipsoidGraphics({
|
||||
radii: new Cesium.Cartesian3(visualRange, visualRange, visualRange),
|
||||
minimumClock: Cesium.Math.toRadians(90 - options.direction - halfClock),
|
||||
maximumClock: Cesium.Math.toRadians(90 - options.direction + halfClock),
|
||||
minimumCone: Cesium.Math.toRadians(90 - pitch - halfCone),
|
||||
maximumCone: Cesium.Math.toRadians(90 - pitch + halfCone),
|
||||
fill: false,
|
||||
outline: true,
|
||||
subdivisions: 256,
|
||||
stackPartitions: 64,
|
||||
slicePartitions: 64,
|
||||
outlineColor: Cesium.Color.YELLOWGREEN.withAlpha(0.5),
|
||||
});
|
||||
const pyramidEntity = new Cesium.Entity({
|
||||
position: position,
|
||||
ellipsoid,
|
||||
});
|
||||
this.pyramid = this.viewer.entities.add(pyramidEntity);
|
||||
},
|
||||
setLightCamera: function () {
|
||||
if (!this.lightCamera) {
|
||||
this.lightCamera = new Cesium.Camera(this.viewer.scene);
|
||||
}
|
||||
let options = this.ViewShedOptions;
|
||||
let visualRange = Number(options.visualRange);
|
||||
this.lightCamera.position = options.viewPosition;
|
||||
this.lightCamera.frustum.near = 0.1;
|
||||
this.lightCamera.frustum.far = visualRange;
|
||||
const hr = Cesium.Math.toRadians(options.horizontalViewAngle);
|
||||
const vr = Cesium.Math.toRadians(options.verticalViewAngle);
|
||||
this.lightCamera.frustum.aspectRatio =
|
||||
(visualRange * Math.tan(hr / 2) * 2) /
|
||||
(visualRange * Math.tan(vr / 2) * 2);
|
||||
this.lightCamera.frustum.fov = hr > vr ? hr : vr;
|
||||
this.lightCamera.setView({
|
||||
destination: options.viewPosition,
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(options.direction || 0),
|
||||
pitch: options.pitch || 0,
|
||||
roll: 0,
|
||||
},
|
||||
});
|
||||
},
|
||||
createFrustum: function () {
|
||||
const scratchRight = new Cesium.Cartesian3();
|
||||
const scratchRotation = new Cesium.Matrix3();
|
||||
const scratchOrientation = new Cesium.Quaternion();
|
||||
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 instanceOutline = new Cesium.GeometryInstance({
|
||||
geometry: new Cesium.FrustumOutlineGeometry({
|
||||
frustum: this.lightCamera.frustum,
|
||||
origin: this.ViewShedOptions.viewPosition,
|
||||
orientation: orientation,
|
||||
}),
|
||||
id: "视椎体轮廓线" + Math.random().toString(36).substr(2),
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
new Cesium.Color(0.0, 1.0, 0.0, 1.0)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true),
|
||||
},
|
||||
});
|
||||
this.frustumPrimitive = this.viewer.scene.primitives.add(
|
||||
new Cesium.Primitive({
|
||||
geometryInstances: instanceOutline,
|
||||
appearance: new Cesium.PerInstanceColorAppearance({
|
||||
flat: true,
|
||||
translucent: false,
|
||||
closed: true,
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
createPoint: function (firstPos, secondPos) {
|
||||
let entity4FirstPos = new Cesium.Entity({
|
||||
name: "firstPos",
|
||||
show: true,
|
||||
position: firstPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 20,
|
||||
color: Cesium.Color.RED,
|
||||
outlineColor: Cesium.Color.YELLOW,
|
||||
outlineWidth: 5,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体起点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4FirstPos);
|
||||
let entity4SecondPos = new Cesium.Entity({
|
||||
name: "secondPos",
|
||||
show: true,
|
||||
position: secondPos,
|
||||
point: {
|
||||
show: true,
|
||||
pixelSize: 30,
|
||||
color: Cesium.Color.YELLOW,
|
||||
outlineColor: Cesium.Color.RED,
|
||||
outlineWidth: 8,
|
||||
},
|
||||
description: `
|
||||
<p>这是绘制的视椎体视角终点</p>`,
|
||||
});
|
||||
this.viewer.entities.add(entity4SecondPos);
|
||||
},
|
||||
|
||||
//绘制可视域
|
||||
add(positionArr) {
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
Cesium.Cartesian3.fromDegreesArray(positionArr)
|
||||
),
|
||||
height: 0.0,
|
||||
extrudedHeight: 0.0,
|
||||
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
|
||||
stRotation: 0.0, // 纹理的旋转坐标(以弧度为单位),正旋转是逆时针方向
|
||||
ellipsoid: Cesium.Ellipsoid.WGS84,
|
||||
granularity: Cesium.Math.RADIANS_PER_DEGREE, // 每个纬度和经度之间的距离(以弧度为单位),确定缓冲区中的位置数
|
||||
perPositionHeight: false, // 每个位置点使用的高度
|
||||
closeTop: true,
|
||||
closeBottom: true,
|
||||
// NONE 与椭圆表面不符的直线;GEODESIC 遵循测地路径;RHUMB 遵循大黄蜂或恶魔般的道路。
|
||||
arcType: Cesium.ArcType.GEODESIC, // 多边形边缘线型
|
||||
});
|
||||
|
||||
let polygonInstance = new Cesium.GeometryInstance({
|
||||
geometry: polygon,
|
||||
name: "ViewershedPolygon",
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.BLUE.withAlpha(0.6)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(true), //显示或者隐藏
|
||||
},
|
||||
});
|
||||
this.viewershedPolygon = this.viewer.scene.primitives.add(
|
||||
new Cesium.GroundPrimitive({
|
||||
geometryInstances: polygonInstance,
|
||||
appearance: new Cesium.EllipsoidSurfaceAppearance({
|
||||
aboveGround: true,
|
||||
material: new Cesium.Material({
|
||||
fabric: {
|
||||
type: "Image",
|
||||
uniforms: {
|
||||
image: this.returnImgae(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
drawViewershed(precision) {
|
||||
const pos = this.cartesian3ToDegree(this.ViewShedOptions.viewPosition);
|
||||
const radius = this.ViewShedOptions.visualRange;
|
||||
const direction = this.ViewShedOptions.direction;
|
||||
let boundary = this.computeBoundaryOptions(pos, radius, direction);
|
||||
const bbox = boundary.bbox;
|
||||
let mask = turf.polygon([boundary.boundaryPoints]);
|
||||
const dis = this.ViewShedOptions.visualRange / (precision * 1000);
|
||||
let gridPoints = turf.pointGrid(bbox, dis, { mask: mask });
|
||||
|
||||
let pointsResult = this.createTargetPoints(gridPoints, dis, pos);
|
||||
let variogram = kriging.train(
|
||||
pointsResult.values,
|
||||
pointsResult.lngs,
|
||||
pointsResult.lats,
|
||||
"exponential",
|
||||
0,
|
||||
100
|
||||
);
|
||||
let grid = kriging.grid([boundary.boundaryPoints], variogram, dis / 1000);
|
||||
const colors = [
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#ff000080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
"#00ff0080",
|
||||
];
|
||||
|
||||
this.canvasEle.width = 3840;
|
||||
this.canvasEle.height = 2160;
|
||||
kriging.plot(
|
||||
this.canvasEle,
|
||||
grid,
|
||||
[bbox[0], bbox[2]],
|
||||
[bbox[1], bbox[3]],
|
||||
colors
|
||||
);
|
||||
this.add(boundary.positionArr);
|
||||
},
|
||||
computeBoundaryOptions(pos, radius, angle) {
|
||||
let Ea = 6378137; // 赤道半径
|
||||
let Eb = 6356725; // 极半径
|
||||
const lng = pos[0],
|
||||
lat = pos[1];
|
||||
const bbox = [lng, lat, lng, lat]; //[minX, minY, maxX, maxY]
|
||||
let positionArr = [];
|
||||
let boundaryPoints = [];
|
||||
positionArr.push(lng, lat);
|
||||
boundaryPoints.push([lng, lat]);
|
||||
//正北是0°
|
||||
let start = angle + 45 > 360 ? angle - 45 - 360 : angle - 45;
|
||||
let end = start + 90;
|
||||
for (let i = start; i <= end; i++) {
|
||||
let dx = radius * Math.sin((i * Math.PI) / 180.0);
|
||||
let dy = radius * Math.cos((i * Math.PI) / 180.0);
|
||||
let ec = Eb + ((Ea - Eb) * (90.0 - lat)) / 90.0;
|
||||
let ed = ec * Math.cos((lat * Math.PI) / 180);
|
||||
let BJD = lng + ((dx / ed) * 180.0) / Math.PI;
|
||||
let BWD = lat + ((dy / ec) * 180.0) / Math.PI;
|
||||
positionArr.push(BJD, BWD);
|
||||
boundaryPoints.push([BJD, BWD]);
|
||||
this.refreshBBox(bbox, BJD, BWD);
|
||||
}
|
||||
boundaryPoints.push([lng, lat]);
|
||||
return {
|
||||
positionArr,
|
||||
boundaryPoints,
|
||||
bbox,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 更新外围矩形 Bbox
|
||||
* @param {Array} result 外围矩形Bbox-[minX, minY, maxX, maxY]
|
||||
* @param {Number} x 经度
|
||||
* @param {Number} y 纬度
|
||||
*/
|
||||
refreshBBox(result, x, y) {
|
||||
result[0] = x < result[0] ? x : result[0];
|
||||
result[1] = y < result[1] ? y : result[1];
|
||||
result[2] = x > result[2] ? x : result[2];
|
||||
result[3] = y > result[3] ? y : result[3];
|
||||
},
|
||||
/**
|
||||
* 插值点用射线判断通视性
|
||||
* @param {*} gridPoints 网格点
|
||||
* @param {*} step 步长,可以理解成是精度
|
||||
* @param {*} sourcePos 视域分析起点
|
||||
* @returns kriging插值所需的参数对象{ values:[], lngs:[], lats:[]}
|
||||
*/
|
||||
createTargetPoints(gridPoints, step, sourcePos) {
|
||||
let positionArr = [];
|
||||
let objectsToExclude = [
|
||||
this.frustumPrimitive,
|
||||
this.pyramid,
|
||||
this.debugModelMatrixPrimitive,
|
||||
];
|
||||
let values = [],
|
||||
lngs = [],
|
||||
lats = [];
|
||||
let height = this.getHeight(sourcePos[0], sourcePos[1], objectsToExclude);
|
||||
positionArr.push({
|
||||
x: sourcePos[0],
|
||||
y: sourcePos[1],
|
||||
z: height,
|
||||
});
|
||||
let viewPoint = this.ViewShedOptions.viewPosition;
|
||||
for (let index = 0; index < gridPoints.features.length; index++) {
|
||||
const feature = gridPoints.features[index];
|
||||
const coords = feature.geometry.coordinates;
|
||||
const x = coords[0],
|
||||
y = coords[1];
|
||||
let h = this.getHeight(x, y, objectsToExclude);
|
||||
let endPoint = Cesium.Cartesian3.fromDegrees(x, y, h);
|
||||
let direction = Cesium.Cartesian3.normalize(
|
||||
Cesium.Cartesian3.subtract(
|
||||
endPoint,
|
||||
viewPoint,
|
||||
new Cesium.Cartesian3()
|
||||
),
|
||||
new Cesium.Cartesian3()
|
||||
);
|
||||
// 建立射线
|
||||
let ray = new Cesium.Ray(viewPoint, direction);
|
||||
let result = this.viewer.scene.pickFromRay(ray, objectsToExclude); // 计算交互点,返回第一个
|
||||
if (result) {
|
||||
let buffer = this.ReturnDistance(endPoint, result.position);
|
||||
// let M_color = Cesium.Color.GREEN;
|
||||
if (buffer > step) {
|
||||
// M_color = Cesium.Color.RED;
|
||||
values.push(0);
|
||||
} else {
|
||||
values.push(1);
|
||||
}
|
||||
lngs.push(x);
|
||||
lats.push(y);
|
||||
// this.viewer.entities.add(
|
||||
// new Cesium.Entity({
|
||||
// name: "插值点哦",
|
||||
// show: true,
|
||||
// position: endPoint,
|
||||
// point: {
|
||||
// show: true,
|
||||
// pixelSize: 10,
|
||||
// color: M_color,
|
||||
// outlineWidth: 2,
|
||||
// outlineColor: Cesium.Color.YELLOW,
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
}
|
||||
}
|
||||
return {
|
||||
values,
|
||||
lngs,
|
||||
lats,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* canvas转image图片
|
||||
* @returns base64图片
|
||||
*/
|
||||
returnImgae() {
|
||||
return this.canvasEle.toDataURL("image/png");
|
||||
},
|
||||
};
|
||||
|
||||
export default ViewShed;
|
||||
72
src/Obj/Analysis/test2/glsl.js
Normal file
72
src/Obj/Analysis/test2/glsl.js
Normal file
@ -0,0 +1,72 @@
|
||||
export default `
|
||||
#define USE_CUBE_MAP_SHADOW true
|
||||
uniform sampler2D colorTexture;
|
||||
uniform sampler2D depthTexture;
|
||||
varying vec2 v_textureCoordinates;
|
||||
uniform mat4 camera_projection_matrix;
|
||||
uniform mat4 camera_view_matrix;
|
||||
uniform vec4 helsing_visibleAreaColor;
|
||||
uniform vec4 helsing_invisibleAreaColor;
|
||||
uniform sampler2D helsing_texture;
|
||||
|
||||
vec4 getPositionEC(){
|
||||
return czm_windowToEyeCoordinates(gl_FragCoord);
|
||||
}
|
||||
|
||||
vec3 getNormalEC(){
|
||||
return vec3(1.);
|
||||
}
|
||||
|
||||
vec4 toEye(in vec2 uv,in float depth){
|
||||
vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
|
||||
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
|
||||
posInCamera=posInCamera/posInCamera.w;
|
||||
return posInCamera;
|
||||
}
|
||||
|
||||
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
|
||||
vec3 v01=point-planeOrigin;
|
||||
float d=dot(planeNormal,v01);
|
||||
return(point-planeNormal*d);
|
||||
}
|
||||
|
||||
float getDepth(in vec4 depth){
|
||||
float z_window=czm_unpackDepth(depth);
|
||||
z_window=czm_reverseLogDepth(z_window);
|
||||
float n_range=czm_depthRange.near;
|
||||
float f_range=czm_depthRange.far;
|
||||
return(2.*z_window-n_range-f_range)/(f_range-n_range);
|
||||
}
|
||||
|
||||
bool visible(in vec4 result)
|
||||
{
|
||||
result.x/=result.w;
|
||||
result.y/=result.w;
|
||||
result.z/=result.w;
|
||||
return result.x>=-1.&&result.x<=1.
|
||||
&&result.y>=-1.&&result.y<=1.
|
||||
&&result.z>=-1.&&result.z<=1.;
|
||||
}
|
||||
|
||||
void main(){
|
||||
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
|
||||
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
|
||||
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
|
||||
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
|
||||
// 视角 = (纹理坐标, 深度)
|
||||
vec4 positionEC = toEye(v_textureCoordinates, depth);
|
||||
// 世界坐标
|
||||
vec4 wordPos = czm_inverseView * positionEC;
|
||||
// 虚拟相机中坐标
|
||||
vec4 vcPos = camera_view_matrix * wordPos;
|
||||
vec4 videoColor = texture2D(helsing_texture, v_textureCoordinates);
|
||||
float dis = length(vcPos.xyz);
|
||||
vec4 posInEye = camera_projection_matrix * vcPos;
|
||||
// 可视区颜色
|
||||
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
|
||||
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
|
||||
if(visible(posInEye)){
|
||||
vec4 out_FragColor = helsing_visibleAreaColor;
|
||||
gl_FragColor = mix(gl_FragColor,videoColor,1.0);
|
||||
}
|
||||
}`;
|
||||
2095
src/Obj/Analysis/test2/index.js
Normal file
2095
src/Obj/Analysis/test2/index.js
Normal file
File diff suppressed because one or more lines are too long
184
src/Obj/Analysis/test2/index2.js
Normal file
184
src/Obj/Analysis/test2/index2.js
Normal file
@ -0,0 +1,184 @@
|
||||
import Base from "../../Base/index";
|
||||
class PolygonObject extends Base {
|
||||
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
super(sdk, options);
|
||||
this.create()
|
||||
}
|
||||
|
||||
create() {
|
||||
let fragmentShaderSource = `
|
||||
varying vec3 v_normal;
|
||||
varying vec2 v_uv;
|
||||
uniform vec2 repeat;
|
||||
uniform sampler2D image;
|
||||
uniform vec4 color;
|
||||
uniform sampler2D colorTexture;
|
||||
czm_material czm_getMaterial(czm_materialInput materialInput)
|
||||
{
|
||||
czm_material material = czm_getDefaultMaterial(materialInput);
|
||||
material.diffuse = czm_gammaCorrect(texture2D(image, fract(repeat * materialInput.st)).rgb * color.rgb);
|
||||
material.alpha = texture2D(image, fract(repeat * materialInput.st)).a * color.a;
|
||||
return material;
|
||||
}
|
||||
|
||||
|
||||
varying vec3 v_positionMC;
|
||||
varying vec3 v_positionEC;
|
||||
varying vec2 v_st;
|
||||
void main()
|
||||
{
|
||||
czm_materialInput materialInput;
|
||||
vec3 normalEC = normalize(czm_normal3D * czm_geodeticSurfaceNormal(v_positionMC, vec3(0.0), vec3(1.0)));
|
||||
#ifdef FACE_FORWARD
|
||||
normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);
|
||||
#endif
|
||||
materialInput.s = v_st.s;
|
||||
materialInput.st = v_st;
|
||||
materialInput.str = vec3(v_st, 0.0);
|
||||
materialInput.normalEC = normalEC;
|
||||
materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, materialInput.normalEC);
|
||||
vec3 positionToEyeEC = -v_positionEC;
|
||||
materialInput.positionToEyeEC = positionToEyeEC;
|
||||
czm_material material = czm_getMaterial(materialInput);
|
||||
#ifdef FLAT
|
||||
gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
|
||||
#else
|
||||
gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
|
||||
#endif
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const vertexShaderSource = `
|
||||
// attribute vec3 position;
|
||||
// attribute vec3 normal;
|
||||
// varying vec3 v_normal;
|
||||
// attribute vec2 uv;
|
||||
// varying vec2 v_uv;
|
||||
// void main()
|
||||
// {
|
||||
// gl_Position = czm_modelViewProjection * vec4(position, 1.);
|
||||
// v_normal = normal;
|
||||
// v_uv = uv;
|
||||
// }
|
||||
attribute vec3 position3DHigh;
|
||||
attribute vec3 position3DLow;
|
||||
attribute vec2 st;
|
||||
attribute float batchId;
|
||||
varying vec3 v_positionMC;
|
||||
varying vec3 v_positionEC;
|
||||
varying vec2 v_st;
|
||||
attribute vec3 position;
|
||||
void main()
|
||||
{
|
||||
vec4 p = vec4(position, 1.);
|
||||
v_positionMC = position3DHigh + position3DLow;
|
||||
v_positionEC = (czm_modelViewRelativeToEye * p).xyz;
|
||||
v_st = st;
|
||||
gl_Position = czm_modelViewProjectionRelativeToEye * p;
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
class ly_primitive {
|
||||
|
||||
constructor(options) {
|
||||
this.drawCommand = undefined
|
||||
|
||||
if (Cesium.defined(options)) {
|
||||
this.modelMatrix = options.modelMatrix
|
||||
this.geometry = options.geometry
|
||||
console.log('this.geometry', this.geometry)
|
||||
}
|
||||
}
|
||||
|
||||
createCommand(context) {
|
||||
let t = performance.now()
|
||||
if (!Cesium.defined(this.geometry)) return
|
||||
const geometry = Cesium.BoxGeometry.createGeometry(this.geometry)
|
||||
this.vertexarray = Cesium.VertexArray.fromGeometry({
|
||||
context: context,
|
||||
geometry: geometry
|
||||
})
|
||||
const renderstate = Cesium.RenderState.fromCache({
|
||||
depthTest: {
|
||||
enabled: true
|
||||
}
|
||||
// blending: {
|
||||
// enabled: true
|
||||
// }
|
||||
// cull: {
|
||||
// enabled: false,
|
||||
// }
|
||||
})
|
||||
const shaderProgram = Cesium.ShaderProgram.fromCache({
|
||||
context: context,
|
||||
vertexShaderSource: vertexShaderSource,
|
||||
fragmentShaderSource: fragmentShaderSource
|
||||
})
|
||||
const that = this
|
||||
const videoElm = document.getElementsByTagName('video')[0];
|
||||
let texture = new Cesium.Texture({
|
||||
context: context,
|
||||
source: videoElm
|
||||
})
|
||||
const uniformmap = {
|
||||
colorTexture: function() {
|
||||
texture.copyFrom({
|
||||
source: videoElm
|
||||
})
|
||||
return texture
|
||||
},
|
||||
iTime: function() {
|
||||
return (performance.now() - t) / 1000
|
||||
}
|
||||
}
|
||||
|
||||
this.drawCommand = new Cesium.DrawCommand({
|
||||
boundingVolume: this.geometry.boundingSphere,
|
||||
modelMatrix: this.modelMatrix,
|
||||
// pass: Cesium.Pass.OPAQUE,
|
||||
pass: Cesium.Pass.TRANSLUCENT,
|
||||
shaderProgram: shaderProgram,
|
||||
renderState: renderstate,
|
||||
vertexArray: this.vertexarray,
|
||||
uniformMap: uniformmap
|
||||
})
|
||||
}
|
||||
|
||||
update(frameState) {
|
||||
if (!this.drawCommand) {
|
||||
this.createCommand(frameState.context)
|
||||
}
|
||||
frameState.commandList.push(this.drawCommand)
|
||||
}
|
||||
}
|
||||
|
||||
let positions = this.options.positions
|
||||
let fromDegreesArray = []
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
fromDegreesArray.push(positions[i].lng, positions[i].lat)
|
||||
}
|
||||
|
||||
const video = document.getElementsByTagName('video')[0];
|
||||
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(
|
||||
Cesium.Cartesian3.fromDegreesArray(fromDegreesArray)
|
||||
),
|
||||
height: 100
|
||||
});
|
||||
|
||||
const options = {
|
||||
modelMatrix: Cesium.Matrix4.IDENTITY,
|
||||
geometry: Cesium.PolygonGeometry.createGeometry(polygon)
|
||||
}
|
||||
|
||||
this.sdk.viewer.scene.primitives.add(
|
||||
new ly_primitive(options)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default PolygonObject
|
||||
93
src/Obj/Base/AssembleObject/_element.js
Normal file
93
src/Obj/Base/AssembleObject/_element.js
Normal file
@ -0,0 +1,93 @@
|
||||
import { attributeElm, labelStyleElm1, labelStyleElm2 } from '../../Element/elm_html'
|
||||
|
||||
function html(that) {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row" style="align-items: flex-start;">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" maxlength="40" type="text" @model="name">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 60%;">
|
||||
<div class="row">
|
||||
<div class="col input-select-unit-box">
|
||||
<span class="label" style="margin-right: 0px;">投影面积:</span>
|
||||
<input class="input input-text" readonly="readonly" type="text" @model="area">
|
||||
<div class="input-select-unit"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<DIV-cy-tabs id="assemble-object-edit-tabs">
|
||||
<DIV-cy-tab-pane label="属性信息">
|
||||
${attributeElm(that)}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="空间信息">
|
||||
<div class="row">
|
||||
<div class="col height-mode-box">
|
||||
<span class="label" style="flex: 0 0 56px;">高度模式</span>
|
||||
<div class="height-mode"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">Z值统一增加</span>
|
||||
<div class="input-number input-number-unit-1 height-box">
|
||||
<input class="input height" type="number" title="" min="-9999999" max="999999999">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
<button class="confirm height-confirm" style="margin-left: 5px;">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="table spatial-info-table">
|
||||
<div class="table-head">
|
||||
<div class="tr">
|
||||
<div class="th"></div>
|
||||
<div class="th">经度(X)</div>
|
||||
<div class="th">纬度(Y)</div>
|
||||
<div class="th">高度(Z)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="面风格">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">面颜色</span>
|
||||
<div class="color"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">边线颜色</span>
|
||||
<div class="lineColor"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">边线宽度</span>
|
||||
<div class="input-number input-number-unit-2" style="width: 80px;">
|
||||
<input class="input" type="number" title="" min="0" max="99" @model="lineWidth">
|
||||
<span class="unit">px</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标注风格">
|
||||
${labelStyleElm1()}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标签风格">
|
||||
${labelStyleElm2()}
|
||||
</DIV-cy-tab-pane>
|
||||
</DIV-cy-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
2419
src/Obj/Base/AssembleObject/index.js
Normal file
2419
src/Obj/Base/AssembleObject/index.js
Normal file
File diff suppressed because it is too large
Load Diff
115
src/Obj/Base/AttackArrowObject/_element.js
Normal file
115
src/Obj/Base/AttackArrowObject/_element.js
Normal file
@ -0,0 +1,115 @@
|
||||
import { attributeElm, labelStyleElm1, labelStyleElm2 } from '../../Element/elm_html'
|
||||
|
||||
function html(that) {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row" style="align-items: flex-start;">
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 56px;">名称</span>
|
||||
<input class="input" maxlength="40" type="text" @model="name">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 60%;">
|
||||
<div class="row">
|
||||
<div class="col input-select-unit-box">
|
||||
<span class="label" style="margin-right: 0px;">投影面积:</span>
|
||||
<input class="input input-text" readonly="readonly" type="text" @model="area">
|
||||
<div class="input-select-unit"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">动画时长</span>
|
||||
<div class="input-number input-number-unit-3">
|
||||
<input class="input blur" type="number" title="" min="500" max="9999999" @model="spreadTime">
|
||||
<span class="unit">ms</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 60%;">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">动画</span>
|
||||
<input class="btn-switch" type="checkbox" @model="spreadState">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">动画重复</span>
|
||||
<input class="btn-switch" type="checkbox" @model="loop">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<DIV-cy-tabs id="attack-arrow-object-edit-tabs">
|
||||
<DIV-cy-tab-pane label="属性信息">
|
||||
${attributeElm(that)}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="空间信息">
|
||||
<div class="row">
|
||||
<div class="col height-mode-box">
|
||||
<span class="label" style="flex: 0 0 56px;">高度模式</span>
|
||||
<div class="height-mode"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">Z值统一增加</span>
|
||||
<div class="input-number input-number-unit-1 height-box">
|
||||
<input class="input height" type="number" title="" min="-9999999" max="999999999">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
<button class="confirm height-confirm" style="margin-left: 5px;">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="table spatial-info-table">
|
||||
<div class="table-head">
|
||||
<div class="tr">
|
||||
<div class="th"></div>
|
||||
<div class="th">经度(X)</div>
|
||||
<div class="th">纬度(Y)</div>
|
||||
<div class="th">高度(Z)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="面风格">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">面颜色</span>
|
||||
<div class="color"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">边线颜色</span>
|
||||
<div class="lineColor"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">边线宽度</span>
|
||||
<div class="input-number input-number-unit-2" style="width: 80px;">
|
||||
<input class="input" type="number" title="" min="0" max="99" @model="lineWidth">
|
||||
<span class="unit">px</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标注风格">
|
||||
${labelStyleElm1()}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标签风格">
|
||||
${labelStyleElm2()}
|
||||
</DIV-cy-tab-pane>
|
||||
</DIV-cy-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
2689
src/Obj/Base/AttackArrowObject/index.js
Normal file
2689
src/Obj/Base/AttackArrowObject/index.js
Normal file
File diff suppressed because it is too large
Load Diff
74
src/Obj/Base/BaseSource/BaseLayer/ArcgisImagery/index.js
Normal file
74
src/Obj/Base/BaseSource/BaseLayer/ArcgisImagery/index.js
Normal file
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-11-20 19:00
|
||||
* @description:index
|
||||
* @update: 2023-11-20 19:00
|
||||
*/
|
||||
|
||||
import BaseLayer from "../index";
|
||||
import { setSplitDirection, setActiveId } from '../../../../../Global/SplitScreen'
|
||||
|
||||
class ArcgisLayer extends BaseLayer {
|
||||
constructor(sdk, options = {}) {
|
||||
super(sdk, options);
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "layer"
|
||||
}
|
||||
|
||||
async createArcGis(url) {
|
||||
let imageryProvider
|
||||
if (Number(Cesium.VERSION.split('.')[1]) >= 107) {
|
||||
imageryProvider = await Cesium.ArcGisMapServerImageryProvider.fromUrl(url);
|
||||
}
|
||||
else {
|
||||
imageryProvider = new Cesium.ArcGisMapServerImageryProvider({
|
||||
url
|
||||
});
|
||||
}
|
||||
if (this.options.hasOwnProperty("layer_index")) {
|
||||
this.entity = this.sdk.viewer.imageryLayers.addImageryProvider(imageryProvider, this.options.layer_index)
|
||||
} else {
|
||||
this.entity = this.sdk.viewer.imageryLayers.addImageryProvider(imageryProvider,)
|
||||
}
|
||||
this.entity._id = this.options.id
|
||||
for (let i = 0; i < this.sdk.viewer.imageryLayers._layers.length; i++) {
|
||||
if (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider && this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type && (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'flw' || this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'jww')) {
|
||||
let layer = this.sdk.viewer.imageryLayers._layers[i]
|
||||
this.sdk.viewer.imageryLayers.raiseToTop(layer)
|
||||
}
|
||||
}
|
||||
this.show = this.options.show
|
||||
this.alpha = this.options.alpha
|
||||
this.brightness = this.options.brightness
|
||||
|
||||
if(this.options.show) {
|
||||
setSplitDirection(0, this.options.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ArcgisWXImagery extends ArcgisLayer {
|
||||
constructor(sdk, options) {
|
||||
super(sdk, options);
|
||||
this.createArcGis("https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer")
|
||||
}
|
||||
}
|
||||
|
||||
class ArcgisBLUEImagery extends ArcgisLayer {
|
||||
constructor(sdk, options) {
|
||||
super(sdk, options);
|
||||
this.createArcGis("https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer")
|
||||
}
|
||||
}
|
||||
|
||||
class ArcgisLWImagery extends ArcgisLayer {
|
||||
constructor(sdk, options) {
|
||||
super(sdk, options);
|
||||
this.createArcGis("https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer")
|
||||
}
|
||||
}
|
||||
|
||||
export {ArcgisWXImagery, ArcgisBLUEImagery, ArcgisLWImagery}
|
||||
72
src/Obj/Base/BaseSource/BaseLayer/GdImagery/index.js
Normal file
72
src/Obj/Base/BaseSource/BaseLayer/GdImagery/index.js
Normal file
@ -0,0 +1,72 @@
|
||||
import BaseLayer from "../index";
|
||||
import { setSplitDirection, setActiveId } from '../../../../../Global/SplitScreen'
|
||||
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-11-20 19:18
|
||||
* @description:index
|
||||
* @update: 2023-11-20 19:18
|
||||
*/
|
||||
class GdImagery extends BaseLayer {
|
||||
constructor(sdk, options = {}) {
|
||||
super(sdk, options);
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "layer"
|
||||
}
|
||||
|
||||
createGD(url) {
|
||||
let gdLayer = new Cesium.UrlTemplateImageryProvider({
|
||||
url,
|
||||
minimumLevel: 3,
|
||||
maximumLevel: 18,
|
||||
tilingScheme: this.amapMercatorTilingScheme()
|
||||
})
|
||||
|
||||
if (this.options.hasOwnProperty("layer_index")) {
|
||||
this.entity = this.sdk.viewer.imageryLayers.addImageryProvider(gdLayer, this.options.layer_index)
|
||||
} else {
|
||||
this.entity = this.sdk.viewer.imageryLayers.addImageryProvider(gdLayer,)
|
||||
}
|
||||
this.entity._id = this.options.id
|
||||
for (let i = 0; i < this.sdk.viewer.imageryLayers._layers.length; i++) {
|
||||
if (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider && this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type && (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'flw' || this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'jww')) {
|
||||
let layer = this.sdk.viewer.imageryLayers._layers[i]
|
||||
this.sdk.viewer.imageryLayers.raiseToTop(layer)
|
||||
}
|
||||
}
|
||||
this.show = this.options.show
|
||||
this.alpha = this.options.alpha
|
||||
this.brightness = this.options.brightness
|
||||
|
||||
if(this.options.show) {
|
||||
|
||||
setSplitDirection(0, this.options.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GDLWImagery extends GdImagery {
|
||||
constructor(earth, options = {}) {
|
||||
super(earth, options);
|
||||
this.createGD("https://webst02.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8")
|
||||
}
|
||||
}
|
||||
|
||||
class GDWXImagery extends GdImagery {
|
||||
constructor(earth, options = {}) {
|
||||
super(earth, options);
|
||||
this.createGD("https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}")
|
||||
}
|
||||
}
|
||||
|
||||
class GDSLImagery extends GdImagery {
|
||||
constructor(earth, options = {}) {
|
||||
super(earth, options);
|
||||
this.createGD("https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}")
|
||||
}
|
||||
}
|
||||
|
||||
export {GDLWImagery, GDWXImagery, GDSLImagery}
|
||||
147
src/Obj/Base/BaseSource/BaseLayer/Layer/index.js
Normal file
147
src/Obj/Base/BaseSource/BaseLayer/Layer/index.js
Normal file
@ -0,0 +1,147 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-11-20 15:51
|
||||
* @description:index
|
||||
* @update: 2023-11-20 15:51
|
||||
*/
|
||||
import { getHost } from "../../../../../on";
|
||||
import { syncData } from '../../../../../Global/MultiViewportMode'
|
||||
import BaseLayer from "../index";
|
||||
import { setSplitDirection, setActiveId } from '../../../../../Global/SplitScreen'
|
||||
|
||||
|
||||
class Layer extends BaseLayer {
|
||||
constructor(sdk, options = {}) {
|
||||
super(sdk, options)
|
||||
this.object = {}
|
||||
this.options.host = this.options.host || getHost()
|
||||
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "layer"
|
||||
}
|
||||
|
||||
on() {
|
||||
return this.add()
|
||||
}
|
||||
|
||||
|
||||
async add() {
|
||||
let res = await this.requestResource()
|
||||
let text = await res.text()
|
||||
text = JSON.parse(text)
|
||||
if ([0, 200].includes(text.code)) {
|
||||
return this.loadLayer(text.data)
|
||||
} else {
|
||||
return new Promise((res, reject) => {
|
||||
reject(text.msg || text.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async loadLayer(data) {
|
||||
this.object = { ...data }
|
||||
let url = ""
|
||||
if (this.object.url.startsWith("http"))
|
||||
url = this.object.url
|
||||
else {
|
||||
if (this.options.host) {
|
||||
let o = new URL(this.object.url, this.options.host)
|
||||
url = o.href
|
||||
} else
|
||||
url = this.object.url
|
||||
}
|
||||
let params = {
|
||||
url: url,
|
||||
mimmumLevel: this.object.minimumLevel,
|
||||
maximumLevel: this.object.maximumLevel,
|
||||
rectangle: new Cesium.Rectangle(
|
||||
Cesium.Math.toRadians(this.object.west),
|
||||
Cesium.Math.toRadians(this.object.south),
|
||||
Cesium.Math.toRadians(this.object.east),
|
||||
Cesium.Math.toRadians(this.object.north)
|
||||
),
|
||||
}
|
||||
// if (this.object.scheme_name === "GeographicTilingScheme") {
|
||||
// console.log("添加GeographicTilingScheme")
|
||||
// params.tilingScheme = new Cesium.GeographicTilingScheme()
|
||||
// }
|
||||
// if (this.object.scheme_name === "amapMercatorTilingScheme") {
|
||||
// console.log("添加amapMercatorTilingScheme")
|
||||
// params.tilingScheme = this.amapMercatorTilingScheme()
|
||||
// }
|
||||
|
||||
let layer
|
||||
// if (this.object.tiletrans === 'tms') {
|
||||
// params.url = params.url.substr(0, params.url.indexOf('{'))
|
||||
// tms = new Cesium.TileMapServiceImageryProvider(params)
|
||||
// } else {
|
||||
// tms = new Cesium.UrlTemplateImageryProvider(params)
|
||||
// }
|
||||
switch (this.object.scheme_name) {
|
||||
case "amapMercatorTilingScheme":
|
||||
params.tilingScheme = this.amapMercatorTilingScheme()
|
||||
break;
|
||||
case "":
|
||||
break;
|
||||
default:
|
||||
params.tilingScheme = new Cesium[this.object.scheme_name]()
|
||||
break;
|
||||
}
|
||||
switch (this.object.load_method) {
|
||||
case "tms":
|
||||
if(this.object.url.endsWith("tilemapresource.xml")){
|
||||
let arr = this.object.url.split("/")
|
||||
arr.pop()
|
||||
let url = arr.join("/")
|
||||
params.url = url
|
||||
}
|
||||
if (Number(Cesium.VERSION.split('.')[1]) >= 107) {
|
||||
layer = await Cesium.TileMapServiceImageryProvider.fromUrl(params.url, params);
|
||||
}
|
||||
else {
|
||||
layer = new Cesium.TileMapServiceImageryProvider(params)
|
||||
}
|
||||
break;
|
||||
case "xyz":
|
||||
layer = new Cesium.UrlTemplateImageryProvider(params)
|
||||
break;
|
||||
case "wmts":
|
||||
layer = new Cesium.WebMapTileServiceImageryProvider(params)
|
||||
break;
|
||||
default:
|
||||
layer = new Cesium.UrlTemplateImageryProvider(params)
|
||||
break;
|
||||
}
|
||||
|
||||
if(!this.sdk || !this.sdk.viewer) {
|
||||
return
|
||||
}
|
||||
if (this.options.hasOwnProperty("layer_index")) {
|
||||
this.entity =
|
||||
this.sdk.viewer.scene.imageryLayers.addImageryProvider(layer, this.options.layer_index)
|
||||
} else {
|
||||
this.entity =
|
||||
this.sdk.viewer.scene.imageryLayers.addImageryProvider(layer,)
|
||||
}
|
||||
this.entity._id = this.options.id
|
||||
for (let i = 0; i < this.sdk.viewer.imageryLayers._layers.length; i++) {
|
||||
if (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider && this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type && (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'flw' || this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'jww')) {
|
||||
let layer = this.sdk.viewer.imageryLayers._layers[i]
|
||||
this.sdk.viewer.imageryLayers.raiseToTop(layer)
|
||||
}
|
||||
}
|
||||
this.show = this.options.show
|
||||
this.alpha = this.options.alpha
|
||||
this.brightness = this.options.brightness
|
||||
|
||||
if(this.options.show) {
|
||||
setSplitDirection(0, this.options.id)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Layer
|
||||
58
src/Obj/Base/BaseSource/BaseLayer/Layer3rdparty/index.js
vendored
Normal file
58
src/Obj/Base/BaseSource/BaseLayer/Layer3rdparty/index.js
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-11-20 15:51
|
||||
* @description:index
|
||||
* @update: 2023-11-20 15:51
|
||||
*/
|
||||
import BaseLayer from "../index";
|
||||
import { setSplitDirection, setActiveId } from '../../../../../Global/SplitScreen'
|
||||
|
||||
|
||||
class Layer3rdparty extends BaseLayer {
|
||||
constructor(sdk, options = {}) {
|
||||
super(sdk, options)
|
||||
this.loadLayer()
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "layer"
|
||||
}
|
||||
|
||||
loadLayer(data) {
|
||||
let params = {
|
||||
url: this.options.url,
|
||||
mimmumLevel: this.options.minimumLevel || 0,
|
||||
maximumLevel: this.options.maximumLevel || 20,
|
||||
subdomains: ['0','1','2','3','4','5','6','7'],
|
||||
}
|
||||
|
||||
let layer
|
||||
|
||||
layer = new Cesium.UrlTemplateImageryProvider(params)
|
||||
if (this.options.hasOwnProperty("layer_index")) {
|
||||
this.entity =
|
||||
this.sdk.viewer.scene.imageryLayers.addImageryProvider(layer, this.options.layer_index)
|
||||
} else {
|
||||
this.entity =
|
||||
this.sdk.viewer.scene.imageryLayers.addImageryProvider(layer,)
|
||||
}
|
||||
this.entity._id = this.options.id
|
||||
for (let i = 0; i < this.sdk.viewer.imageryLayers._layers.length; i++) {
|
||||
if (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider && this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type && (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'flw' || this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'jww')) {
|
||||
let layer = this.sdk.viewer.imageryLayers._layers[i]
|
||||
this.sdk.viewer.imageryLayers.raiseToTop(layer)
|
||||
}
|
||||
}
|
||||
this.show = this.options.show
|
||||
this.alpha = this.options.alpha
|
||||
this.brightness = this.options.brightness
|
||||
|
||||
if(this.options.show) {
|
||||
setSplitDirection(0, this.options.id)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Layer3rdparty
|
||||
322
src/Obj/Base/BaseSource/BaseLayer/index.js
Normal file
322
src/Obj/Base/BaseSource/BaseLayer/index.js
Normal file
@ -0,0 +1,322 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-11-20 18:06
|
||||
* @description:index
|
||||
* @update: 2023-11-20 18:06
|
||||
*/
|
||||
|
||||
import Dialog from '../../../Element/Dialog';
|
||||
import CoordTransform from "../../../../transform/CoordTransform";
|
||||
import BaseSource from "../index";
|
||||
import { syncData, get2DView } from '../../../../Global/MultiViewportMode'
|
||||
import { setSplitDirection, syncSplitData } from '../../../../Global/SplitScreen'
|
||||
import { setActiveViewer, closeRotateAround, closeViewFollow } from '../../../../Global/global'
|
||||
|
||||
class BaseLayer extends BaseSource {
|
||||
constructor(sdk, options, _Dialog = {}) {
|
||||
super(sdk, options);
|
||||
this.options.name = options.name || '未命名对象'
|
||||
this.Dialog = _Dialog
|
||||
this._elms = {};
|
||||
}
|
||||
|
||||
setDefaultValue() {
|
||||
super.setDefaultValue();
|
||||
this.options.alpha = this.options.alpha ?? 1
|
||||
this.options.brightness = this.options.brightness ?? 1
|
||||
}
|
||||
|
||||
get layerIndex() {
|
||||
return this.entity ? this.entity._layerIndex : undefined
|
||||
}
|
||||
|
||||
get layer_index() {
|
||||
return this.entity ? this.entity._layerIndex : undefined
|
||||
}
|
||||
|
||||
get brightness() {
|
||||
return this.options.brightness
|
||||
}
|
||||
|
||||
set brightness(v) {
|
||||
this.options.brightness = v
|
||||
this.entity.brightness = v
|
||||
}
|
||||
|
||||
get alpha() {
|
||||
return this.options.alpha
|
||||
}
|
||||
|
||||
|
||||
set alpha(v) {
|
||||
if (Number(v) > 1) v = 1
|
||||
if (Number(v) < 0) v = 0
|
||||
this.entity.alpha = v
|
||||
this.options.alpha = v
|
||||
this._elms.alpha && this._elms.alpha.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
/**@description 提高图层的一层层级
|
||||
* @method layerRaise
|
||||
* @param id {string} 图层id
|
||||
*@memberOf Layer
|
||||
* */
|
||||
layerRaise(id) {
|
||||
this.sdk.viewer.imageryLayers.raise(this.entity)
|
||||
for (let i = 0; i < this.sdk.viewer.imageryLayers._layers.length; i++) {
|
||||
if (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider && this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type && (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'flw' || this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'jww')) {
|
||||
let layer = this.sdk.viewer.imageryLayers._layers[i]
|
||||
this.sdk.viewer.imageryLayers.raiseToTop(layer)
|
||||
}
|
||||
}
|
||||
this.options.layer_index = this.entity._layerIndex
|
||||
return this.entity._layerIndex
|
||||
}
|
||||
|
||||
/**@description 降低图层的一层层级
|
||||
* @method layerLower
|
||||
* @memberOf Layer
|
||||
|
||||
* */
|
||||
layerLower() {
|
||||
this.sdk.viewer.imageryLayers.lower(this.entity)
|
||||
this.options.layer_index = this.entity._layerIndex
|
||||
return this.entity._layerIndex
|
||||
}
|
||||
|
||||
/**@description 置顶
|
||||
* @method layerToTop
|
||||
* @memberOf Layer
|
||||
|
||||
* */
|
||||
layerToTop() {
|
||||
this.sdk.viewer.imageryLayers.raiseToTop(this.entity)
|
||||
for (let i = 0; i < this.sdk.viewer.imageryLayers._layers.length; i++) {
|
||||
if (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider && this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type && (this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'flw' || this.sdk.viewer.imageryLayers._layers[i]._imageryProvider._type === 'jww')) {
|
||||
let layer = this.sdk.viewer.imageryLayers._layers[i]
|
||||
this.sdk.viewer.imageryLayers.raiseToTop(layer)
|
||||
}
|
||||
}
|
||||
this.options.layer_index = this.entity._layerIndex
|
||||
return this.entity._layerIndex
|
||||
}
|
||||
|
||||
/**@description 置底
|
||||
* @method lowerToBottom
|
||||
* @memberOf Layer
|
||||
|
||||
* */
|
||||
layerToBottom() {
|
||||
this.sdk.viewer.imageryLayers.lowerToBottom(this.entity)
|
||||
this.options.layer_index = this.entity._layerIndex
|
||||
return this.entity._layerIndex
|
||||
}
|
||||
|
||||
remove() {
|
||||
super.remove()
|
||||
this.sdk.viewer.scene.imageryLayers.remove(this.entity)
|
||||
this.entity = null
|
||||
}
|
||||
|
||||
/**@description 定位
|
||||
* @method flyTo
|
||||
* @memberOf Layer
|
||||
|
||||
* */
|
||||
async flyTo(options = {}) {
|
||||
if (this._error) {
|
||||
return
|
||||
}
|
||||
setActiveViewer(0)
|
||||
closeRotateAround(this.sdk)
|
||||
closeViewFollow(this.sdk)
|
||||
|
||||
if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) {
|
||||
let orientation = {
|
||||
heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0),
|
||||
pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0),
|
||||
roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0)
|
||||
}
|
||||
|
||||
let lng = this.options.customView.relativePosition.lng
|
||||
let lat = this.options.customView.relativePosition.lat
|
||||
let alt = this.options.customView.relativePosition.alt
|
||||
let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
|
||||
let position = { lng: 0, lat: 0 }
|
||||
if (this.options.position) {
|
||||
position = { ...this.options.position }
|
||||
}
|
||||
else if (this.options.positions) {
|
||||
position = { ...this.options.positions[0] }
|
||||
}
|
||||
else if (this.options.center) {
|
||||
position = { ...this.options.center }
|
||||
}
|
||||
else if (this.options.start) {
|
||||
position = { ...this.options.start }
|
||||
}
|
||||
else {
|
||||
if (this.options.hasOwnProperty('lng')) {
|
||||
position.lng = this.options.lng
|
||||
}
|
||||
if (this.options.hasOwnProperty('lat')) {
|
||||
position.lat = this.options.lat
|
||||
}
|
||||
if (this.options.hasOwnProperty('alt')) {
|
||||
position.alt = this.options.alt
|
||||
}
|
||||
}
|
||||
// 如果没有高度值,则获取紧贴高度计算
|
||||
if (!position.hasOwnProperty('alt')) {
|
||||
position.alt = await this.getClampToHeight(position)
|
||||
}
|
||||
lng = this.options.customView.relativePosition.lng + position.lng
|
||||
lat = this.options.customView.relativePosition.lat + position.lat
|
||||
alt = this.options.customView.relativePosition.alt + position.alt
|
||||
destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
this.sdk.viewer.camera.flyTo({
|
||||
destination: destination,
|
||||
orientation: orientation
|
||||
})
|
||||
}
|
||||
else {
|
||||
this.sdk.viewer.flyTo(this.entity)
|
||||
}
|
||||
}
|
||||
|
||||
/*高德的纠偏*/
|
||||
amapMercatorTilingScheme(options) {
|
||||
class AmapMercatorTilingScheme extends Cesium.WebMercatorTilingScheme {
|
||||
constructor(options) {
|
||||
super(options)
|
||||
let projection = new Cesium.WebMercatorProjection()
|
||||
this._projection.project = function (cartographic, result) {
|
||||
result = CoordTransform.WGS84ToGCJ02(
|
||||
Cesium.Math.toDegrees(cartographic.longitude),
|
||||
Cesium.Math.toDegrees(cartographic.latitude)
|
||||
)
|
||||
result = projection.project(
|
||||
new Cesium.Cartographic(
|
||||
Cesium.Math.toRadians(result[0]),
|
||||
Cesium.Math.toRadians(result[1])
|
||||
)
|
||||
)
|
||||
return new Cesium.Cartesian2(result.x, result.y)
|
||||
}
|
||||
this._projection.unproject = function (cartesian, result) {
|
||||
let cartographic = projection.unproject(cartesian)
|
||||
result = CoordTransform.GCJ02ToWGS84(
|
||||
Cesium.Math.toDegrees(cartographic.longitude),
|
||||
Cesium.Math.toDegrees(cartographic.latitude)
|
||||
)
|
||||
return new Cesium.Cartographic(
|
||||
Cesium.Math.toRadians(result[0]),
|
||||
Cesium.Math.toRadians(result[1])
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new AmapMercatorTilingScheme(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 编辑框
|
||||
* @param state=false {boolean} 状态: true打开, false关闭
|
||||
*/
|
||||
async edit(state = false) {
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
this._DialogObject = await new Dialog(this.sdk, this.options, {
|
||||
title: '底图属性', left: '180px', top: '100px',
|
||||
confirmCallBack: (options) => {
|
||||
this.name = this.name.trim()
|
||||
if (!this.name) {
|
||||
this.name = '未命名对象'
|
||||
}
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
this._DialogObject.close()
|
||||
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.originalOptions)
|
||||
// syncData(this.sdk, this.options.id)
|
||||
syncSplitData(this.sdk, this.options.id)
|
||||
let sdk2D = get2DView()
|
||||
if (sdk2D && sdk2D != this.sdk) {
|
||||
for(let i=0;i<sdk2D.viewer.imageryLayers._layers.length;i++) {
|
||||
let layer = sdk2D.viewer.imageryLayers._layers[i]
|
||||
if(layer._id && layer._id == this.options.id) {
|
||||
layer.alpha = this.options.alpha
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
closeCallBack: () => {
|
||||
this.reset()
|
||||
this.Dialog.closeCallBack && this.Dialog.closeCallBack()
|
||||
},
|
||||
// resetCallBack: () => {
|
||||
// this.name = this.originalOptions.name
|
||||
// this.alpha = this.originalOptions.alpha
|
||||
// this.Dialog.resetCallBack && this.Dialog.resetCallBack()
|
||||
// },
|
||||
removeCallBack: () => {
|
||||
this.Dialog.removeCallBack && this.Dialog.removeCallBack()
|
||||
},
|
||||
}, true)
|
||||
let contentElm = document.createElement('div')
|
||||
contentElm.style.width = '300px'
|
||||
let html = `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 60px;">名称:</span>
|
||||
<input class="input name" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 60px;">透明度:</span>
|
||||
<input type="range" class="alpha" min="0" max="1" step="0.01">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
contentElm.innerHTML = html
|
||||
let nameElm = contentElm.getElementsByClassName('name')[0]
|
||||
let alphaElm = contentElm.getElementsByClassName('alpha')[0]
|
||||
nameElm.value = this.name
|
||||
alphaElm.value = this.alpha
|
||||
nameElm.addEventListener('input', () => {
|
||||
this.name = nameElm.value
|
||||
})
|
||||
alphaElm.addEventListener('input', () => {
|
||||
this.alpha = alphaElm.value
|
||||
})
|
||||
this._DialogObject.contentAppChild(contentElm)
|
||||
this._elms.name = [nameElm]
|
||||
this._elms.alpha = [alphaElm]
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (!this.entity && !this._DialogObject) {
|
||||
return
|
||||
}
|
||||
this.options = this.deepCopyObj(this.originalOptions)
|
||||
this.name = this.options.name
|
||||
this.alpha = this.options.alpha
|
||||
this.brightness = this.options.brightness
|
||||
}
|
||||
|
||||
flicker() { }
|
||||
}
|
||||
|
||||
export default BaseLayer
|
||||
196
src/Obj/Base/BaseSource/BaseModel/Model/_element.js
Normal file
196
src/Obj/Base/BaseSource/BaseModel/Model/_element.js
Normal file
@ -0,0 +1,196 @@
|
||||
|
||||
import { attributeElm, labelStyleElm1, labelStyleElm2 } from '../../../../Element/elm_html'
|
||||
|
||||
function html(that) {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row" style="align-items: flex-start;">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" maxlength="40" type="text" @model="name">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 60px;">颜色</span>
|
||||
<div class="color"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">经度</span>
|
||||
<input class="input" type="number" title="" min="-180" max="180" @model="lng">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 60px;">最大比例</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0.1" max="99999" step="0.1" @model="maximumScale">
|
||||
<span class="unit">倍</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">纬度</span>
|
||||
<input class="input" type="number" title="" min="-90" max="90" @model="lat">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 60px;">最小像素</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="1" max="99999" @model="minimumPixelSize">
|
||||
<span class="unit">px</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">高度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="-99999" max="9999999" @model="alt">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label" style="flex: 0 0 60px;">固定大小</span>
|
||||
<input class="btn-switch" type="checkbox" @model="scaleByDistance">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<DIV-cy-tabs id="model-edit-tabs">
|
||||
<DIV-cy-tab-pane label="属性信息">
|
||||
${attributeElm(that)}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="方向信息">
|
||||
<div>
|
||||
<div class="row">
|
||||
<p class="lable-left-line">旋转</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">x 轴</span>
|
||||
<input style="flex: 1;margin-right: 15px;" type="range" max="360" min="0" step="0.01" @model="rotateX">
|
||||
<div class="input-number input-number-unit-1" style="width: auto;">
|
||||
<input style="width: 100px;" type="number" title="" min="0" max="360" @model="rotateX">
|
||||
<span class="unit">°</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">y 轴</span>
|
||||
<input style="flex: 1;margin-right: 15px;" type="range" max="360" min="0" step="0.01" @model="rotateY">
|
||||
<div class="input-number input-number-unit-1" style="width: auto;">
|
||||
<input style="width: 100px;" type="number" title="" min="0" max="360" @model="rotateY">
|
||||
<span class="unit">°</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">z 轴</span>
|
||||
<input style="flex: 1;margin-right: 15px;" type="range" max="360" min="0" step="0.01" @model="rotateZ">
|
||||
<div class="input-number input-number-unit-1" style="width: auto;">
|
||||
<input style="width: 100px;" type="number" title="" min="0" max="360" @model="rotateZ">
|
||||
<span class="unit">°</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="lable-left-line">
|
||||
<span>缩放</span>
|
||||
<div class="checkbox-box">
|
||||
<input type="checkbox">
|
||||
<span>是否等比例缩放</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-equal" style="display: none;">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">x 轴</span>
|
||||
<input style="flex: 1;margin-right: 15px;" type="range" max="99999" min="0" step="1" @model="scaleX">
|
||||
<div class="input-number input-number-unit-1" style="width: auto;">
|
||||
<input style="width: 100px;" type="number" title="" min="0" max="99999" @model="scaleX">
|
||||
<span class="unit">倍</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">y 轴</span>
|
||||
<input style="flex: 1;margin-right: 15px;" type="range" max="99999" min="0" step="1" @model="scaleY">
|
||||
<div class="input-number input-number-unit-1" style="width: auto;">
|
||||
<input style="width: 100px;" type="number" title="" min="0" max="99999" @model="scaleY">
|
||||
<span class="unit">倍</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">z 轴</span>
|
||||
<input style="flex: 1;margin-right: 15px;" type="range" max="99999" min="0" step="1" @model="scaleZ">
|
||||
<div class="input-number input-number-unit-1" style="width: auto;">
|
||||
<input style="width: 100px;" type="number" title="" min="0" max="99999" @model="scaleZ">
|
||||
<span class="unit">倍</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row equal" style="display: none;">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">等比例缩放</span>
|
||||
<input style="flex: 1;margin-right: 15px;" type="range" max="99999" min="0" step="1">
|
||||
<div class="input-number input-number-unit-1" style="width: auto;">
|
||||
<input style="width: 100px;" type="number" title="" min="0" max="99999" step="1">
|
||||
<span class="unit">倍</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p class="lable-left-line">高度</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">高度</span>
|
||||
<input style="flex: 1;margin-right: 15px;" type="range" max="999999" min="-99999" step="0.01" @model="alt">
|
||||
<div class="input-number input-number-unit-1" style="width: auto;">
|
||||
<input style="width: 100px;" type="number" title="" min="-99999" max="999999" @model="alt">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标注风格">
|
||||
${labelStyleElm1()}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标签风格">
|
||||
${labelStyleElm2()}
|
||||
</DIV-cy-tab-pane>
|
||||
</DIV-cy-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
1715
src/Obj/Base/BaseSource/BaseModel/Model/index.js
Normal file
1715
src/Obj/Base/BaseSource/BaseModel/Model/index.js
Normal file
File diff suppressed because it is too large
Load Diff
123
src/Obj/Base/BaseSource/BaseModel/Model2/_element.js
Normal file
123
src/Obj/Base/BaseSource/BaseModel/Model2/_element.js
Normal file
@ -0,0 +1,123 @@
|
||||
|
||||
import { attributeElm, labelStyleElm1, labelStyleElm2 } from '../../../../Element/elm_html'
|
||||
|
||||
function html(that) {
|
||||
return `
|
||||
<div class="row" style="align-items: flex-start;">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" maxlength="40" type="text" @model="name">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">颜色</span>
|
||||
<div class="color"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">经度</span>
|
||||
<input class="input" type="number" title="" min="-180" max="180" @model="lng">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">最大比例</span>
|
||||
<input class="input" type="number" title="" min="0.1" max="99999" step="0.1" @model="maximumScale">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">纬度</span>
|
||||
<input class="input" type="number" title="" min="-90" max="90" @model="lat">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">最小像素</span>
|
||||
<input class="input" type="number" title="" min="1" max="99999" @model="minimumPixelSize">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">高度</span>
|
||||
<input class="input" type="number" title="" min="-9999999" max="999999999" @model="alt">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">视野缩放</span>
|
||||
<input class="btn-switch" type="checkbox" @model="scaleByDistance">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<DIV-cy-tabs id="model-edit-tabs">
|
||||
<DIV-cy-tab-pane label="属性信息">
|
||||
${attributeElm(that)}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="方向信息">
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">x轴</span>
|
||||
<input style="width: 332px;" type="range" max="360" min="0" step="0.01" @model="rotateX">
|
||||
<input style="font-size: 13px;width: 100px;" type="number" title="" min="0" max="360" @model="rotateX">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">y轴</span>
|
||||
<input style="width: 332px;" type="range" max="360" min="0" step="0.01" @model="rotateY">
|
||||
<input style="font-size: 13px;width: 100px;" type="number" title="" min="0" max="360" @model="rotateY">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">z轴</span>
|
||||
<input style="width: 332px;" type="range" max="360" min="0" step="0.01" @model="rotateZ">
|
||||
<input style="font-size: 13px;width: 100px;" type="number" title="" min="0" max="360" @model="rotateZ">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">高度</span>
|
||||
<input style="width: 332px;" type="range" max="999999" min="-99999" step="0.01" @model="alt">
|
||||
<input style="font-size: 13px;width: 100px;" type="number" title="" min="-99999" max="999999" @model="alt">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">缩放</span>
|
||||
<input style="width: 332px;" type="range" max="999" min="0" step="1" @model="scale">
|
||||
<input style="font-size: 13px;width: 100px;" type="number" title="" min="0" max="999" step="1" @model="scale">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标注风格">
|
||||
${labelStyleElm1()}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标签风格">
|
||||
${labelStyleElm2()}
|
||||
</DIV-cy-tab-pane>
|
||||
</DIV-cy-tabs>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
function css() {
|
||||
return `
|
||||
.YJ-custom-base-dialog>.content {
|
||||
width: 600px;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content>div>.row .col {
|
||||
margin: 0 10px;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content>div>.row .col:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content>div>.row .col:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content>div>.row .label {
|
||||
flex: 0 0 74px;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content .DIV-cy-tab-content-pane .input-select {
|
||||
width: 100px;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export { html, css }
|
||||
1349
src/Obj/Base/BaseSource/BaseModel/Model2/index.js
Normal file
1349
src/Obj/Base/BaseSource/BaseModel/Model2/index.js
Normal file
File diff suppressed because it is too large
Load Diff
74
src/Obj/Base/BaseSource/BaseModel/index.js
Normal file
74
src/Obj/Base/BaseSource/BaseModel/index.js
Normal file
@ -0,0 +1,74 @@
|
||||
import richText from "../../../Element/richText";
|
||||
import BaseSource from "../index";
|
||||
|
||||
class BaseModel extends BaseSource {
|
||||
constructor(sdk, options, _Dialog = {}) {
|
||||
super(sdk, options);
|
||||
}
|
||||
|
||||
async add() {
|
||||
if (this.options.url) {
|
||||
return this.loadModel(this.options.url)
|
||||
}
|
||||
}
|
||||
|
||||
_addLink() {
|
||||
// document.getElementsByClassName
|
||||
if (this._DialogObject._element.content.getElementsByClassName('link_add')[0].value) {
|
||||
this.options.attribute.link.content.push({
|
||||
name: '链接',
|
||||
url: this._DialogObject._element.content.getElementsByClassName('link_add')[0].value
|
||||
})
|
||||
this._DialogObject._element.content.getElementsByClassName('link_add')[0].value = ''
|
||||
this.attributeLink = this.options.attribute.link.content
|
||||
}
|
||||
else {
|
||||
this.Dialog.clickAddLink && this.Dialog.clickAddLink()
|
||||
}
|
||||
}
|
||||
|
||||
addAttributeLink(link) {
|
||||
this.options.attribute.link.content.push({
|
||||
name: '链接',
|
||||
url: link
|
||||
})
|
||||
this.attributeLink = this.options.attribute.link.content
|
||||
}
|
||||
|
||||
_addRr() {
|
||||
if (this._DialogObject._element.content.getElementsByClassName('vr_add')[0].value) {
|
||||
this.options.attribute.vr.content.push({
|
||||
name: '全景图' ,
|
||||
url: this._DialogObject._element.content.getElementsByClassName('vr_add')[0].value
|
||||
})
|
||||
this._DialogObject._element.content.getElementsByClassName('vr_add')[0].value = ''
|
||||
this.attributeVr = this.options.attribute.vr.content
|
||||
}
|
||||
else {
|
||||
this.Dialog.clickAddVr && this.Dialog.clickAddVr()
|
||||
}
|
||||
}
|
||||
|
||||
addAttributeRr(vr) {
|
||||
this.options.attribute.vr.content.push({
|
||||
name: '全景图' ,
|
||||
url: vr
|
||||
})
|
||||
this.attributeVr = this.options.attribute.vr.content
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开富文本框
|
||||
*/
|
||||
openRichTextEditor(e) {
|
||||
// var ue = UE.getEditor('app');
|
||||
richText.open(this.options.id, this.options.name, this.options.richTextContent)
|
||||
richText.primaryCallBack = (content) => {
|
||||
this.options.richTextContent = content
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default BaseModel
|
||||
|
||||
293
src/Obj/Base/BaseSource/BaseTerrain/index.js
Normal file
293
src/Obj/Base/BaseSource/BaseTerrain/index.js
Normal file
@ -0,0 +1,293 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-11-20 17:54
|
||||
* @description:index
|
||||
* @update: 2023-11-20 17:54
|
||||
*/
|
||||
import Dialog from '../../../Element/Dialog';
|
||||
import { getHost } from "../../../../on";
|
||||
import BaseSource from "../index";
|
||||
import { setActiveViewer, closeRotateAround, closeViewFollow } from '../../../../Global/global'
|
||||
import { setSplitDirection, syncSplitData } from '../../../../Global/SplitScreen'
|
||||
|
||||
class BaseTerrain extends BaseSource {
|
||||
#updateModelTimeout;
|
||||
constructor(sdk, options = {}, object = {}, _Dialog = {}) {
|
||||
super(sdk, options);
|
||||
this.object = JSON.parse(JSON.stringify(object))
|
||||
this.object.west || (this.object.west = 40)
|
||||
this.object.south || (this.object.south = 30)
|
||||
this.object.east || (this.object.east = 160)
|
||||
this.object.north || (this.object.north = 50)
|
||||
this.show = this.options.show
|
||||
this._elms = {};
|
||||
this.Dialog = _Dialog
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "terrain"
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.options.name
|
||||
}
|
||||
set name(v) {
|
||||
this.options.name = v
|
||||
this._elms.name && this._elms.name.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get show() {
|
||||
return !(
|
||||
this.sdk.viewer.scene.terrainProvider instanceof
|
||||
Cesium.EllipsoidTerrainProvider
|
||||
)
|
||||
}
|
||||
|
||||
set show(status) {
|
||||
status ? this.open() : this.close()
|
||||
}
|
||||
|
||||
async open() {
|
||||
if (this.options.url) {
|
||||
return this.loadTerrain({
|
||||
url: this.options.url
|
||||
})
|
||||
} else {
|
||||
let res = await this.requestResource()
|
||||
let text = await res.text()
|
||||
text = JSON.parse(text)
|
||||
if ([0, 200].includes(text.code)) {
|
||||
if (text.data.url.length)
|
||||
return this.loadTerrain(text.data)
|
||||
else
|
||||
return new Promise((res, reject) => {
|
||||
reject('资源不存在')
|
||||
})
|
||||
} else {
|
||||
return new Promise((res, reject) => {
|
||||
reject(text.msg || text.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//关闭地形
|
||||
close() {
|
||||
this.sdk.viewer.scene.terrainProvider =
|
||||
new Cesium.EllipsoidTerrainProvider({})
|
||||
for (let i = 0; i < YJ.Analysis.AnalysesResults.length; i++) {
|
||||
if (YJ.Analysis.AnalysesResults[i].type === 'ContourAnalysis') {
|
||||
YJ.Analysis.AnalysesResults[i].destroy()
|
||||
}
|
||||
}
|
||||
syncSplitData(this.sdk, this.options.id)
|
||||
|
||||
clearTimeout(this.#updateModelTimeout)
|
||||
this.#updateModelTimeout = setTimeout(() => {
|
||||
clearTimeout(this.#updateModelTimeout)
|
||||
for (let [key, entity] of this.sdk.entityMap) {
|
||||
if (entity.type === 'BillboardObject' && (entity.heightMode == 1 || entity.heightMode == 3)) {
|
||||
entity.updateHeight()
|
||||
}
|
||||
else {
|
||||
if (entity.label) {
|
||||
entity.label.show = entity.label.show
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
async loadTerrain(options) {
|
||||
let object = { ...options }
|
||||
let url = ""
|
||||
if (object.url.startsWith("http"))
|
||||
url = object.url
|
||||
else {
|
||||
//说明是本地的json,在磁盘中存在的
|
||||
if (object.url.includes(":")) {
|
||||
url = object.url
|
||||
} else {
|
||||
if (this.options.host) {
|
||||
let o = new URL(object.url, this.options.host)
|
||||
url = o.href
|
||||
} else
|
||||
url = object.url
|
||||
}
|
||||
}
|
||||
if (Number(Cesium.VERSION.split('.')[1]) >= 107) {
|
||||
this.terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(url)
|
||||
}
|
||||
else {
|
||||
this.terrainProvider = new Cesium.CesiumTerrainProvider({
|
||||
url: url
|
||||
})
|
||||
}
|
||||
if (!this.sdk || !this.sdk.viewer) {
|
||||
return
|
||||
}
|
||||
this.sdk.viewer.terrainProvider = this.terrainProvider;
|
||||
clearTimeout(this.#updateModelTimeout)
|
||||
this.#updateModelTimeout = setTimeout(() => {
|
||||
clearTimeout(this.#updateModelTimeout)
|
||||
this.terrainProvider.readyPromise.then(() => {
|
||||
for (let [key, entity] of this.sdk.entityMap) {
|
||||
if (entity.type === 'BillboardObject' && (entity.heightMode == 1 || entity.heightMode == 3)) {
|
||||
entity.updateHeight()
|
||||
}
|
||||
else {
|
||||
if (entity.label) {
|
||||
entity.label.show = entity.label.show
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}, 1000);
|
||||
|
||||
|
||||
syncSplitData(this.sdk, this.options.id)
|
||||
}
|
||||
|
||||
remove() {
|
||||
this.close()
|
||||
}
|
||||
|
||||
async flyTo(duration = 3) {
|
||||
if (this._error) {
|
||||
return
|
||||
}
|
||||
setActiveViewer(0)
|
||||
closeRotateAround(this.sdk)
|
||||
closeViewFollow(this.sdk)
|
||||
|
||||
if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) {
|
||||
let orientation = {
|
||||
heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0),
|
||||
pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0),
|
||||
roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0)
|
||||
}
|
||||
|
||||
let lng = this.options.customView.relativePosition.lng
|
||||
let lat = this.options.customView.relativePosition.lat
|
||||
let alt = this.options.customView.relativePosition.alt
|
||||
let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
|
||||
let position = { lng: 0, lat: 0 }
|
||||
if (this.options.position) {
|
||||
position = { ...this.options.position }
|
||||
}
|
||||
else if (this.options.positions) {
|
||||
position = { ...this.options.positions[0] }
|
||||
}
|
||||
else if (this.options.center) {
|
||||
position = { ...this.options.center }
|
||||
}
|
||||
else if (this.options.start) {
|
||||
position = { ...this.options.start }
|
||||
}
|
||||
else {
|
||||
if (this.options.hasOwnProperty('lng')) {
|
||||
position.lng = this.options.lng
|
||||
}
|
||||
if (this.options.hasOwnProperty('lat')) {
|
||||
position.lat = this.options.lat
|
||||
}
|
||||
if (this.options.hasOwnProperty('alt')) {
|
||||
position.alt = this.options.alt
|
||||
}
|
||||
}
|
||||
// 如果没有高度值,则获取紧贴高度计算
|
||||
if (!position.hasOwnProperty('alt')) {
|
||||
position.alt = await this.getClampToHeight(position)
|
||||
}
|
||||
lng = this.options.customView.relativePosition.lng + position.lng
|
||||
lat = this.options.customView.relativePosition.lat + position.lat
|
||||
alt = this.options.customView.relativePosition.alt + position.alt
|
||||
destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
this.sdk.viewer.camera.flyTo({
|
||||
destination: destination,
|
||||
orientation: orientation,
|
||||
duration
|
||||
})
|
||||
}
|
||||
else {
|
||||
let rectangle = new Cesium.Rectangle(
|
||||
Cesium.Math.toRadians(this.object.west),
|
||||
Cesium.Math.toRadians(this.object.south),
|
||||
Cesium.Math.toRadians(this.object.east),
|
||||
Cesium.Math.toRadians(this.object.north)
|
||||
)
|
||||
this.sdk.viewer.camera.flyTo({
|
||||
destination: rectangle,
|
||||
duration,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setDefaultValue() {
|
||||
super.setDefaultValue()
|
||||
this.options.host = this.options.host || getHost()
|
||||
this.options.url = this.options.url || ""
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 编辑框
|
||||
* @param state=false {boolean} 状态: true打开, false关闭
|
||||
*/
|
||||
async edit(state = false) {
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
this._DialogObject = await new Dialog(this.sdk, this.options, {
|
||||
title: '地形属性', left: '180px', top: '100px',
|
||||
confirmCallBack: (options) => {
|
||||
this.name = this.name.trim()
|
||||
if (!this.name) {
|
||||
this.name = '未命名对象'
|
||||
}
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
this._DialogObject.close()
|
||||
let cdoptions = this.deepCopyObj(this.options)
|
||||
cdoptions.host = ''
|
||||
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(cdoptions)
|
||||
},
|
||||
// resetCallBack: () => {
|
||||
// this.name = this.originalOptions.name
|
||||
// this.Dialog.resetCallBack && this.Dialog.resetCallBack()
|
||||
// },
|
||||
removeCallBack: () => {
|
||||
this.Dialog.removeCallBack && this.Dialog.removeCallBack()
|
||||
},
|
||||
}, true)
|
||||
let contentElm = document.createElement('div')
|
||||
contentElm.style.width = '300px'
|
||||
let html = `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">名称:</span>
|
||||
<input class="input name" type="text">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
contentElm.innerHTML = html
|
||||
let nameElm = contentElm.getElementsByClassName('name')[0]
|
||||
nameElm.value = this.name
|
||||
nameElm.addEventListener('input', () => {
|
||||
this.name = nameElm.value
|
||||
})
|
||||
this._DialogObject.contentAppChild(contentElm)
|
||||
this._elms.name = [nameElm]
|
||||
}
|
||||
|
||||
flicker() { }
|
||||
}
|
||||
|
||||
export default BaseTerrain
|
||||
93
src/Obj/Base/BaseSource/BaseTileset/BIM/_element.js
Normal file
93
src/Obj/Base/BaseSource/BaseTileset/BIM/_element.js
Normal file
@ -0,0 +1,93 @@
|
||||
|
||||
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" @model="name">
|
||||
</div>
|
||||
<div class="col">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">经度</span>
|
||||
<input class="input" type="number" title="" min="-180" max="180" @model="lng">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">x轴</span>
|
||||
<input type="number" title="" min="-180" max="180" @model="roll">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">纬度</span>
|
||||
<input class="input" type="number" title="" min="-90" max="90" @model="lat">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">y轴</span>
|
||||
<input type="number" title="" min="-180" max="180" @model="heading">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">高度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="-9999999" max="999999999" @model="height">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">z轴</span>
|
||||
<input type="number" title="" min="-180" max="180" @model="pitch">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">大小</span>
|
||||
<input type="range" max="10" min="0.1" step="0.1" @model="scale">
|
||||
<div class="input-number" style="width: 100px;margin-left: 10px;">
|
||||
<input type="number" title="" min="0" max="10" step="0.1" @model="scale">
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divide">
|
||||
<div class="line"></div>
|
||||
<p>BIM属性导出选项</p>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div style="display: flex;">
|
||||
<div class="checkbox-box" @click="exportState">
|
||||
<input type="checkbox" value="3">
|
||||
<span>修建中</span>
|
||||
</div>
|
||||
<div class="checkbox-box" @click="exportState">
|
||||
<input type="checkbox" value="2">
|
||||
<span>未完成</span>
|
||||
</div>
|
||||
<div class="checkbox-box" @click="exportState">
|
||||
<input type="checkbox" value="1">
|
||||
<span>已完成</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button @click="exportProperty">导 出</button>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
82
src/Obj/Base/BaseSource/BaseTileset/BIM/_element2.js
Normal file
82
src/Obj/Base/BaseSource/BaseTileset/BIM/_element2.js
Normal file
@ -0,0 +1,82 @@
|
||||
|
||||
|
||||
function html2() {
|
||||
return `
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">构件名称</span>
|
||||
<input class="input" name="name" disabled="disabled">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">选择状态</span>
|
||||
<select class="input input-select" name="state-select">
|
||||
<option value="0" style="color: #000;">重置</option>
|
||||
<option value="1" style="color: #f00;">已完成</option>
|
||||
<option value="2" style="color: #0f0;">未完成</option>
|
||||
<option value="3" style="color: #00f;">修建中</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divide">
|
||||
<div class="line"></div>
|
||||
<p>自定义属性</p>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<div class="property">
|
||||
</div>
|
||||
`
|
||||
}
|
||||
function css2() {
|
||||
return `
|
||||
.YJ-custom-base-dialog>.content {
|
||||
width: 440px;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content .row .label {
|
||||
flex: 0 0 110px;
|
||||
}
|
||||
.col:first-child {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.col:last-child {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content .divide {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content .divide .line{
|
||||
height: 1px;
|
||||
background: #ddd;
|
||||
flex: 1;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content .divide p{
|
||||
margin: 0 10px;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content .property .property-item .btn{
|
||||
font-size: 24px;
|
||||
line-height: 20px;
|
||||
padding: 2px 0;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content .property .property-item .input_lable {
|
||||
flex: 0 0 115px;
|
||||
border: none;
|
||||
margin-right: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content .property .property-item:first-child .delete{
|
||||
display: none
|
||||
}
|
||||
.YJ-custom-base-dialog>.content .property .property-item .add{
|
||||
display: none;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content .property .property-item:last-child .add{
|
||||
display: inline-block;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export { html2, css2 }
|
||||
877
src/Obj/Base/BaseSource/BaseTileset/BIM/index.js
Normal file
877
src/Obj/Base/BaseSource/BaseTileset/BIM/index.js
Normal file
@ -0,0 +1,877 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-11-20 16:05
|
||||
* @description:index
|
||||
* @update: 2023-11-20 16:05
|
||||
*/
|
||||
import BaseTileset from "../index";
|
||||
import { html } from "./_element";
|
||||
import { html2, css2 } from "./_element2";
|
||||
import Dialog from '../../../../Element/Dialog';
|
||||
import EventBinding from '../../../../Element/Dialog/eventBinding';
|
||||
import BaseDialog from '../../../../../BaseDialog'
|
||||
import { syncData } from '../../../../../Global/MultiViewportMode'
|
||||
import { syncSplitData } from '../../../../../Global/SplitScreen'
|
||||
import { setActiveViewer, closeRotateAround, closeViewFollow} from '../../../../../Global/global'
|
||||
|
||||
class BIM extends BaseTileset {
|
||||
#updateModelTimeout
|
||||
/**
|
||||
* @constructor
|
||||
* @description 加载BIM模型
|
||||
* @param sdk {object} sdk
|
||||
* @param options {object} 模型参数
|
||||
* @param options.id {string} 对象id
|
||||
* @param options.show=true {boolean} 模型显隐
|
||||
* @param options.name {string} 名称
|
||||
* @param options.url {string} 资源地址
|
||||
* @param options.position {object} 模型位置
|
||||
* @param options.position.lng {number} 经度
|
||||
* @param options.position.lat {number} 纬度
|
||||
* @param options.position.alt {number} 高度
|
||||
* */
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
super(sdk, options)
|
||||
this.picking = false
|
||||
this.features = options.features || []
|
||||
this.exportStateArray = []
|
||||
this.Dialog = _Dialog
|
||||
this._elms = {};
|
||||
this._EventBinding = new EventBinding()
|
||||
this.Dialog.exportState = (e) => {
|
||||
this.exportState(e)
|
||||
}
|
||||
this.Dialog.exportProperty = (e) => {
|
||||
this.exportProperty(this.exportStateArray)
|
||||
}
|
||||
this.features = new Map()
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "bim"
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.newData.name
|
||||
}
|
||||
|
||||
set name(v) {
|
||||
this.newData.name = v
|
||||
this._elms.name && this._elms.name.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get lng() {
|
||||
return this.newData.lng
|
||||
}
|
||||
|
||||
set lng(v) {
|
||||
this.newData.lng = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch, this.newData.scale)
|
||||
this._elms.lng && this._elms.lng.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get lat() {
|
||||
return this.newData.lat
|
||||
}
|
||||
|
||||
set lat(v) {
|
||||
this.newData.lat = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch, this.newData.scale)
|
||||
this._elms.lat && this._elms.lat.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this.newData.height
|
||||
}
|
||||
|
||||
set height(v) {
|
||||
this.newData.height = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch, this.newData.scale)
|
||||
this._elms.height && this._elms.height.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get roll() {
|
||||
return this.newData.roll
|
||||
}
|
||||
|
||||
set roll(v) {
|
||||
this.newData.roll = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch, this.newData.scale)
|
||||
this._elms.roll && this._elms.roll.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get heading() {
|
||||
return this.newData.heading
|
||||
}
|
||||
|
||||
set heading(v) {
|
||||
this.newData.heading = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch, this.newData.scale)
|
||||
this._elms.heading && this._elms.heading.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get pitch() {
|
||||
return this.newData.pitch
|
||||
}
|
||||
|
||||
set pitch(v) {
|
||||
this.newData.pitch = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch, this.newData.scale)
|
||||
this._elms.pitch && this._elms.pitch.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get scale() {
|
||||
return this.newData.scale
|
||||
}
|
||||
|
||||
set scale(v) {
|
||||
this.newData.scale = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch, this.newData.scale)
|
||||
this._elms.scale && this._elms.scale.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
// get transparency() {
|
||||
// return this.newData.transparency
|
||||
// }
|
||||
|
||||
// set transparency(v) {
|
||||
// this.newData.transparency = v
|
||||
// this.entity.style = new Cesium.Cesium3DTileStyle({
|
||||
// color: "color('rgba(255,255,255," + this.newData.transparency + ")')",
|
||||
// show: true,
|
||||
// });
|
||||
// this.entity.transparency = Number(this.newData.transparency)
|
||||
// this._elms.transparency && this._elms.transparency.forEach((item) => {
|
||||
// item.value = v
|
||||
// })
|
||||
// }
|
||||
|
||||
async loadSceneTree(url) {
|
||||
|
||||
// Cesium.ExperimentalFeatures.enableModelExperimental = true;
|
||||
let array = url.split('/')
|
||||
array[array.length - 1] = 'scenetree.json'
|
||||
|
||||
|
||||
await Cesium.Resource.fetchJson({
|
||||
url: array.join('/')
|
||||
}).then(res => {
|
||||
this.scenetree = res
|
||||
|
||||
const initData = (array) => {
|
||||
array.forEach(item => {
|
||||
if (this.features.has(item.id)) {
|
||||
this.features.get(item.id).sphere = item.sphere
|
||||
}
|
||||
else {
|
||||
this.features.set(item.id, { sphere: item.sphere })
|
||||
}
|
||||
if (item.children) {
|
||||
initData(item.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
initData(res.scenes[0].children)
|
||||
|
||||
// res.scenes[0].children.forEach(item => {
|
||||
// if (this.features.has(item.id)) {
|
||||
// this.features.get(item.id).sphere = item.sphere
|
||||
// }
|
||||
// else {
|
||||
// this.features.set(item.id, {sphere: item.sphere})
|
||||
// }
|
||||
// })
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑框
|
||||
async edit(state) {
|
||||
let _this = this
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
if (state) {
|
||||
|
||||
this._DialogObject = await new Dialog(this.sdk, this.newData, {
|
||||
title: 'BIM模型属性', left: '180px', top: '100px',
|
||||
resetCallBack: () => {
|
||||
this.reset()
|
||||
this.Dialog.resetCallBack && this.Dialog.resetCallBack()
|
||||
},
|
||||
confirmCallBack: (info) => {
|
||||
this.name = this.name.trim()
|
||||
if (!this.name) {
|
||||
this.name = '未命名对象'
|
||||
}
|
||||
this.oldData.name = this.newData.name
|
||||
this.oldData.height = this.newData.height
|
||||
this.oldData.lng = this.newData.lng
|
||||
this.oldData.lat = this.newData.lat
|
||||
// this.oldData.transparency = this.newData.transparency
|
||||
this.oldData.scale = this.newData.scale
|
||||
this.oldData.roll = this.newData.roll
|
||||
this.oldData.heading = this.newData.heading
|
||||
this.oldData.pitch = this.newData.pitch
|
||||
this._DialogObject.close()
|
||||
let features = new Map()
|
||||
this.features.forEach((item, key) => {
|
||||
let data = { ...item }
|
||||
delete data.features
|
||||
features.set(key, data)
|
||||
})
|
||||
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack({ ...this.oldData, features: features, type: this.type })
|
||||
syncSplitData(this.sdk, this.oldData.id)
|
||||
},
|
||||
removeCallBack: () => {
|
||||
this.Dialog.removeCallBack && this.Dialog.removeCallBack()
|
||||
},
|
||||
closeCallBack: () => {
|
||||
this.reset()
|
||||
// this.newData.transparency = this.oldData.transparency
|
||||
// this.newData.name = this.oldData.name
|
||||
// this.newData.height = this.oldData.height
|
||||
// this.newData.lng = this.oldData.lng
|
||||
// this.newData.lat = this.oldData.lat
|
||||
// this.newData.scale = this.oldData.scale
|
||||
// this.entity.style = new Cesium.Cesium3DTileStyle({
|
||||
// color: "color('rgba(255,255,255," + this.newData.transparency + ")')",
|
||||
// show: true,
|
||||
// });
|
||||
this.editObj.destroy()
|
||||
this.Dialog.closeCallBack && this.Dialog.closeCallBack()
|
||||
},
|
||||
showCallBack: (show) => {
|
||||
this.newData.show = show
|
||||
this.entity && (this.entity.show = show)
|
||||
this.Dialog.showCallBack && this.Dialog.showCallBack()
|
||||
},
|
||||
rotateCallBack: () => {
|
||||
if (this.rotationEditing) {
|
||||
this.rotationEditing = false
|
||||
}
|
||||
else {
|
||||
this.rotationEditing = true
|
||||
}
|
||||
},
|
||||
translationalCallBack: () => {
|
||||
if (this.positionEditing) {
|
||||
this.positionEditing = false
|
||||
}
|
||||
else {
|
||||
this.positionEditing = true
|
||||
}
|
||||
}
|
||||
})
|
||||
this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' tileset-bim'
|
||||
// 内容部分
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
this._DialogObject.contentAppChild(contentElm)
|
||||
|
||||
let all_elm = contentElm.getElementsByTagName("*")
|
||||
this._EventBinding.on(this, all_elm)
|
||||
this._elms = this._EventBinding.element
|
||||
|
||||
} else {
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async featureEdit(state, feature) {
|
||||
let _this = this
|
||||
this._element_style = null
|
||||
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
if (state) {
|
||||
// console.log(this.entity)
|
||||
// console.log(this.entity.root.children[0].content)
|
||||
// console.log(feature.getProperty('id'), feature.getProperty('name'), feature.getProperty('state'))
|
||||
let name = feature.getProperty('name')
|
||||
// console.log(feature.getProperty('descriptions'))
|
||||
let data = {
|
||||
id: feature.getProperty('id'),
|
||||
name: name,
|
||||
state: feature.getProperty('state') || 0,
|
||||
descriptions: feature.getProperty('descriptions') || [
|
||||
{
|
||||
id: this.randomString(),
|
||||
key: "点击此处可编辑",
|
||||
value: ""
|
||||
}
|
||||
]
|
||||
}
|
||||
switch (feature.getProperty('state')) {
|
||||
case '0': data.stateCH = ''
|
||||
break
|
||||
case '1': data.stateCH = '已完成'
|
||||
break
|
||||
case '2': data.stateCH = '未完成'
|
||||
break
|
||||
case '3': data.stateCH = '修建中'
|
||||
break
|
||||
}
|
||||
// let feature = this.entity.root.children[0].content.getFeature(0)
|
||||
// console.log(id,feature, this.entity)
|
||||
// return
|
||||
this._element_style = document.createElement('style');
|
||||
this._element_style.type = 'text/css';
|
||||
this._element_style.setAttribute('data-name', 'YJ_style_dialog');
|
||||
this._element_style.innerHTML = css2();
|
||||
|
||||
|
||||
this._DialogObject = await new BaseDialog(this.sdk.viewer._container, {
|
||||
title: this.oldData.name + '-----设置状态', left: 'calc(50% - 200px)', top: 'calc(50% - 200px)',
|
||||
closeCallBack: () => {
|
||||
this.Dialog.closeCallBack && this.Dialog.closeCallBack()
|
||||
}
|
||||
})
|
||||
await this._DialogObject.init()
|
||||
document.getElementsByTagName('head')[0].appendChild(this._element_style);
|
||||
// 内容部分
|
||||
let content = document.createElement('div');
|
||||
content.innerHTML = html2()
|
||||
// 名称
|
||||
let e_name = content.querySelector("input[name='name']")
|
||||
e_name.value = name
|
||||
//状态
|
||||
let e_state = content.querySelector("select[name='state-select']")
|
||||
e_state.value = data.state
|
||||
e_state.addEventListener('change', (e) => {
|
||||
data.state = e.target.value
|
||||
switch (data.state) {
|
||||
case '0': data.stateCH = ''
|
||||
break
|
||||
case '1': data.stateCH = '已完成'
|
||||
break
|
||||
case '2': data.stateCH = '未完成'
|
||||
break
|
||||
case '3': data.stateCH = '修建中'
|
||||
break
|
||||
}
|
||||
});
|
||||
//自定义属性
|
||||
let e_property = content.getElementsByClassName('property')[0]
|
||||
for (let i = 0; i < data.descriptions.length; i++) {
|
||||
createPropertyItem(data.descriptions[i], i)
|
||||
}
|
||||
function createPropertyItem(item) {
|
||||
let html = `<div class="row property-item">
|
||||
<div class="col">
|
||||
<input class="input_lable" name="key" value="${item.key}">
|
||||
<input class="input" name="value" value="${item.value}">
|
||||
<button class="btn add">+</button>
|
||||
<button class="btn delete">-</button>
|
||||
</div>
|
||||
</div>`
|
||||
// document.createRange().createContextualFragment(html)
|
||||
let newElement = document.createElement("div");
|
||||
newElement.innerHTML = html
|
||||
let itemElm = newElement.getElementsByClassName('property-item')[0]
|
||||
let e_key = itemElm.querySelector("input[name='key']")
|
||||
let e_value = itemElm.querySelector("input[name='value']")
|
||||
let e_add = itemElm.getElementsByClassName('add')[0]
|
||||
let e_delete = itemElm.getElementsByClassName('delete')[0]
|
||||
e_key.addEventListener('input', (e) => {
|
||||
item.key = e.target.value
|
||||
})
|
||||
e_value.addEventListener('input', (e) => {
|
||||
item.value = e.target.value
|
||||
})
|
||||
e_add.addEventListener('click', () => {
|
||||
let newItem = {
|
||||
id: _this.randomString(),
|
||||
key: "点击此处可编辑",
|
||||
value: ""
|
||||
}
|
||||
data.descriptions.push(newItem)
|
||||
createPropertyItem(newItem)
|
||||
})
|
||||
e_delete.addEventListener('click', (e) => {
|
||||
for (let i = 0; i < data.descriptions.length; i++) {
|
||||
if (data.descriptions[i].id === item.id) {
|
||||
data.descriptions.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
e_property.removeChild(itemElm)
|
||||
// let item = {
|
||||
// key: "点击此处可编辑",
|
||||
// value: ""
|
||||
// }
|
||||
// createPropertyItem(item)
|
||||
})
|
||||
e_property.appendChild(itemElm)
|
||||
}
|
||||
let target = this._DialogObject._element.foot.getElementsByClassName('translational')[0]
|
||||
this._DialogObject.contentAppChild(content)
|
||||
// foot部分
|
||||
let confirmBtn = document.createElement('button');
|
||||
confirmBtn.className = 'confirm';
|
||||
confirmBtn.innerHTML = '确认'
|
||||
this._DialogObject.footAppChild(confirmBtn, target)
|
||||
confirmBtn.addEventListener('click', () => {
|
||||
let flag = false
|
||||
for (let i = 0; i < this.features.length; i++) {
|
||||
if (this.features[i].id == data.id) {
|
||||
this.features[i] = data
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
this.features.push(data)
|
||||
}
|
||||
feature.setProperty('state', data.state)
|
||||
feature.setProperty('descriptions', data.descriptions)
|
||||
let color = '#fff'
|
||||
switch (data.state) {
|
||||
case '0':
|
||||
color = '#fff'
|
||||
break;
|
||||
case '1':
|
||||
color = '#f00'
|
||||
break;
|
||||
case '2':
|
||||
color = '#0f0'
|
||||
break;
|
||||
case '3':
|
||||
color = '#00f'
|
||||
break;
|
||||
default:
|
||||
}
|
||||
feature.color = Cesium.Color.fromCssColorString(color)
|
||||
this._DialogObject.close()
|
||||
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack({ ...this.newData, features: this.features })
|
||||
});
|
||||
} else {
|
||||
if (this._element_style) {
|
||||
document.getElementsByTagName('head')[0].removeChild(this._element_style)
|
||||
this._element_style = null
|
||||
}
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (!this.entity) {
|
||||
return
|
||||
}
|
||||
// this.transparency = this.oldData.transparency
|
||||
this.name = this.oldData.name
|
||||
this.height = this.oldData.height
|
||||
this.lng = this.oldData.lng
|
||||
this.lat = this.oldData.lat
|
||||
this.roll = this.oldData.roll
|
||||
this.heading = this.oldData.heading
|
||||
this.pitch = this.oldData.pitch
|
||||
this.scale = this.oldData.scale
|
||||
}
|
||||
|
||||
//更新模型位置
|
||||
updateModel(_tx, _ty, _tz, _rx = 0, _ry = 0, _rz = 0, s = 1) {
|
||||
if (!this.tileset.root.transform) {
|
||||
if (window.ELEMENT) {
|
||||
window.ELEMENT.Message.closeAll();
|
||||
window.ELEMENT.Message({
|
||||
message: '该模型不支持移动和旋转!',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
}
|
||||
console.warn('该模型不支持移动和旋转!')
|
||||
return
|
||||
}
|
||||
if ((!_tx && _tx!==0) || (!_ty && _ty!==0) || (!_tz && _tz!==0)) {
|
||||
return
|
||||
}
|
||||
let mx = Cesium.Matrix3.fromRotationX(
|
||||
Cesium.Math.toRadians(_rx)
|
||||
)
|
||||
let my = Cesium.Matrix3.fromRotationY(
|
||||
Cesium.Math.toRadians(_ry)
|
||||
)
|
||||
let mz = Cesium.Matrix3.fromRotationZ(
|
||||
Cesium.Math.toRadians(_rz)
|
||||
)
|
||||
// 平移
|
||||
let m = Cesium.Transforms.eastNorthUpToFixedFrame(new Cesium.Cartesian3.fromDegrees(_tx, _ty, _tz))
|
||||
// 旋转
|
||||
let rotationX = Cesium.Matrix4.fromRotationTranslation(mx)
|
||||
let rotationY = Cesium.Matrix4.fromRotationTranslation(my)
|
||||
let rotationZ = Cesium.Matrix4.fromRotationTranslation(mz)
|
||||
let originalMatrix = new Cesium.Matrix4()
|
||||
Cesium.Matrix4.multiply(m, rotationX, originalMatrix)
|
||||
Cesium.Matrix4.multiply(originalMatrix, rotationY, originalMatrix)
|
||||
Cesium.Matrix4.multiply(originalMatrix, rotationZ, originalMatrix)
|
||||
const scale = Cesium.Matrix4.fromUniformScale(s);
|
||||
Cesium.Matrix4.multiply(originalMatrix, scale, this.entity._root.transform)
|
||||
if (!this.editObj.activeAxis) {
|
||||
this.editObj.position = { lng: _tx, lat: _ty, alt: _tz }
|
||||
}
|
||||
if (!this.editObj.activeCircle) {
|
||||
this.editObj.rotate = { x: _rx, y: _ry, z: _rz }
|
||||
}
|
||||
this.editObj && this.editObj.update()
|
||||
|
||||
clearTimeout(this.#updateModelTimeout)
|
||||
this.#updateModelTimeout = setTimeout(() => {
|
||||
clearTimeout(this.#updateModelTimeout)
|
||||
let center = this.cartesian3Towgs84(this.entity.boundingSphere.center, this.sdk.viewer)
|
||||
let circle = turf.circle([center.lng, center.lat], this.entity.boundingSphere.radius / 1000, { steps: 360, units: 'kilometers' });
|
||||
for (let [key, entity] of this.sdk.entityMap) {
|
||||
if (entity.type === 'BillboardObject' && entity.heightMode == 3) {
|
||||
let pt = turf.point([entity.lng, entity.lat]);
|
||||
if (turf.booleanPointInPolygon(pt, circle)) {
|
||||
entity.updateHeight()
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(entity.label) {
|
||||
entity.label.show = entity.label.show
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
|
||||
// exportProperty(states) {
|
||||
// console.log(this.features)
|
||||
// let fieldKeys = ['name', '', '', '', '', '', '', '', '', '', '', '', 'stateCH', 'descriptions']
|
||||
// let fieldLabels = ['构件名称', '体积', '墩全高H', '墩身高h', '底部高程', '承台宽', '承台长', '承台高', '族', '桩径', '桩长', '结构材质', '完成情况', '自定义属性']
|
||||
|
||||
// let dataStr = fieldLabels.toString() + '\r\n';
|
||||
// for (let i = 0; i < this.features.length; i++) {
|
||||
// for (let j = 0; j < states.length; j++) {
|
||||
// if (this.features[i].state == states[j]) {
|
||||
// fieldKeys.forEach(key => {
|
||||
// if (Array.isArray(this.features[i][key])) {
|
||||
// let str = ''
|
||||
// for (let k in this.features[i][key]) {
|
||||
// str += `${this.features[i][key][k].key + ':' + this.features[i][key][k].value}\n`
|
||||
// }
|
||||
// dataStr += `"${str}"\t`
|
||||
// }
|
||||
// else {
|
||||
// // 加引号是为了使换行符在单元格内正常显示
|
||||
// dataStr += `"${this.features[i][key] ? this.features[i][key] : ''}"\t,`;
|
||||
// }
|
||||
// });
|
||||
// dataStr += '\r\n';
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // encodeURIComponent 解决中文乱码
|
||||
// const url = "data:text/xls;charset=utf-8,\ufeff" + encodeURIComponent(dataStr);
|
||||
// console.log(url)
|
||||
// // const link = document.createElement("a");
|
||||
// // link.href = url;
|
||||
// // link.download = this.oldData.name + "--构件属性.xls";
|
||||
// // link.style.display = 'none';
|
||||
// // document.body.appendChild(link);
|
||||
// // link.click();
|
||||
// // document.body.removeChild(link); //释放标签
|
||||
// }
|
||||
|
||||
getScenetree() {
|
||||
return this.scenetree
|
||||
}
|
||||
|
||||
// 设置feature颜色
|
||||
featureColor(id, color) {
|
||||
if (this.features.has(id)) {
|
||||
let features = this.features.get(id).features
|
||||
for (let key in features) {
|
||||
if (features[key].content._model) {
|
||||
features[key].color = Cesium.Color.fromCssColorString(color)
|
||||
}
|
||||
features[key].customColor = Cesium.Color.fromCssColorString(color)
|
||||
}
|
||||
this.features.get(id).customColor = Cesium.Color.fromCssColorString(color)
|
||||
}
|
||||
}
|
||||
|
||||
getFeatureColor(id) {
|
||||
if (this.features.has(id)) {
|
||||
if (this.features.get(id).customColor) {
|
||||
return this.features.get(id).customColor
|
||||
}
|
||||
let features = this.features.get(id).features
|
||||
for (let key in features) {
|
||||
if (features[key].content._model) {
|
||||
return features[key].customColor || features[key].color
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置feature显隐
|
||||
featureShow(id, show) {
|
||||
if (this.features.has(id)) {
|
||||
let features = this.features.get(id).features
|
||||
for (let key in features) {
|
||||
if (features[key].content._model) {
|
||||
features[key].show = show
|
||||
}
|
||||
features[key].customShow = show
|
||||
}
|
||||
this.features.get(id).customShow = show
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//飞到feature位置
|
||||
async featureFlyto(id) {
|
||||
if (this.features.has(id)) {
|
||||
let sphere = this.features.get(id).sphere
|
||||
let center = new Cesium.Cartesian3(
|
||||
sphere[0],
|
||||
sphere[1],
|
||||
sphere[2]
|
||||
)
|
||||
let srcMatInv = this.entity._root.originalTransform
|
||||
srcMatInv = Cesium.Matrix4.inverse(srcMatInv, new Cesium.Matrix4())
|
||||
let curMat = this.entity._root.transform
|
||||
let mat = Cesium.Matrix4.multiply(curMat, srcMatInv, new Cesium.Matrix4())
|
||||
let center2 = Cesium.Matrix4.multiplyByPoint(
|
||||
mat,
|
||||
center,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
let wgs84 = this.cartesian3Towgs84(center2, this.sdk.viewer)
|
||||
let cartesian3 = Cesium.Cartesian3.fromDegrees(
|
||||
wgs84.lng,
|
||||
wgs84.lat,
|
||||
wgs84.alt + sphere[3]
|
||||
)
|
||||
setActiveViewer(0)
|
||||
closeRotateAround(this.sdk)
|
||||
closeViewFollow(this.sdk)
|
||||
|
||||
this.sdk.viewer.camera.flyTo({
|
||||
destination: cartesian3
|
||||
})
|
||||
// this.entity.style = await new Cesium.Cesium3DTileStyle({
|
||||
// color: "color('rgba(255,255,255,0.2)')"
|
||||
// });
|
||||
this.features.forEach((item, key) => {
|
||||
if (key === id) {
|
||||
let color = this.getFeatureColor(id)
|
||||
if (this.features.has(id) && color) {
|
||||
let features = this.features.get(id).features
|
||||
for (let k in features) {
|
||||
if (features[k].content._model) {
|
||||
features[k].color = color
|
||||
}
|
||||
features[k].customAlpha = 1
|
||||
}
|
||||
this.features.get(id).customAlpha = 1
|
||||
}
|
||||
// this.featureColor(id, `rgba(${Cesium.Color.floatToByte(color.red)},${Cesium.Color.floatToByte(color.green)},${Cesium.Color.floatToByte(color.blue)},${color.alpha})`)
|
||||
}
|
||||
else {
|
||||
let color = this.getFeatureColor(key)
|
||||
if (this.features.has(key) && color) {
|
||||
let features = this.features.get(key).features
|
||||
for (let k in features) {
|
||||
if (features[k].content._model) {
|
||||
features[k].color = Cesium.Color.fromCssColorString(`rgba(${Cesium.Color.floatToByte(color.red)},${Cesium.Color.floatToByte(color.green)},${Cesium.Color.floatToByte(color.blue)},${color.alpha * 0.2})`)
|
||||
}
|
||||
features[k].customAlpha = color.alpha * 0.2
|
||||
}
|
||||
this.features.get(key).customAlpha = color.alpha * 0.2
|
||||
}
|
||||
// this.featureColor(key, `rgba(${Cesium.Color.floatToByte(color.red)},${Cesium.Color.floatToByte(color.green)},${Cesium.Color.floatToByte(color.blue)},${color.alpha * 0.2})`)
|
||||
}
|
||||
})
|
||||
// this.entity.readyPromise.then(()=>{
|
||||
// this.featureColor(id, '#ffffff')
|
||||
// })
|
||||
}
|
||||
else {
|
||||
this.features.forEach((item, key) => {
|
||||
let features = this.features.get(key).features
|
||||
let color = this.getFeatureColor(key)
|
||||
if (color) {
|
||||
for (let k in features) {
|
||||
if (features[k].content._model) {
|
||||
features[k].color = color
|
||||
}
|
||||
features[k].customAlpha = 1
|
||||
}
|
||||
this.features.get(key).customAlpha = color.alpha * 0.2
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 导出属性
|
||||
exportProperty(states) {
|
||||
if (this.exportStateArray.length === 0) {
|
||||
window.ELEMENT && window.ELEMENT.Message({
|
||||
message: '未选择属性导出选项!',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
return
|
||||
}
|
||||
let fieldKeys = ['name', '', '', '', '', '', '', '', '', '', '', '', 'stateCH', 'descriptions']
|
||||
let fieldLabels = ['构件名称', '体积', '墩全高H', '墩身高h', '底部高程', '承台宽', '承台长', '承台高', '族', '桩径', '桩长', '结构材质', '完成情况', '自定义属性']
|
||||
var url = 'data:application/vnd.ms-excel;base64,',
|
||||
tmplWorkbookXML = '<?xml version="1.0"?><?mso-application progid="Excel.Sheet"?><Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">'
|
||||
+ '<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office"><Author>Axel Richter</Author><Created>{created}</Created></DocumentProperties>'
|
||||
+ '<Styles>'
|
||||
+ '<Style ss:ID="Currency"><NumberFormat ss:Format="Currency"></NumberFormat></Style>'
|
||||
+ '<Style ss:ID="Date"><NumberFormat ss:Format="Medium Date"></NumberFormat></Style>'
|
||||
+ '</Styles>'
|
||||
+ '{worksheets}</Workbook>'
|
||||
, tmplWorksheetXML = '<Worksheet ss:Name="{nameWS}"><Table><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="240"/>{rows}</Table></Worksheet>'
|
||||
, tmplCellXML = '<Cell><Data ss:Type="{nameType}">{data}</Data></Cell>'
|
||||
, base64 = function (s) {
|
||||
return window.btoa(unescape(encodeURIComponent(s)))
|
||||
}
|
||||
, format = function (s, c) {
|
||||
return s.replace(/{(\w+)}/g, function (m, p) {
|
||||
return c[p];
|
||||
})
|
||||
}
|
||||
|
||||
var ctx = "";
|
||||
var workbookXML = "";
|
||||
var worksheetsXML = "";
|
||||
var rowsXML = "";
|
||||
|
||||
var pil = 0;
|
||||
for (var i = 0; i < this.features.length; i++) {
|
||||
for (let j = 0; j < states.length; j++) {
|
||||
if (this.features[i].state == states[j]) {
|
||||
if (i == 0) {
|
||||
rowsXML += '<Row>' +
|
||||
'<Cell><Data ss:Type="String">构件名称</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">体积</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">墩全高H</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">墩身高h</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">底部高程</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">承台宽</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">承台长</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">承台高</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">族</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">桩径</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">桩长</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">结构材质</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">完成情况</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">自定义属性</Data></Cell>' +
|
||||
'</Row>';
|
||||
}
|
||||
rowsXML += '<Row>';
|
||||
for (var key in fieldKeys) {
|
||||
if (Array.isArray(this.features[i][fieldKeys[key]])) {
|
||||
let str = ''
|
||||
for (let k in this.features[i][fieldKeys[key]]) {
|
||||
str += `${this.features[i][fieldKeys[key]][k].key + ':' + this.features[i][fieldKeys[key]][k].value} `
|
||||
}
|
||||
ctx = {
|
||||
nameType: 'String',
|
||||
data: str
|
||||
};
|
||||
}
|
||||
else {
|
||||
ctx = {
|
||||
nameType: 'String',
|
||||
data: this.features[i][fieldKeys[key]] || "0"
|
||||
};
|
||||
}
|
||||
rowsXML += format(tmplCellXML, ctx);
|
||||
}
|
||||
rowsXML += '</Row>';
|
||||
if (i > 0 && (i / 60000) % 1 === 0) {
|
||||
pil++;
|
||||
ctx = { rows: rowsXML, nameWS: 'Sheet' + i };
|
||||
worksheetsXML += format(tmplWorksheetXML, ctx);
|
||||
rowsXML = "";
|
||||
rowsXML += '<Row>' +
|
||||
'<Cell><Data ss:Type="String">构件名称</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">体积</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">墩全高H</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">墩身高h</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">底部高程</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">承台宽</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">承台长</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">承台高</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">族</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">桩径</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">桩长</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">结构材质</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">完成情况</Data></Cell>' +
|
||||
'<Cell><Data ss:Type="String">自定义属性</Data></Cell>' +
|
||||
'</Row>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx = { rows: rowsXML, nameWS: 'Sheet' };
|
||||
worksheetsXML += format(tmplWorksheetXML, ctx);
|
||||
rowsXML = "";
|
||||
ctx = { created: (new Date()).getTime(), worksheets: worksheetsXML };
|
||||
workbookXML = format(tmplWorkbookXML, ctx);
|
||||
var link = document.createElement("A");
|
||||
link.href = url + base64(workbookXML);
|
||||
link.download = this.oldData.name + "--构件属性.xls"
|
||||
link.target = '_blank';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
exportState(e) {
|
||||
let checkbox = e.target.getElementsByTagName('input')[0]
|
||||
checkbox.checked = !checkbox.checked
|
||||
if (checkbox.checked) {
|
||||
this.exportStateArray.push(checkbox.value)
|
||||
this.exportStateArray = Array.from(new Set(this.exportStateArray))
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < this.exportStateArray.length; i++) {
|
||||
if (this.exportStateArray[i] == checkbox.value) {
|
||||
this.exportStateArray.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default BIM
|
||||
54
src/Obj/Base/BaseSource/BaseTileset/Tileset/_element.js
Normal file
54
src/Obj/Base/BaseSource/BaseTileset/Tileset/_element.js
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" @model="name">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">高度</span>
|
||||
<div class="input-number input-number-unit-3">
|
||||
<input class="input" type="number" title="" @model="height">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">精度</span>
|
||||
<div class="input-number input-number-unit-3">
|
||||
<input class="input" type="number" title="" min="0.1" max="10" step="0.1" @model="accuracy">
|
||||
<span class="unit">倍</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">透视</span>
|
||||
<input type="range" min="0" max="1" step="0.01" @model="transparency">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
function css() {
|
||||
return `
|
||||
.YJ-custom-base-dialog>.content {
|
||||
width: 420px;
|
||||
}
|
||||
.YJ-custom-base-dialog>.content .row .label {
|
||||
flex: 0 0 45px;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export { html, css }
|
||||
87
src/Obj/Base/BaseSource/BaseTileset/Tileset/eventBinding.js
Normal file
87
src/Obj/Base/BaseSource/BaseTileset/Tileset/eventBinding.js
Normal file
@ -0,0 +1,87 @@
|
||||
class eventBinding {
|
||||
constructor() {
|
||||
this.element = {}
|
||||
}
|
||||
static event = {}
|
||||
|
||||
getEvent(name) {
|
||||
return eventBinding.event[name]
|
||||
}
|
||||
|
||||
getEventAll() {
|
||||
return eventBinding.event
|
||||
}
|
||||
|
||||
setEvent(name, event) {
|
||||
eventBinding.event[name] = event
|
||||
}
|
||||
|
||||
on(that, elements) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let Event = []
|
||||
let isEvent = false
|
||||
let removeName = []
|
||||
if (!elements[i] || !elements[i].attributes) {
|
||||
continue;
|
||||
}
|
||||
for (let m of elements[i].attributes) {
|
||||
switch (m.name) {
|
||||
case '@model': {
|
||||
isEvent = true
|
||||
if (elements[i].type == 'checkbox') {
|
||||
Event.push((e) => { that[m.value] = e.target.checked })
|
||||
elements[i].checked = that[m.value]
|
||||
}
|
||||
else {
|
||||
Event.push((e) => {
|
||||
let value = e.target.value
|
||||
if(e.target.type == 'number') {
|
||||
value = Number(value)
|
||||
}
|
||||
that[m.value] = value
|
||||
})
|
||||
if(elements[i].nodeName=='IMG') {
|
||||
elements[i].src = that[m.value]
|
||||
}
|
||||
else {
|
||||
elements[i].value = that[m.value]
|
||||
}
|
||||
}
|
||||
this.element[m.value] = elements[i]
|
||||
removeName.push(m.name)
|
||||
break;
|
||||
}
|
||||
case '@click': {
|
||||
elements[i].addEventListener('click', (e) => {
|
||||
if (typeof (that.Dialog[m.value]) === 'function') {
|
||||
that.Dialog[m.value](e)
|
||||
}
|
||||
});
|
||||
removeName.push(m.name)
|
||||
// elements[i].attributes.removeNamedItem(m.name)
|
||||
break;
|
||||
}
|
||||
}
|
||||
// elements[i].attributes[m] = undefined
|
||||
}
|
||||
for(let n=0;n<removeName.length;n++) {
|
||||
elements[i].attributes.removeNamedItem(removeName[n])
|
||||
}
|
||||
|
||||
if (isEvent) {
|
||||
let ventType = 'input'
|
||||
if (elements[i].tagName != 'INPUT' || elements[i].type == 'checkbox') {
|
||||
ventType = 'change'
|
||||
}
|
||||
elements[i].addEventListener(ventType, (e) => {
|
||||
for (let t = 0; t < Event.length; t++) {
|
||||
Event[t](e)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const EventBinding = new eventBinding();
|
||||
export default EventBinding;
|
||||
373
src/Obj/Base/BaseSource/BaseTileset/Tileset/index.js
Normal file
373
src/Obj/Base/BaseSource/BaseTileset/Tileset/index.js
Normal file
@ -0,0 +1,373 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-11-20 16:05
|
||||
* @description:index
|
||||
* @update: 2023-11-20 16:05
|
||||
*/
|
||||
import BaseTileset from "../index";
|
||||
import cy_slider from "../../../../Element/cy_html_slider";
|
||||
import { html, css } from "./_element";
|
||||
import EventBinding from '../../../../Element/Dialog/eventBinding';
|
||||
import { syncSplitData } from '../../../../../Global/SplitScreen'
|
||||
import Dialog from '../../../../Element/Dialog';
|
||||
|
||||
class Tileset extends BaseTileset {
|
||||
#updateModelTimeout;
|
||||
/**
|
||||
* @constructor
|
||||
* @description 加载Tileset模型
|
||||
* @param sdk {object} sdk
|
||||
* @param options {object} 模型参数
|
||||
* @param options.id {string} 对象id
|
||||
* @param options.show=true {boolean} 模型显隐
|
||||
* @param options.name {string} 名称
|
||||
* @param options.url {string} 资源地址
|
||||
* @param options.accuracy=1 {number} 精度(倍)
|
||||
* @param options.position {object} 模型位置
|
||||
* @param options.position.lng {number} 经度
|
||||
* @param options.position.lat {number} 纬度
|
||||
* @param options.position.alt {number} 高度
|
||||
* */
|
||||
constructor(earth, options = {}, _Dialog = {}) {
|
||||
super(earth, options)
|
||||
this.picking = false
|
||||
this.Dialog = _Dialog
|
||||
this._elms = {};
|
||||
this._EventBinding = new EventBinding()
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "tileset"
|
||||
}
|
||||
|
||||
on() {
|
||||
return this.add()
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.newData.name
|
||||
}
|
||||
|
||||
set name(v) {
|
||||
this.newData.name = v
|
||||
this._elms.name && this._elms.name.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this.newData.height
|
||||
}
|
||||
|
||||
set height(v) {
|
||||
this.newData.height = v
|
||||
this.options.position.alt = v
|
||||
this._elms.height && this._elms.height.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch)
|
||||
}
|
||||
|
||||
get accuracy() {
|
||||
return this.newData.accuracy
|
||||
}
|
||||
|
||||
set accuracy(v) {
|
||||
this.newData.accuracy = Number(v.toFixed(1))
|
||||
if(this.newData.accuracy<0.1) {
|
||||
this.newData.accuracy = 0.1
|
||||
}
|
||||
if(this.entity) {
|
||||
this.entity.maximumScreenSpaceError = 32/this.newData.accuracy
|
||||
}
|
||||
this._elms.accuracy && this._elms.accuracy.forEach((item) => {
|
||||
item.value = this.newData.accuracy
|
||||
})
|
||||
}
|
||||
|
||||
get lng() {
|
||||
return this.newData.lng
|
||||
}
|
||||
set lng(v) {
|
||||
this.newData.lng = v
|
||||
this.options.position.lng = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch)
|
||||
}
|
||||
|
||||
get lat() {
|
||||
return this.newData.lat
|
||||
}
|
||||
set lat(v) {
|
||||
this.newData.lat = v
|
||||
this.options.position.lat = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch)
|
||||
}
|
||||
|
||||
get roll() {
|
||||
return this.newData.roll
|
||||
}
|
||||
|
||||
set roll(v) {
|
||||
this.newData.roll = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch, this.newData.scale)
|
||||
}
|
||||
|
||||
get heading() {
|
||||
return this.newData.heading
|
||||
}
|
||||
|
||||
set heading(v) {
|
||||
this.newData.heading = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch, this.newData.scale)
|
||||
}
|
||||
|
||||
get pitch() {
|
||||
return this.newData.pitch
|
||||
}
|
||||
|
||||
set pitch(v) {
|
||||
this.newData.pitch = v
|
||||
this.updateModel(this.newData.lng, this.newData.lat, this.newData.height, this.newData.roll, this.newData.heading, this.newData.pitch, this.newData.scale)
|
||||
}
|
||||
|
||||
get transparency() {
|
||||
return this.newData.transparency
|
||||
}
|
||||
|
||||
set transparency(v) {
|
||||
if (!this.newData) {
|
||||
return
|
||||
}
|
||||
this.newData.transparency = Number(v)
|
||||
this._elms.transparency && this._elms.transparency.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
let transparency = this.newData.transparency
|
||||
// if (transparency == 1) {
|
||||
// transparency = 0.99
|
||||
// }
|
||||
this.entity.style = new Cesium.Cesium3DTileStyle({
|
||||
color: {
|
||||
"conditions": [
|
||||
//有size属性表示为点云,需要与点颜色融合
|
||||
["Boolean(${SIZE})", "${COLOR} * color('rgba(255,255,255)', " + transparency + ")"],
|
||||
["true", "color('rgba(255,255,255," + transparency + ")')"]
|
||||
]
|
||||
},
|
||||
show: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 编辑框
|
||||
* @param state=false {boolean} 状态: true打开, false关闭
|
||||
*/
|
||||
async edit(state = false) {
|
||||
let _this = this
|
||||
this._element_style = null
|
||||
|
||||
// let elms = this.sdk.viewer._container.getElementsByClassName('YJ-custom-base-dialog')
|
||||
// for (let i = elms.length - 1; i >= 0; i--) {
|
||||
// this.sdk.viewer._container.removeChild(elms[i])
|
||||
// }
|
||||
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
|
||||
if (state) {
|
||||
this._element_style = document.createElement('style');
|
||||
this._element_style.type = 'text/css';
|
||||
this._element_style.setAttribute('data-name', 'YJ_style_dialog');
|
||||
this._element_style.innerHTML = css();
|
||||
|
||||
this._DialogObject = await new Dialog(this.sdk, this.newData, {
|
||||
title: '倾斜模型属性', left: '180px', top: '100px',
|
||||
confirmCallBack: (options) => {
|
||||
this.oldData.name = this.newData.name
|
||||
this.oldData.height = this.newData.height
|
||||
this.oldData.lng = this.newData.lng
|
||||
this.oldData.lat = this.newData.lat
|
||||
this.oldData.transparency = this.newData.transparency
|
||||
this.oldData.scale = this.newData.scale
|
||||
this.oldData.roll = this.newData.roll
|
||||
this.oldData.heading = this.newData.heading
|
||||
this.oldData.pitch = this.newData.pitch
|
||||
this.oldData.type = this.type
|
||||
this.oldData.accuracy = this.newData.accuracy
|
||||
this._DialogObject.close()
|
||||
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack({ ...this.oldData, type: this.type })
|
||||
syncSplitData(this.sdk, this.oldData.id)
|
||||
},
|
||||
resetCallBack: () => {
|
||||
this.reset()
|
||||
this.Dialog.resetCallBack && this.Dialog.resetCallBack()
|
||||
},
|
||||
removeCallBack: () => {
|
||||
this.Dialog.removeCallBack && this.Dialog.removeCallBack()
|
||||
},
|
||||
closeCallBack: () => {
|
||||
this.reset()
|
||||
if (this.positionEditing) {
|
||||
this.positionEditing = false
|
||||
}
|
||||
if (this.rotationEditing) {
|
||||
this.rotationEditing = false
|
||||
}
|
||||
this.Dialog.closeCallBack && this.Dialog.closeCallBack()
|
||||
},
|
||||
showCallBack: (show) => {
|
||||
this.newData.show = show
|
||||
this.entity && (this.entity.show = show)
|
||||
this.Dialog.showCallBack && this.Dialog.showCallBack()
|
||||
},
|
||||
rotateCallBack: () => {
|
||||
if (this.rotationEditing) {
|
||||
this.rotationEditing = false
|
||||
}
|
||||
else {
|
||||
this.rotationEditing = true
|
||||
}
|
||||
},
|
||||
translationalCallBack: () => {
|
||||
if (this.positionEditing) {
|
||||
this.positionEditing = false
|
||||
}
|
||||
else {
|
||||
this.positionEditing = true
|
||||
}
|
||||
}
|
||||
}, true)
|
||||
document.getElementsByTagName('head')[0].appendChild(this._element_style);
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
this._DialogObject.contentAppChild(contentElm)
|
||||
|
||||
let all_elm = contentElm.getElementsByTagName("*")
|
||||
this._EventBinding.on(this, all_elm)
|
||||
this._elms = this._EventBinding.element
|
||||
} else {
|
||||
// if (this._element_style) {
|
||||
// document.getElementsByTagName('head')[0].removeChild(this._element_style)
|
||||
// this._element_style = null
|
||||
// }
|
||||
// if (this._DialogObject && this._DialogObject.remove) {
|
||||
// this._DialogObject.remove()
|
||||
// this._DialogObject = null
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
//更新模型位置
|
||||
updateModel(_tx, _ty, _tz, _rx = 0, _ry = 0, _rz = 0, s = 1) {
|
||||
if (!this.tileset.root.transform) {
|
||||
if (window.ELEMENT) {
|
||||
window.ELEMENT.Message.closeAll();
|
||||
window.ELEMENT.Message({
|
||||
message: '该模型不支持移动和旋转!',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
}
|
||||
console.warn('该模型不支持移动和旋转!')
|
||||
return
|
||||
}
|
||||
if ((!_tx && _tx!==0) || (!_ty && _ty!==0) || (!_tz && _tz!==0)) {
|
||||
return
|
||||
}
|
||||
let mx = Cesium.Matrix3.fromRotationX(
|
||||
Cesium.Math.toRadians(_rx)
|
||||
)
|
||||
let my = Cesium.Matrix3.fromRotationY(
|
||||
Cesium.Math.toRadians(_ry)
|
||||
)
|
||||
let mz = Cesium.Matrix3.fromRotationZ(
|
||||
Cesium.Math.toRadians(_rz)
|
||||
)
|
||||
// 平移
|
||||
let m = Cesium.Transforms.eastNorthUpToFixedFrame(new Cesium.Cartesian3.fromDegrees(_tx, _ty, _tz))
|
||||
// 旋转
|
||||
let rotationX = Cesium.Matrix4.fromRotationTranslation(mx)
|
||||
let rotationY = Cesium.Matrix4.fromRotationTranslation(my)
|
||||
let rotationZ = Cesium.Matrix4.fromRotationTranslation(mz)
|
||||
let originalMatrix = new Cesium.Matrix4()
|
||||
Cesium.Matrix4.multiply(m, rotationX, originalMatrix)
|
||||
Cesium.Matrix4.multiply(originalMatrix, rotationY, originalMatrix)
|
||||
Cesium.Matrix4.multiply(originalMatrix, rotationZ, originalMatrix)
|
||||
const scale = Cesium.Matrix4.fromUniformScale(s);
|
||||
Cesium.Matrix4.multiply(originalMatrix, scale, this.entity._root.transform)
|
||||
// console.log(_tx, _ty, _tz)
|
||||
if (!this.editObj.activeAxis) {
|
||||
this.editObj.position = { lng: _tx, lat: _ty, alt: _tz }
|
||||
}
|
||||
if (!this.editObj.activeCircle) {
|
||||
this.editObj.rotate = { x: _rx, y: _ry, z: _rz }
|
||||
}
|
||||
this.editObj && this.editObj.update()
|
||||
|
||||
clearTimeout(this.#updateModelTimeout)
|
||||
this.#updateModelTimeout = setTimeout(() => {
|
||||
clearTimeout(this.#updateModelTimeout)
|
||||
let center = this.cartesian3Towgs84(this.entity.boundingSphere.center, this.sdk.viewer)
|
||||
let circle = turf.circle([center.lng, center.lat], this.entity.boundingSphere.radius / 1000, { steps: 360, units: 'kilometers' });
|
||||
for (let [key, entity] of this.sdk.entityMap) {
|
||||
if (entity.type === 'BillboardObject' && entity.heightMode == 3) {
|
||||
let pt = turf.point([entity.lng, entity.lat]);
|
||||
if (turf.booleanPointInPolygon(pt, circle)) {
|
||||
entity.updateHeight()
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (entity.label) {
|
||||
entity.label.show = entity.label.show
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// flyTo() {
|
||||
// this.entity.readyPromise.then(() => {
|
||||
// console.log(this.entity)
|
||||
// let boundingSphere
|
||||
// if(!this.lng || !this.lat) {
|
||||
// boundingSphere = new Cesium.BoundingSphere(this.entity.boundingSphere)
|
||||
// }
|
||||
// else {
|
||||
// boundingSphere = new Cesium.BoundingSphere(Cesium.Cartesian3.fromDegrees(this.lng, this.lat, this.height), this.entity.boundingSphere.radius)
|
||||
// }
|
||||
// this.sdk.viewer.camera.flyToBoundingSphere(boundingSphere)
|
||||
// })
|
||||
// }
|
||||
|
||||
reset() {
|
||||
this.editObj.destroy()
|
||||
if (!this.entity) {
|
||||
return
|
||||
}
|
||||
this.transparency = this.oldData.transparency
|
||||
this.name = this.oldData.name
|
||||
this.height = this.oldData.height
|
||||
this.lng = this.oldData.lng
|
||||
this.lat = this.oldData.lat
|
||||
this.roll = this.oldData.roll
|
||||
this.heading = this.oldData.heading
|
||||
this.pitch = this.oldData.pitch
|
||||
this.scale = this.oldData.scale
|
||||
this.accuracy = this.oldData.accuracy
|
||||
|
||||
this.entity.style = new Cesium.Cesium3DTileStyle({
|
||||
color: {
|
||||
"conditions": [
|
||||
["Boolean(${SIZE})", "${COLOR} * color('rgba(255,255,255)', " + this.transparency + ")"],
|
||||
["true", "color('rgba(255,255,255," + this.transparency + ")')"]
|
||||
]
|
||||
},
|
||||
show: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Tileset
|
||||
650
src/Obj/Base/BaseSource/BaseTileset/index.js
Normal file
650
src/Obj/Base/BaseSource/BaseTileset/index.js
Normal file
@ -0,0 +1,650 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-11-20 17:54
|
||||
* @description:index
|
||||
* @update: 2023-11-20 17:54
|
||||
*/
|
||||
import { getHost } from "../../../../on";
|
||||
import BaseSource from "../index";
|
||||
import { regLeftClickCallback, regRightClickCallback, regMoveCallback } from "../../../../Global/ClickCallback";
|
||||
import Controller from "../../../../Controller/index";
|
||||
import { syncData } from '../../../../Global/MultiViewportMode'
|
||||
import { setSplitDirection, syncSplitData, setActiveId } from '../../../../Global/SplitScreen'
|
||||
|
||||
class BaseTileset extends BaseSource {
|
||||
#updateModelTimeout;
|
||||
/**
|
||||
* @constructor
|
||||
* @param sdk
|
||||
* @description 模型
|
||||
* @param options {object}
|
||||
* @param options.id{string} id
|
||||
* @param options.name{string} 名称
|
||||
* @param options.url{string} 模型地址
|
||||
* @param options.lng{number} 经度
|
||||
* @param options.lat{number} 纬度
|
||||
* @param options.height=0{number} 高度
|
||||
* @param options.scale=1{number} 模型比例
|
||||
* @param options.roll=0{number} 模型x旋转
|
||||
* @param options.heading=0{number} 模型z轴旋转角度
|
||||
* @param options.pitch=0{number} 模型y轴旋转角度
|
||||
* */
|
||||
constructor(sdk, options) {
|
||||
super(sdk, options);
|
||||
this.setDefaultValue()
|
||||
this.watchs = []
|
||||
this.positionCallBack = null
|
||||
this.rotationCallback = null
|
||||
this.onClickCallback = null
|
||||
this._DialogObject = null
|
||||
this._element_style = null
|
||||
this.options.accuracy = options.accuracy ? Number(options.accuracy.toFixed(1)) : 1
|
||||
this.options.position = this.options.position || {}
|
||||
this.oldData = {
|
||||
id: this.options.id,
|
||||
transparency: (this.options.transparency || this.options.transparency === 0) ? this.options.transparency : 1,
|
||||
name: this.options.name,
|
||||
accuracy: this.options.accuracy,
|
||||
url: this.options.url,
|
||||
height: this.options.position.alt || 0,
|
||||
lng: this.options.position.lng,
|
||||
lat: this.options.position.lat,
|
||||
scale: (this.options.scale || this.options.scale === 0) ? this.options.scale : 1,
|
||||
roll: this.options.roll || 0,
|
||||
heading: this.options.heading || 0,
|
||||
pitch: this.options.pitch || 0
|
||||
}
|
||||
this.newData = {
|
||||
id: this.options.id,
|
||||
transparency: (this.options.transparency || this.options.transparency === 0) ? this.options.transparency : 1,
|
||||
name: this.options.name,
|
||||
accuracy: this.options.accuracy,
|
||||
url: this.options.url,
|
||||
height: this.options.position.alt || 0,
|
||||
lng: this.options.position.lng,
|
||||
lat: this.options.position.lat,
|
||||
scale: (this.options.scale || this.options.scale === 0) ? this.options.scale : 1,
|
||||
roll: this.options.roll || 0,
|
||||
heading: this.options.heading || 0,
|
||||
pitch: this.options.pitch || 0
|
||||
}
|
||||
this.tileset = undefined
|
||||
this.editObj = new Controller(this.sdk)
|
||||
this.editObj.controllerCallBack = this.rotationEditingCallBack
|
||||
}
|
||||
|
||||
async add() {
|
||||
if (this.options.url) {
|
||||
return this.loadTileset({
|
||||
url: this.options.url
|
||||
})
|
||||
} else {
|
||||
let res = await this.requestResource()
|
||||
let text = await res.text()
|
||||
text = JSON.parse(text)
|
||||
if ([0, 200].includes(text.code)) {
|
||||
if (text.data.url.length)
|
||||
return this.loadTileset(text.data)
|
||||
else
|
||||
return new Promise((res, reject) => {
|
||||
reject('资源不存在')
|
||||
})
|
||||
} else {
|
||||
return new Promise((res, reject) => {
|
||||
reject(text.msg || text.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
loadSceneTree() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
async loadTileset(options) {
|
||||
let object = { ...options }
|
||||
let url = ""
|
||||
if (object.url.startsWith("http"))
|
||||
url = object.url
|
||||
else {
|
||||
//说明是本地的json,在磁盘中存在的
|
||||
if (object.url.includes(":")) {
|
||||
url = object.url
|
||||
} else {
|
||||
if (this.options.host) {
|
||||
let o = new URL(object.url, this.options.host)
|
||||
url = o.href
|
||||
} else
|
||||
url = object.url
|
||||
}
|
||||
}
|
||||
|
||||
let response = await fetch(url, {
|
||||
method: 'get',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
if (response.status === 200) {
|
||||
this.tileset = await response.json()
|
||||
}
|
||||
let params = {
|
||||
show: this.options.show,
|
||||
skipLevelOfDetail: true,
|
||||
baseScreenSpaceError: 1024,
|
||||
maximumScreenSpaceError: 32, // 数值加大,能让最终成像变模糊
|
||||
skipScreenSpaceErrorFactor: 16,
|
||||
skipLevels: 1,
|
||||
immediatelyLoadDesiredLevelOfDetail: false,
|
||||
loadSiblings: true, // 如果为true则不会在已加载完概况房屋后,自动从中心开始超清化房屋
|
||||
cullWithChildrenBounds: true,
|
||||
cullRequestsWhileMoving: true,
|
||||
cullRequestsWhileMovingMultiplier: 10, // 值越小能够更快的剔除
|
||||
preloadWhenHidden: false,
|
||||
preferLeaves: true,
|
||||
maximumCacheOverflowBytes: 128, // 内存分配变小有利于倾斜摄影数据回收,提升性能体验
|
||||
progressiveResolutionHeightFraction: 0.5, // 数值偏于0能够让初始加载变得模糊
|
||||
dynamicScreenSpaceErrorDensity: 0.1, // 数值加大,能让周边加载变快
|
||||
dynamicScreenSpaceErrorFactor: 1,
|
||||
dynamicScreenSpaceError: true // 有了这个后,会在真正的全屏加载完之后才清晰化房屋
|
||||
}
|
||||
let tileset
|
||||
if (Number(Cesium.VERSION.split('.')[1]) >= 107) {
|
||||
tileset = await Cesium.Cesium3DTileset.fromUrl(url, params);
|
||||
this.entity = tileset
|
||||
this.entity.imageBasedLighting.luminanceAtZenith = 0.1
|
||||
}
|
||||
else {
|
||||
params.url = url
|
||||
tileset = new Cesium.Cesium3DTileset(params);
|
||||
this.entity = await tileset.readyPromise
|
||||
this.entity.imageBasedLighting.luminanceAtZenith = 0.1
|
||||
}
|
||||
|
||||
// syncData(this.sdk, this.options.id)
|
||||
|
||||
|
||||
await this.loadSceneTree(url)
|
||||
const initData = (tile) => {
|
||||
if (tile._contents) {
|
||||
for (let i = 0; i < tile._contents.length; i++) {
|
||||
initData(tile._contents[i])
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < tile.featuresLength; i++) {
|
||||
let feature = tile.getFeature(i)
|
||||
let file = feature.content.url
|
||||
let id = feature.getProperty('id')
|
||||
if (this.features.has(id)) {
|
||||
if (this.features.get(id).features) {
|
||||
if (this.features.get(id).features[file]) {
|
||||
// feature = this.features.get(id).features[feature.featureId]
|
||||
if (this.features.get(id).features[file].customColor) {
|
||||
feature.color = this.features.get(id).features[file].customColor
|
||||
feature.customColor = this.features.get(id).features[file].customColor
|
||||
}
|
||||
if (this.features.get(id).features[file].customAlpha) {
|
||||
let color = feature.color
|
||||
feature.color = Cesium.Color.fromCssColorString(`rgba(${Cesium.Color.floatToByte(color.red)},${Cesium.Color.floatToByte(color.green)},${Cesium.Color.floatToByte(color.blue)},${this.features.get(id).features[file].customAlpha})`)
|
||||
feature.customAlpha = this.features.get(id).features[file].customAlpha
|
||||
}
|
||||
if (this.features.get(id).features[file].customShow) {
|
||||
feature.show = this.features.get(id).features[file].customShow
|
||||
feature.customShow = this.features.get(id).features[file].customShow
|
||||
}
|
||||
}
|
||||
this.features.get(id).features[file] = feature
|
||||
}
|
||||
else {
|
||||
let object = {}
|
||||
if (this.features.get(id).customColor) {
|
||||
feature.color = this.features.get(id).customColor
|
||||
feature.customColor = this.features.get(id).customColor
|
||||
}
|
||||
if (this.features.get(id).customAlpha) {
|
||||
let color = feature.color
|
||||
feature.color = Cesium.Color.fromCssColorString(`rgba(${Cesium.Color.floatToByte(color.red)},${Cesium.Color.floatToByte(color.green)},${Cesium.Color.floatToByte(color.blue)},${this.features.get(id).customAlpha})`)
|
||||
feature.customAlpha = this.features.get(id).customAlpha
|
||||
}
|
||||
if (this.features.get(id).customShow) {
|
||||
feature.show = this.features.get(id).customShow
|
||||
feature.customShow = this.features.get(id).customShow
|
||||
}
|
||||
object[file] = feature
|
||||
this.features.get(id).features = object
|
||||
}
|
||||
}
|
||||
else {
|
||||
let object = {}
|
||||
object[file] = feature
|
||||
this.features.set(id, { features: object })
|
||||
}
|
||||
if (!feature.customColor) {
|
||||
feature.customColor = Cesium.Color.fromCssColorString('#ffffff')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// for (let i = 0; i < tile._content.featuresLength; i++) {
|
||||
// let feature = tile._content.getFeature(i)
|
||||
// feature.show = false
|
||||
// }
|
||||
// if (tile._content._contents) {
|
||||
// for (let i = 0; i < tile._content._contents.length; i++) {
|
||||
// for (let m = 0; m < tile._content._contents[i].featuresLength; m++) {
|
||||
// let feature = tile._content._contents[i].getFeature(m)
|
||||
// feature.show = false
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
if (!this.sdk || !this.sdk.viewer || !this.sdk.viewer.scene) {
|
||||
return
|
||||
}
|
||||
tileset.tileLoad.addEventListener(tile => {
|
||||
// this.test()
|
||||
initData(tile._content)
|
||||
|
||||
clearTimeout(this.#updateModelTimeout)
|
||||
this.#updateModelTimeout = setTimeout(() => {
|
||||
clearTimeout(this.#updateModelTimeout)
|
||||
let center = this.cartesian3Towgs84(tileset.boundingSphere.center, this.sdk.viewer)
|
||||
let circle = turf.circle([center.lng, center.lat], tileset.boundingSphere.radius / 1000, { steps: 360, units: 'kilometers' });
|
||||
for (let [key, entity] of this.sdk.entityMap) {
|
||||
if (entity.type === 'BillboardObject' && entity.heightMode == 3) {
|
||||
let pt = turf.point([entity.lng, entity.lat]);
|
||||
if (turf.booleanPointInPolygon(pt, circle)) {
|
||||
entity.updateHeight()
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (entity.label) {
|
||||
entity.label.show = entity.label.show
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// if (tile._content._contents) {
|
||||
// for (let i = 0; i < tile._content._contents.length; i++) {
|
||||
// for (let m = 0; m < tile._content._contents[i].featuresLength; m++) {
|
||||
// let feature = tile._content._contents[i].getFeature(m)
|
||||
// console.log(feature)
|
||||
// feature.show = false
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// for (let i = 0; i < tile._content.featuresLength; i++) {
|
||||
// let feature = tile._content.getFeature(i)
|
||||
// let file = feature.content.url
|
||||
// let id = feature.getProperty('id')
|
||||
// if (this.features.has(id)) {
|
||||
// if (this.features.get(id).features) {
|
||||
// if (this.features.get(id).features[file]) {
|
||||
// // feature = this.features.get(id).features[feature.featureId]
|
||||
// if (this.features.get(id).features[file].customColor) {
|
||||
// feature.color = this.features.get(id).features[file].customColor
|
||||
// feature.customColor = this.features.get(id).features[file].customColor
|
||||
// }
|
||||
// if (this.features.get(id).features[file].customAlpha) {
|
||||
// let color = feature.color
|
||||
// feature.color = Cesium.Color.fromCssColorString(`rgba(${Cesium.Color.floatToByte(color.red)},${Cesium.Color.floatToByte(color.green)},${Cesium.Color.floatToByte(color.blue)},${this.features.get(id).features[file].customAlpha})`)
|
||||
// feature.customAlpha = this.features.get(id).features[file].customAlpha
|
||||
// }
|
||||
// if (this.features.get(id).features[file].customShow) {
|
||||
// feature.show = this.features.get(id).features[file].customShow
|
||||
// feature.customShow = this.features.get(id).features[file].customShow
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
// this.features.get(id).features[file] = feature
|
||||
// }
|
||||
// else {
|
||||
// let object = {}
|
||||
// object[file] = feature
|
||||
// this.features.get(id).features = object
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// let object = {}
|
||||
// object[file] = feature
|
||||
// this.features.set(id, { features: object })
|
||||
// }
|
||||
// if (!feature.customColor) {
|
||||
// feature.customColor = Cesium.Color.fromCssColorString('#ffffff')
|
||||
// }
|
||||
// }
|
||||
})
|
||||
// // console.log(tileset)
|
||||
// if (this.type === 'bim') {
|
||||
// const setTilesetStyle = (f) => {
|
||||
// if (tileset.style) {
|
||||
// // tileset.style = new Cesium.Cesium3DTileStyle({
|
||||
// // color: {
|
||||
// // conditions: [
|
||||
// // ['${name} ==="对象074" ', 'color("red")'], //符合条件项
|
||||
// // ['true', 'rgba(255,255,255,1)'] //其他项
|
||||
// // ]
|
||||
// // }
|
||||
// // })
|
||||
// // tileset.tileLoad.removeEventListener(setTilesetStyle)
|
||||
// }
|
||||
// console.log(f)
|
||||
// }
|
||||
// tileset.tileLoad.addEventListener(setTilesetStyle)
|
||||
// }
|
||||
|
||||
this.entity._root.originalTransform = { ...this.entity._root.transform }
|
||||
this.entity.id = this.options.id || this.randomString()
|
||||
this.entity.type = this.type
|
||||
// this.editObj = new EditB3DM(this.sdk, this.entity)
|
||||
|
||||
this.sdk.viewer.scene.primitives.add(tileset);
|
||||
if (this.options.position && JSON.stringify(this.options.position) != "{}"
|
||||
&& (this.options.position.lng || this.options.position.lng === 0) && (this.options.position.lat || this.options.position.lat === 0)) {
|
||||
this.options.position.alt == this.options.position.alt || 0
|
||||
let cartographic = Cesium.Cartographic.fromCartesian(this.entity.boundingSphere.center);
|
||||
if (this.tileset.root.transform) {
|
||||
cartographic = Cesium.Cartographic.fromCartesian({ x: this.tileset.root.transform[12], y: this.tileset.root.transform[13], z: this.tileset.root.transform[14] })
|
||||
}
|
||||
this.entity.original = {
|
||||
lng: Cesium.Math.toDegrees(cartographic.longitude), // 经度
|
||||
lat: Cesium.Math.toDegrees(cartographic.latitude), // 纬度
|
||||
height: cartographic.height
|
||||
}
|
||||
let m = Cesium.Transforms.eastNorthUpToFixedFrame(new Cesium.Cartesian3.fromDegrees(this.options.position.lng, this.options.position.lat, this.options.position.alt))
|
||||
const scale = Cesium.Matrix4.fromUniformScale(this.oldData.scale);
|
||||
if (this.tileset.root.transform) {
|
||||
Cesium.Matrix4.multiply(m, scale, this.entity._root.transform)
|
||||
}
|
||||
this.lng = this.oldData.lng
|
||||
this.lat = this.oldData.lat
|
||||
this.height = this.oldData.height
|
||||
}
|
||||
else {
|
||||
this.options.position = {}
|
||||
let cartographic = Cesium.Cartographic.fromCartesian(this.entity.boundingSphere.center);
|
||||
if (this.tileset.root.transform) {
|
||||
cartographic = Cesium.Cartographic.fromCartesian({ x: this.tileset.root.transform[12], y: this.tileset.root.transform[13], z: this.tileset.root.transform[14] })
|
||||
}
|
||||
this.entity.original = {
|
||||
lng: Cesium.Math.toDegrees(cartographic.longitude),
|
||||
lat: this.oldData.lat = Cesium.Math.toDegrees(cartographic.latitude),
|
||||
height: cartographic.height,
|
||||
}
|
||||
this.lng = this.oldData.lng = Cesium.Math.toDegrees(cartographic.longitude); // 经度
|
||||
this.lat = this.oldData.lat = Cesium.Math.toDegrees(cartographic.latitude); // 纬度
|
||||
this.height = this.oldData.height = cartographic.height; // 高度
|
||||
|
||||
}
|
||||
|
||||
this.scale = this.oldData.scale
|
||||
this.roll = this.oldData.roll
|
||||
this.heading = this.oldData.heading
|
||||
this.pitch = this.oldData.pitch
|
||||
this.transparency = this.oldData.transparency
|
||||
|
||||
syncSplitData(this.sdk, this.options.id)
|
||||
|
||||
regMoveCallback(this.entity.id, this.mouseMoveCB, this)
|
||||
|
||||
|
||||
// this.entity = this.sdk.viewer.scene.primitives.add(tileset);
|
||||
// if (this.options.position && JSON.stringify(this.options.position) != "{}") {
|
||||
// let m = Cesium.Transforms.eastNorthUpToFixedFrame(new Cesium.Cartesian3.fromDegrees(this.options.position.lng, this.options.position.lat, this.options.position.alt))
|
||||
// const scale = Cesium.Matrix4.fromUniformScale(this.oldData.scale);
|
||||
// Cesium.Matrix4.multiply(m, scale, this.entity._root.transform)
|
||||
// }
|
||||
// else {
|
||||
// this.options.position = {}
|
||||
// }
|
||||
// this.lng = this.oldData.lng
|
||||
// this.lat = this.oldData.lat
|
||||
// this.height = this.oldData.height
|
||||
// this.scale = this.oldData.scale
|
||||
// this.roll = this.oldData.roll
|
||||
// this.heading = this.oldData.heading
|
||||
// this.pitch = this.oldData.pitch
|
||||
// this.transparency = this.oldData.transparency
|
||||
|
||||
// regMoveCallback(this.entity.id, this.mouseMoveCB, this)
|
||||
|
||||
// this.editObj = new EditB3DM(this.sdk, this.entity)
|
||||
// this.editObj.transformCallBack = this.rotationEditingCallBack
|
||||
// tileset.readyPromise.then(() => {
|
||||
// this.entity = this.sdk.viewer.scene.primitives.add(tileset);
|
||||
|
||||
// })
|
||||
// let x = this.sdk.viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
|
||||
// url: url
|
||||
// }));
|
||||
// setTimeout(() => {
|
||||
// console.log(x)
|
||||
// this.sdk.viewer.flyTo(this.entity)
|
||||
// }, 3000);
|
||||
}
|
||||
|
||||
// test() {
|
||||
// let heightstyle = new Cesium.Cesium3DTileStyle({
|
||||
// color: {
|
||||
// conditions: [
|
||||
// ["Number(${height})>=300", "rgba(45,0,75,0.5)"],
|
||||
// ["Number(${height})>=200", "rgb(102,71,151)"],
|
||||
// ["Number(${height})>=100", "rgb(170,162,204)"],
|
||||
// ["Number(${height})>=50", "rgb(224,226,238)"],
|
||||
// ["Number(${height})>=25", "rgb(252,230, 200)"],
|
||||
// ["Number(${height})>=10", "rgb(248,176,87)"],
|
||||
// ["Number(${height})>=5", "rgb(198, 106,11)"],
|
||||
// ["isNaN(Number(${height}))", "rgb(255, 255, 255)"],
|
||||
// ["true", "rgb(127,59,8)"]
|
||||
// ]
|
||||
// }
|
||||
// });
|
||||
// this.entity.style = heightstyle;
|
||||
// }
|
||||
|
||||
|
||||
remove() {
|
||||
super.remove()
|
||||
this.editObj.destroy()
|
||||
this.sdk.viewer.scene.primitives.remove(this.entity);
|
||||
this.entity = null
|
||||
if (this._DialogObject) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
}
|
||||
|
||||
flyTo() {
|
||||
super.flyTo()
|
||||
}
|
||||
|
||||
|
||||
on() {
|
||||
return this.add()
|
||||
}
|
||||
|
||||
setDefaultValue() {
|
||||
super.setDefaultValue()
|
||||
this.options.host = this.options.host || getHost()
|
||||
this.options.url = this.options.url || ""
|
||||
}
|
||||
|
||||
get position() {
|
||||
let cartographic = Cesium.Cartographic.fromCartesian(this.entity.boundingSphere.center);
|
||||
if (this.tileset.root.transform) {
|
||||
cartographic = Cesium.Cartographic.fromCartesian({ x: this.tileset.root.transform[12], y: this.tileset.root.transform[13], z: this.tileset.root.transform[14] })
|
||||
}
|
||||
let lng = Cesium.Math.toDegrees(cartographic.longitude + 0.00000000663814);
|
||||
let lat = Cesium.Math.toDegrees(cartographic.latitude + 0.00000025137835);
|
||||
if (this.newData.lng && this.newData.lat && this.newData.height) {
|
||||
return { lng: this.newData.lng, lat: this.newData.lat, height: this.newData.height }
|
||||
}
|
||||
else {
|
||||
return { lng: lng, lat: lat, height: cartographic.height - 2.19104611043234 }
|
||||
}
|
||||
}
|
||||
|
||||
set position(p) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 打开模型旋转功能
|
||||
* @param status {boolean}
|
||||
* @methodOf Source
|
||||
* */
|
||||
set rotationEditing(status) {
|
||||
if (!this.tileset.root.transform) {
|
||||
if (window.ELEMENT) {
|
||||
window.ELEMENT.Message.closeAll();
|
||||
window.ELEMENT.Message({
|
||||
message: '该模型不支持移动和旋转!',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
}
|
||||
console.warn('该模型不支持移动和旋转!')
|
||||
return
|
||||
}
|
||||
if (status) {
|
||||
this.editObj.position = { lng: this.newData.lng, lat: this.newData.lat, alt: this.newData.height }
|
||||
this.editObj.update()
|
||||
this.editObj.editRtation()
|
||||
}
|
||||
else {
|
||||
this.editObj.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 获取模型旋转状态
|
||||
* @method rotationEditing
|
||||
* @return boolean
|
||||
* @methodOf Source
|
||||
|
||||
* */
|
||||
get rotationEditing() {
|
||||
if (this.editObj.getActiveState() === 'rtation') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**@desc 打开平移模型功能
|
||||
*
|
||||
* @memberOf Source
|
||||
*@param status {boolean}
|
||||
*
|
||||
* */
|
||||
set positionEditing(status) {
|
||||
if (!this.sdk || !this.sdk.viewer || !this.entity) {
|
||||
return
|
||||
}
|
||||
if (!this.tileset.root.transform) {
|
||||
if (window.ELEMENT) {
|
||||
window.ELEMENT.Message.closeAll();
|
||||
window.ELEMENT.Message({
|
||||
message: '该模型不支持移动和旋转!',
|
||||
type: 'warning',
|
||||
duration: 1500
|
||||
});
|
||||
}
|
||||
console.warn('该模型不支持移动和旋转!')
|
||||
return
|
||||
}
|
||||
if (status) {
|
||||
this.editObj.position = { lng: this.newData.lng, lat: this.newData.lat, alt: this.newData.height }
|
||||
this.editObj.update()
|
||||
this.editObj.editTranslational()
|
||||
}
|
||||
else {
|
||||
this.editObj.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
get positionEditing() {
|
||||
if (this.editObj.getActiveState() === 'translational') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//平移时,坐标信息变化的回调
|
||||
set positionEditingCallBack(callback) {
|
||||
return
|
||||
}
|
||||
|
||||
get positionEditingCallBack() {
|
||||
}
|
||||
|
||||
//旋转时,坐标信息变化的回调
|
||||
set rotationEditingCallBack(callback) {
|
||||
this._rotationEditingCallBack = callback
|
||||
}
|
||||
|
||||
get rotationEditingCallBack() {
|
||||
return (params, state) => {
|
||||
this.lng = params.position.lng
|
||||
this.lat = params.position.lat
|
||||
this.height = params.position.alt
|
||||
this.roll = params.rotate.x
|
||||
this.heading = params.rotate.y
|
||||
this.pitch = params.rotate.z
|
||||
// this._rotationEditingCallBack && this._rotationEditingCallBack(this.editObj._params)
|
||||
}
|
||||
}
|
||||
|
||||
flicker() { }
|
||||
|
||||
// 编辑框
|
||||
async edit(state) { }
|
||||
|
||||
get show() {
|
||||
return this.options.show
|
||||
}
|
||||
|
||||
set show(v) {
|
||||
if (typeof v === "boolean") {
|
||||
this.options.show = v
|
||||
this.entity && (this.entity.show = v)
|
||||
if (this._DialogObject && this._DialogObject.showBtn) {
|
||||
this._DialogObject.showBtn.checked = v
|
||||
}
|
||||
if (this.options.label && this.options.label.show && this.label) {
|
||||
this.label.show = v
|
||||
}
|
||||
setTimeout(() => {
|
||||
let center = this.cartesian3Towgs84(this.entity.boundingSphere.center, this.sdk.viewer)
|
||||
let circle = turf.circle([center.lng, center.lat], this.entity.boundingSphere.radius / 1000, { steps: 360, units: 'kilometers' });
|
||||
for (let [key, entity] of this.sdk.entityMap) {
|
||||
if (entity.type === 'BillboardObject' && entity.heightMode == 3) {
|
||||
let pt = turf.point([entity.lng, entity.lat]);
|
||||
if (turf.booleanPointInPolygon(pt, circle)) {
|
||||
entity.updateHeight()
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (entity.label) {
|
||||
entity.label.show = entity.label.show
|
||||
}
|
||||
}
|
||||
}
|
||||
syncData(this.sdk, this.options.id)
|
||||
syncSplitData(this.sdk, this.options.id)
|
||||
}, 300);
|
||||
} else {
|
||||
console.error("参数必须为boolean")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseTileset
|
||||
45
src/Obj/Base/BaseSource/index.js
Normal file
45
src/Obj/Base/BaseSource/index.js
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-11-20 18:31
|
||||
* @description:index
|
||||
* @update: 2023-11-20 18:31
|
||||
*/
|
||||
import Base from "../index";
|
||||
import {getHost, getToken} from "../../../on";
|
||||
import { setSplitDirection } from "../../../Global/SplitScreen";
|
||||
|
||||
class BaseSource extends Base {
|
||||
constructor(sdk, options) {
|
||||
super(sdk, options);
|
||||
this.sdk.addIncetance(this.options.id, this)
|
||||
if (this.options.show) {
|
||||
setSplitDirection(0, this.options.id)
|
||||
}
|
||||
}
|
||||
|
||||
setDefaultValue() {
|
||||
super.setDefaultValue();
|
||||
this.options.host = this.options.host || getHost()
|
||||
}
|
||||
|
||||
requestResource() {
|
||||
let url = ""
|
||||
if (this.options.host.endsWith("yjearth4.0"))
|
||||
url = this.options.host + '/data/service/load-compact-service'
|
||||
else
|
||||
url = this.options.host + '/yjearth4.0/data/service/load-compact-service'
|
||||
return fetch(url, {
|
||||
method: 'post',
|
||||
body: JSON.stringify({source_id: this.options.id}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"token": getToken(),
|
||||
"Authorization": "Bearer " + getToken(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default BaseSource
|
||||
24
src/Obj/Base/BatchModel/_element.js
Normal file
24
src/Obj/Base/BatchModel/_element.js
Normal file
@ -0,0 +1,24 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col add-type-box">
|
||||
<span class="label" style="flex: 0 0 56px;">添加方式</span>
|
||||
<div class="add-type"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">间距</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="1" max="99999" @model="spacing">
|
||||
<span class="unit">米</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
229
src/Obj/Base/BatchModel/_element_拓展.js
Normal file
229
src/Obj/Base/BatchModel/_element_拓展.js
Normal file
@ -0,0 +1,229 @@
|
||||
import { attributeElm } from '../../Element/elm_html'
|
||||
|
||||
function html(that) {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">颜色</span>
|
||||
<div class="color"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div style="width: 46%;">
|
||||
<div class="row add-type-box">
|
||||
<div class="lable-left-line">添加方式
|
||||
<div class="input input-select add-type" style="margin-left: 20px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 50%;">
|
||||
<div class="row" style="margin-bottom: 5px;">
|
||||
<div class="col">
|
||||
<span class="label">朝向偏移</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="360" @model="deviation">
|
||||
<span class="unit">°</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h4>模型间隔</h4>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="YJ-custom-checkbox-box" style="display: flex;align-items: center;cursor: pointer;">
|
||||
<input type="checkbox" class="YJ-custom-checkbox">
|
||||
<span style="margin-left: 10px; margin-bottom: 1px;user-select: none;">自定义距离</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="YJ-custom-checkbox-box" style="display: flex;align-items: center;cursor: pointer;">
|
||||
<input type="checkbox" class="YJ-custom-checkbox">
|
||||
<span style="margin-left: 10px; margin-bottom: 1px;user-select: none;">固定距离</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">模型间隔</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="360" @model="spacing">
|
||||
<span class="unit">米</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h4>线型选择</h4>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="YJ-custom-checkbox-box" style="display: flex;align-items: center;cursor: pointer;">
|
||||
<input type="checkbox" class="YJ-custom-checkbox">
|
||||
<span style="margin-left: 10px; margin-bottom: 1px;user-select: none;">折线</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="YJ-custom-checkbox-box" style="display: flex;align-items: center;cursor: pointer;">
|
||||
<input type="checkbox" class="YJ-custom-checkbox">
|
||||
<span style="margin-left: 10px; margin-bottom: 1px;user-select: none;">曲线</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">线条数量</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="360" @model="lineNum">
|
||||
<span class="unit">条</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">线条间隔</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="360" @model="lineSpacing">
|
||||
<span class="unit">米</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="YJ-custom-checkbox-box" style="display: flex;align-items: center;cursor: pointer;">
|
||||
<input type="checkbox" class="YJ-custom-checkbox">
|
||||
<span style="margin-left: 10px; margin-bottom: 1px;user-select: none;">随机采样</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">随机数量</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="360" @model="lineNum">
|
||||
<span class="unit">个</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="YJ-custom-checkbox-box" style="display: flex;align-items: center;cursor: pointer;">
|
||||
<input type="checkbox" class="YJ-custom-checkbox">
|
||||
<span style="margin-left: 10px; margin-bottom: 1px;user-select: none;">网格采样</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">首边间隔</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="360" @model="fistLineSpacing">
|
||||
<span class="unit">米</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">次边间隔</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="360" @model="secondLineSpacing">
|
||||
<span class="unit">米</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="row">
|
||||
<DIV-cy-tabs id="point-object-edit-tabs">
|
||||
<DIV-cy-tab-pane label="空间信息">
|
||||
<div class="row">
|
||||
<div class="col height-mode-box">
|
||||
<span class="label" style="flex: 0 0 56px;">高度模式</span>
|
||||
<div class="height-mode"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div style="width: 46%;">
|
||||
<div class="row add-type-box">
|
||||
<div class="lable-left-line">缩放
|
||||
<div class="YJ-custom-checkbox-box" style="display: flex;align-items: center;cursor: pointer;">
|
||||
<input type="checkbox" class="YJ-custom-checkbox">
|
||||
<span style="margin-left: 10px; margin-bottom: 1px;user-select: none;">是否等比例缩放</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标注风格">
|
||||
<div>
|
||||
<div class="row" style="margin-bottom: 10px;">
|
||||
<div class="col">
|
||||
<span class="label">新增模型风格设置</span>
|
||||
<button @click="openRichTextEditor">初始风格</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button @click="openRichTextEditor">当前风格</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-bottom: 10px;">
|
||||
<div class="col" style="flex: 0 0 80px;">
|
||||
<span class="label" style="flex: none;">显隐</span>
|
||||
<input class="btn-switch" type="checkbox" @model="billboardShow">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 90px;">
|
||||
<span class="label" style="flex: none;">图标</span>
|
||||
<div class="image-box" @click="clickChangeImage">
|
||||
<img class="image" src="" alt="" @model="billboardImage">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 90px;">
|
||||
<span class="label" style="flex: none;">默认图标</span>
|
||||
<div class="image-box" @click="clickChangeDefaultImage">
|
||||
<img class="image" src="" alt="" @model="billboardDefaultImage">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">图标倍数</span>
|
||||
<div class="input-number input-number-unit-2">
|
||||
<input class="input" type="number" title="" min="0.1" max="99" @model="billboardScale">
|
||||
<span class="unit">倍</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>文字设置</h4>
|
||||
<div class="row">
|
||||
<div class="col" style="flex: 0 0 80px;">
|
||||
<span class="label" style="flex: none;">显隐</span>
|
||||
<input class="btn-switch" type="checkbox" @model="labelShow">
|
||||
</div>
|
||||
<div class="col font-select-box">
|
||||
<span class="label" style="flex: none;">字体选择</span>
|
||||
<div class="input input-select font-select"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">文字大小</span>
|
||||
<div class="input-number input-number-unit-2">
|
||||
<input class="input" type="number" title="" min="1" max="99" @model="labelFontSize" style="width: 70px;">
|
||||
<span class="unit">px</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">文字颜色</span>
|
||||
<div class="labelColor"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
</DIV-cy-tabs>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
92
src/Obj/Base/BatchModel/eventBinding.js
Normal file
92
src/Obj/Base/BatchModel/eventBinding.js
Normal file
@ -0,0 +1,92 @@
|
||||
class eventBinding {
|
||||
constructor() {
|
||||
this.element = {}
|
||||
}
|
||||
static event = {}
|
||||
|
||||
getEvent(name) {
|
||||
return eventBinding.event[name]
|
||||
}
|
||||
|
||||
getEventAll() {
|
||||
return eventBinding.event
|
||||
}
|
||||
|
||||
setEvent(name, event) {
|
||||
eventBinding.event[name] = event
|
||||
}
|
||||
|
||||
on(that, elements) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let Event = []
|
||||
let isEvent = false
|
||||
let removeName = []
|
||||
if (!elements[i] || !elements[i].attributes) {
|
||||
continue;
|
||||
}
|
||||
for (let m of elements[i].attributes) {
|
||||
switch (m.name) {
|
||||
case '@model': {
|
||||
isEvent = true
|
||||
if (elements[i].type == 'checkbox') {
|
||||
Event.push((e) => { that[m.value] = e.target.checked })
|
||||
elements[i].checked = that[m.value]
|
||||
}
|
||||
else {
|
||||
Event.push((e) => {
|
||||
let value = e.target.value
|
||||
if (e.target.type == 'number') {
|
||||
value = Number(value)
|
||||
}
|
||||
that[m.value] = value
|
||||
})
|
||||
if (elements[i].nodeName == 'IMG') {
|
||||
elements[i].src = that[m.value]
|
||||
}
|
||||
else {
|
||||
elements[i].value = that[m.value]
|
||||
}
|
||||
}
|
||||
if (this.element[m.value]) {
|
||||
this.element[m.value].push(elements[i])
|
||||
}
|
||||
else {
|
||||
this.element[m.value] = [elements[i]]
|
||||
}
|
||||
removeName.push(m.name)
|
||||
break;
|
||||
}
|
||||
case '@click': {
|
||||
elements[i].addEventListener('click', (e) => {
|
||||
if (typeof (that.Dialog[m.value]) === 'function') {
|
||||
that.Dialog[m.value](e)
|
||||
}
|
||||
});
|
||||
removeName.push(m.name)
|
||||
// elements[i].attributes.removeNamedItem(m.name)
|
||||
break;
|
||||
}
|
||||
}
|
||||
// elements[i].attributes[m] = undefined
|
||||
}
|
||||
for (let n = 0; n < removeName.length; n++) {
|
||||
elements[i].attributes.removeNamedItem(removeName[n])
|
||||
}
|
||||
|
||||
if (isEvent) {
|
||||
let ventType = 'input'
|
||||
if (elements[i].tagName != 'INPUT' || elements[i].type == 'checkbox') {
|
||||
ventType = 'change'
|
||||
}
|
||||
elements[i].addEventListener(ventType, (e) => {
|
||||
for (let t = 0; t < Event.length; t++) {
|
||||
Event[t](e)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const EventBinding = new eventBinding();
|
||||
export default EventBinding;
|
||||
713
src/Obj/Base/BatchModel/index.js
Normal file
713
src/Obj/Base/BatchModel/index.js
Normal file
@ -0,0 +1,713 @@
|
||||
/**
|
||||
* @description 批量模型
|
||||
*/
|
||||
import Dialog from '../../Element/Dialog';
|
||||
import { html } from "./_element";
|
||||
import EventBinding from '../../Element/Dialog/eventBinding';
|
||||
import Base from "../index";
|
||||
import Tools from "../../../Tools";
|
||||
import { syncData } from '../../../Global/MultiViewportMode'
|
||||
import Model from '../BaseSource/BaseModel/Model'
|
||||
import { legp } from '../../Element/datalist'
|
||||
|
||||
import DrawPolyline from '../../../Draw/drawPolyline'
|
||||
import DrawPolygon from '../../../Draw/drawPolygon'
|
||||
import DrawThreeRect from '../../../Draw/drawThreeRect'
|
||||
import DrawPoint from '../../../Draw/drawPoint'
|
||||
import { setActiveViewer, closeRotateAround, closeViewFollow, CesiumContainer } from '../../../Global/global'
|
||||
|
||||
import { setSplitDirection, syncSplitData, setActiveId } from '../../../Global/SplitScreen'
|
||||
|
||||
class BatchModel extends Base {
|
||||
/**
|
||||
* @constructor
|
||||
* @param sdk
|
||||
* @description 批量模型
|
||||
* @param options {object} 批量模型属性
|
||||
* @param options.name=未命名对象 {string} 名称
|
||||
* @param options.type=polygon {string} 线类型(line,polygon)
|
||||
* @param options.url=polygon {string} 线类型(line,polygon,point)
|
||||
* @param options.spacing= {number} 间距
|
||||
* @param options.show=true {boolean}
|
||||
* @param Dialog {object} 弹框对象
|
||||
* @param Dialog.confirmCallBack {function} 弹框确认时的回调
|
||||
* */
|
||||
constructor(sdk, options = {}, callback = null, _Dialog = {}) {
|
||||
super(sdk, options);
|
||||
this.viewer = this.sdk.viewer
|
||||
this.options.name = options.name || '批量模型'
|
||||
this.options.type = options.type || '面'
|
||||
this.options.url = options.url || ''
|
||||
this.options.spacing = options.spacing * 1 || 50
|
||||
this.options.positions = options.positions || []
|
||||
this.options.show = (options.show || options.show === false) ? options.show : true
|
||||
this.callback = callback
|
||||
this.Dialog = _Dialog
|
||||
this._EventBinding = new EventBinding()
|
||||
this._elms = {};
|
||||
this.pointArr = []
|
||||
this.sdk.addIncetance(this.options.id, this)
|
||||
let tools = new Tools(sdk)
|
||||
// BatchModel.computeDis(this)
|
||||
// if (this.options.positions.length > 0 || this.options.positions.lng) {
|
||||
if (this.options.spacing < 0 || options.spacing * 1 === 0) {
|
||||
tools.message({ type: 'warning', text: '请输入正确的间距!' })
|
||||
return;
|
||||
}
|
||||
if ((options.type && options.spacing != undefined) || options.type == '点') {
|
||||
// BatchModel.computeDis(this)
|
||||
|
||||
let Draw
|
||||
switch (options.type) {
|
||||
case '点':
|
||||
Draw = new DrawPoint(this.sdk)
|
||||
break;
|
||||
case '线':
|
||||
Draw = new DrawPolyline(this.sdk)
|
||||
break;
|
||||
case '面':
|
||||
Draw = new DrawThreeRect(this.sdk)
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Draw && Draw.start((a, positions) => {
|
||||
this.options.positions = positions;
|
||||
//判断范围是否过大
|
||||
if (options.type == '面') {
|
||||
let posi = positions.map(v => {
|
||||
return Cesium.Cartesian3.fromDegrees(v.lng, v.lat)
|
||||
})
|
||||
let dis1 = Cesium.Cartesian3.distance(posi[0], posi[1])
|
||||
let dis2 = Cesium.Cartesian3.distance(posi[1], posi[2])
|
||||
let num1 = dis1 / this.options.spacing
|
||||
let num2 = dis2 / this.options.spacing
|
||||
if (num1 * num2 > 100) {
|
||||
tools.message({ type: 'warning', text: '数量大于100,请重新绘制' })
|
||||
return;
|
||||
}
|
||||
} else if (options.type == '线') {
|
||||
let posi = positions.map(v => {
|
||||
return Cesium.Cartesian3.fromDegrees(v.lng, v.lat)
|
||||
})
|
||||
let dis = 0
|
||||
for (let i = 0; i < posi.length - 2; i++) {
|
||||
dis += Cesium.Cartesian3.distance(posi[i], posi[i + 1])
|
||||
}
|
||||
if (dis / this.options.spacing > 100) {
|
||||
tools.message({ type: 'warning', text: '数量大于100,请重新绘制' })
|
||||
return;
|
||||
}
|
||||
}
|
||||
// this.callback(this.options);
|
||||
(this.options.positions.length || this.options.positions.lng) && BatchModel.computeDis(this)
|
||||
})
|
||||
|
||||
} else {
|
||||
this.edit(true)
|
||||
}
|
||||
}
|
||||
|
||||
// 计算距离
|
||||
static async computeDis(that) {
|
||||
let fromDegreesArray = []
|
||||
let arr
|
||||
let posiArr = []
|
||||
let array = []
|
||||
if (that.options.type == '面') {
|
||||
that.options.positions.forEach(item => {
|
||||
fromDegreesArray.push(item.lng, item.lat)
|
||||
})
|
||||
// arr = that.generateInterpolatedPoints(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray), that.options.spacing)
|
||||
arr = await that.computedArea(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray), that.options.spacing)
|
||||
array[0] = arr
|
||||
array[1] = that.calculateRoadAngle(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray)[0], Cesium.Cartesian3.fromDegreesArray(fromDegreesArray)[3])
|
||||
arr.forEach((item, index) => {
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(
|
||||
item // Cartesian3对象 {x, y, z}
|
||||
);
|
||||
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
|
||||
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
|
||||
const height = cartographic.height;
|
||||
posiArr.push({
|
||||
lng: longitude,
|
||||
lat: latitude,
|
||||
alt: height
|
||||
})
|
||||
})
|
||||
} else if (that.options.type == '线') {
|
||||
that.options.positions.forEach(item => {
|
||||
fromDegreesArray.push(item.lng, item.lat)
|
||||
})
|
||||
array = await that.linePoint(Cesium.Cartesian3.fromDegreesArray(fromDegreesArray), that.options.spacing)
|
||||
arr = array[0]
|
||||
that.pointArr = arr
|
||||
arr.forEach((item, index) => {
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(
|
||||
item // Cartesian3对象 {x, y, z}
|
||||
);
|
||||
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
|
||||
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
|
||||
const height = cartographic.height;
|
||||
posiArr.push({
|
||||
lng: longitude,
|
||||
lat: latitude,
|
||||
alt: height
|
||||
})
|
||||
})
|
||||
} else if (that.options.type == '点') {
|
||||
let height = await that.getClampToHeight({ lng: that.options.positions.lng, lat: that.options.positions.lat })
|
||||
posiArr = [{ lng: that.options.positions.lng, lat: that.options.positions.lat, alt: height }]
|
||||
// posiArr = [that.options.positions]
|
||||
that.pointArr = posiArr
|
||||
}
|
||||
let params = {
|
||||
type: that.options.type,
|
||||
positions: posiArr,
|
||||
rotate: that.options.type == '点' ? undefined : array[1]
|
||||
}
|
||||
that.callback(params)
|
||||
// posiArr.forEach((item, index) => {
|
||||
// let model = new Model(that.sdk, {
|
||||
// id: 'model' + index,
|
||||
// show: that.options.show,
|
||||
// url: that.options.url,
|
||||
// position: item,
|
||||
// rotate: that.options.type == '点' ? undefined : { x: 0, y: 0, z: array[1] && (array[1][index] || array[1]) }
|
||||
// })
|
||||
// that.pointArr.push(model)
|
||||
// })
|
||||
}
|
||||
async linePoint(polygonPositions, spacing) {
|
||||
let boundaryPoints = [];
|
||||
let boundaryAngle = [];
|
||||
for (let i = 0; i < polygonPositions.length - 1; i++) {
|
||||
|
||||
const start = polygonPositions[i];
|
||||
const end = polygonPositions[(i + 1) % polygonPositions.length];
|
||||
const segmentLength = Cesium.Cartesian3.distance(start, end);
|
||||
const segments = Math.ceil(segmentLength / spacing);
|
||||
|
||||
for (let j = 0; j <= segments; j++) {
|
||||
const ratio = j / segments;
|
||||
let point = Cesium.Cartesian3.lerp(
|
||||
start, end, ratio, new Cesium.Cartesian3()
|
||||
);
|
||||
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(
|
||||
point // Cartesian3对象 {x, y, z}
|
||||
);
|
||||
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
|
||||
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
|
||||
|
||||
|
||||
let height = await this.getClampToHeight({ lng: longitude, lat: latitude })
|
||||
point = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);
|
||||
|
||||
boundaryPoints.push(point);
|
||||
if (j != segments || i == polygonPositions.length - 2) {
|
||||
boundaryAngle.push(this.calculateRoadAngle(start, end))
|
||||
}
|
||||
}
|
||||
}
|
||||
return [[...new Set(boundaryPoints
|
||||
.map(p => `${p.x},${p.y},${p.z}`))]
|
||||
.map(str => {
|
||||
const [x, y, z] = str.split(',').map(Number);
|
||||
return new Cesium.Cartesian3(x, y, z);
|
||||
}), boundaryAngle];
|
||||
}
|
||||
calculateRoadAngle(startPoint, endPoint) {
|
||||
const normal = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(startPoint);
|
||||
const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(startPoint, undefined, normal);
|
||||
const inverseMatrix = Cesium.Matrix4.inverse(enuMatrix, new Cesium.Matrix4());
|
||||
|
||||
const localEnd = Cesium.Matrix4.multiplyByPoint(inverseMatrix, endPoint, new Cesium.Cartesian3());
|
||||
const horizontalVec = new Cesium.Cartesian2(localEnd.x, localEnd.y);
|
||||
Cesium.Cartesian2.normalize(horizontalVec, horizontalVec);
|
||||
|
||||
const north = new Cesium.Cartesian2(1, 0);
|
||||
|
||||
let angle = Cesium.Cartesian2.angleBetween(north, horizontalVec);
|
||||
angle = Cesium.Math.toDegrees(angle)
|
||||
const cross = Cesium.Cartesian2.cross(north, horizontalVec, new Cesium.Cartesian2());
|
||||
// return cross < 0 ? angle : - angle;
|
||||
return cross < 0 ? -angle : angle;
|
||||
}
|
||||
generateInterpolatedPoints(polygonPositions, spacing) {
|
||||
// 1. 边界点插值
|
||||
const boundaryPoints = [];
|
||||
|
||||
for (let i = 0; i < polygonPositions.length; i++) {
|
||||
|
||||
const start = polygonPositions[i];
|
||||
const end = polygonPositions[(i + 1) % polygonPositions.length];
|
||||
const segmentLength = Cesium.Cartesian3.distance(start, end);
|
||||
const segments = Math.ceil(segmentLength / spacing);
|
||||
|
||||
for (let j = 0; j <= segments; j++) {
|
||||
const ratio = j / segments;
|
||||
const point = Cesium.Cartesian3.lerp(
|
||||
start, end, ratio, new Cesium.Cartesian3()
|
||||
);
|
||||
boundaryPoints.push(point);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 内部网格生成
|
||||
const extent = this.computePolygonExtent(polygonPositions);
|
||||
let result = this.createGridFromBBox(extent, this.options.spacing)
|
||||
// const extent = Cesium.Rectangle.fromCartesianArray(polygonPositions);
|
||||
|
||||
const gridPoints = [];
|
||||
// const polygon = new Cesium.PolygonHierarchy(polygonPositions);
|
||||
var polygon = []
|
||||
this.options.positions.forEach(item => {
|
||||
polygon.push([item.lng, item.lat])
|
||||
})
|
||||
polygon.push(polygon[0])
|
||||
for (let x = extent.west; x <= extent.east; x += result.lonStep) {
|
||||
for (let y = extent.south; y <= extent.north; y += result.latStep) {
|
||||
const position = Cesium.Cartesian3.fromDegrees(x, y);
|
||||
const point = turf.point([x, y]);
|
||||
const polygonTurf = turf.polygon([polygon]);
|
||||
const isInside = turf.booleanPointInPolygon(point, polygonTurf);
|
||||
isInside && gridPoints.push(position)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 合并结果并去重
|
||||
// return [...new Set([...boundaryPoints, ...gridPoints]
|
||||
return [...new Set([...gridPoints]
|
||||
.map(p => `${p.x},${p.y},${p.z}`))]
|
||||
.map(str => {
|
||||
const [x, y, z] = str.split(',').map(Number);
|
||||
return new Cesium.Cartesian3(x, y, z);
|
||||
});
|
||||
}
|
||||
|
||||
createGridFromBBox(bbox, spacing) {
|
||||
const earthRadius = 6378137; // WGS84椭球体长半轴
|
||||
// 计算经度方向网格数
|
||||
const lonDistance = Cesium.Cartesian3.distance(
|
||||
Cesium.Cartesian3.fromDegrees(bbox.west, (bbox.south + bbox.north) / 2, 0),
|
||||
Cesium.Cartesian3.fromDegrees(bbox.east, (bbox.south + bbox.north) / 2, 0)
|
||||
);
|
||||
const lonCount = Math.ceil(lonDistance / spacing);
|
||||
|
||||
// 计算纬度方向网格数
|
||||
const latDistance = Cesium.Cartesian3.distance(
|
||||
Cesium.Cartesian3.fromDegrees((bbox.west + bbox.east) / 2, bbox.south, 0),
|
||||
Cesium.Cartesian3.fromDegrees((bbox.west + bbox.east) / 2, bbox.north, 0)
|
||||
);
|
||||
const latCount = Math.ceil(latDistance / spacing);
|
||||
// 生成网格线
|
||||
const lonStep = (bbox.east - bbox.west) / lonCount;
|
||||
const latStep = (bbox.north - bbox.south) / latCount;
|
||||
return { lonStep, latStep }
|
||||
}
|
||||
|
||||
computePolygonExtent(positions) {
|
||||
// 计算多边形经纬度范围
|
||||
const cartographics = positions.map(p =>
|
||||
Cesium.Cartographic.fromCartesian(p));
|
||||
const lons = cartographics.map(c => Cesium.Math.toDegrees(c.longitude));
|
||||
const lats = cartographics.map(c => Cesium.Math.toDegrees(c.latitude));
|
||||
return {
|
||||
west: Math.min(...lons),
|
||||
east: Math.max(...lons),
|
||||
south: Math.min(...lats),
|
||||
north: Math.max(...lats)
|
||||
};
|
||||
}
|
||||
async computedArea(polygonPositions, spacing) {
|
||||
let dis12 = Cesium.Cartesian3.distance(polygonPositions[0], polygonPositions[1]);
|
||||
let dis23 = Cesium.Cartesian3.distance(polygonPositions[1], polygonPositions[2]);
|
||||
let vec12 = Cesium.Cartesian3.subtract(polygonPositions[1], polygonPositions[0], new Cesium.Cartesian3());
|
||||
let vec23 = Cesium.Cartesian3.subtract(polygonPositions[2], polygonPositions[1], new Cesium.Cartesian3());
|
||||
|
||||
let num12 = Math.ceil(dis12 / spacing);
|
||||
let num23 = Math.ceil(dis23 / spacing);
|
||||
|
||||
let line1 = []
|
||||
for (let i = 0; i < num12; i++) {
|
||||
line1.push(await this.calculatePointB(polygonPositions[0], polygonPositions[1], i * spacing))
|
||||
}
|
||||
let line2 = []
|
||||
for (let i = 0; i < num12; i++) {
|
||||
line2.push(await this.calculatePointB(polygonPositions[3], polygonPositions[2], i * spacing))
|
||||
}
|
||||
|
||||
let allPoints = []
|
||||
for (let i = 0; i < line1.length; i++) {
|
||||
for (let j = 0; j < num23; j++) {
|
||||
allPoints.push(await this.calculatePointB(line1[i], line2[i], j * spacing))
|
||||
}
|
||||
}
|
||||
return allPoints
|
||||
}
|
||||
async calculatePointB(pointA, pointC, distance) {
|
||||
// 将输入坐标转换为Cartesian3类型
|
||||
// const pointA = Cesium.Cartesian3.fromDegrees(a.longitude, a.latitude, a.height);
|
||||
// const pointC = Cesium.Cartesian3.fromDegrees(c.longitude, c.latitude, c.height);
|
||||
|
||||
// 计算向量AC
|
||||
const vectorAC = Cesium.Cartesian3.subtract(pointC, pointA, new Cesium.Cartesian3());
|
||||
|
||||
// 计算向量AC的长度
|
||||
const lengthAC = Cesium.Cartesian3.magnitude(vectorAC);
|
||||
|
||||
// 归一化向量AC
|
||||
const unitVector = Cesium.Cartesian3.normalize(vectorAC, new Cesium.Cartesian3());
|
||||
|
||||
// 计算点B坐标
|
||||
const scaledVector = Cesium.Cartesian3.multiplyByScalar(unitVector, distance, new Cesium.Cartesian3());
|
||||
const pointB = Cesium.Cartesian3.add(pointA, scaledVector, new Cesium.Cartesian3());
|
||||
|
||||
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(
|
||||
pointB // Cartesian3对象 {x, y, z}
|
||||
);
|
||||
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
|
||||
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
|
||||
|
||||
|
||||
let height = await this.getClampToHeight({ lng: longitude, lat: latitude })
|
||||
let point = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);
|
||||
// 转换回经纬度
|
||||
// const cartographic = Cesium.Cartographic.fromCartesian(pointB);
|
||||
// return {
|
||||
// longitude: Cesium.Math.toDegrees(cartographic.longitude),
|
||||
// latitude: Cesium.Math.toDegrees(cartographic.latitude),
|
||||
// height: cartographic.height
|
||||
// };
|
||||
// return pointB
|
||||
return point
|
||||
}
|
||||
get show() {
|
||||
return this.options.show
|
||||
}
|
||||
|
||||
set show(v) {
|
||||
this.options.show = v
|
||||
for (let i = 0; i < this.pointArr.length; i++) {
|
||||
this.pointArr[i].show = v
|
||||
}
|
||||
}
|
||||
get type() {
|
||||
return this.options.type
|
||||
}
|
||||
|
||||
set type(v) {
|
||||
this.options.type = v
|
||||
this._elms.type &&
|
||||
this._elms.type.forEach(item => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
get spacing() {
|
||||
return this.options.spacing
|
||||
}
|
||||
|
||||
set spacing(v) {
|
||||
this.options.spacing = v
|
||||
this._elms.spacing &&
|
||||
this._elms.spacing.forEach(item => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
/**
|
||||
* @description 编辑框
|
||||
* @param state=false {boolean} 状态: true打开, false关闭
|
||||
*/
|
||||
async edit(state = false) {
|
||||
let _this = this
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
|
||||
// let elms = this.sdk.viewer._container.getElementsByClassName('YJ-custom-base-dialog')
|
||||
// for (let i = elms.length - 1; i >= 0; i--) {
|
||||
// this.sdk.viewer._container.removeChild(elms[i])
|
||||
// }
|
||||
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
|
||||
if (state) {
|
||||
this._DialogObject = await new Dialog(this.sdk, this.originalOptions, {
|
||||
title: '默认模型参数设置', left: '180px', top: '100px',
|
||||
confirmCallBack: (options) => {
|
||||
this.name = this.name.trim()
|
||||
if (!this.name) {
|
||||
// this.name = '未命名对象'
|
||||
this.name = '飞线'
|
||||
}
|
||||
|
||||
let Draw
|
||||
switch (this.options.type) {
|
||||
case '点':
|
||||
Draw = new DrawPoint(this.sdk)
|
||||
break;
|
||||
case '线':
|
||||
Draw = new DrawPolyline(this.sdk)
|
||||
break;
|
||||
case '面':
|
||||
Draw = new DrawThreeRect(this.sdk)
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Draw && Draw.start((a, positions) => {
|
||||
this.options.positions = positions;
|
||||
// this.callback(this.options);
|
||||
(this.options.positions.length || this.options.positions.lng) && BatchModel.computeDis(this)
|
||||
})
|
||||
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
this._DialogObject.close()
|
||||
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.originalOptions)
|
||||
syncData(this.sdk, this.options.id)
|
||||
syncSplitData(this.sdk, this.options.id)
|
||||
},
|
||||
// resetCallBack: () => {
|
||||
// this.reset()
|
||||
// console.log('22222')
|
||||
// this.Dialog.resetCallBack && this.Dialog.resetCallBack()
|
||||
// },
|
||||
// removeCallBack: () => {
|
||||
// console.log('33333')
|
||||
// this.Dialog.removeCallBack && this.Dialog.removeCallBack()
|
||||
// },
|
||||
closeCallBack: () => {
|
||||
this.reset()
|
||||
// this.entity.style = new Cesium.Cesium3DTileStyle({
|
||||
// color: "color('rgba(255,255,255," + this.newData.transparency + ")')",
|
||||
// show: true,
|
||||
// });
|
||||
this.Dialog.closeCallBack && this.Dialog.closeCallBack()
|
||||
},
|
||||
addFootElm: [
|
||||
{
|
||||
tagName: 'button',
|
||||
className: 'flipe-over-y',
|
||||
innerHTML: '重置',
|
||||
event: [
|
||||
'click',
|
||||
() => {
|
||||
this.reset()
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
// showCallBack: (show) => {
|
||||
// this.show = show
|
||||
// this.Dialog.showCallBack && this.Dialog.showCallBack()
|
||||
// }
|
||||
}, true)
|
||||
this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' flow-line-surface'
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
this._DialogObject.contentAppChild(contentElm)
|
||||
// 颜色组件
|
||||
// let waterColorPicker = new YJColorPicker({
|
||||
// el: contentElm.getElementsByClassName("flowLine-color")[0],
|
||||
// size: 'mini',//颜色box类型
|
||||
// alpha: true,//是否开启透明度
|
||||
// defaultColor: this.color,
|
||||
// disabled: false,//是否禁止打开颜色选择器
|
||||
// openPickerAni: 'opacity',//打开颜色选择器动画
|
||||
// sure: (color) => {
|
||||
// this.color = color
|
||||
// },//点击确认按钮事件回调
|
||||
// clear: () => {
|
||||
// this.color = 'rgba(255,255,255,1)'
|
||||
// },//点击清空按钮事件回调
|
||||
// })
|
||||
|
||||
let all_elm = contentElm.getElementsByTagName("*")
|
||||
this._EventBinding.on(this, all_elm)
|
||||
this._elms = this._EventBinding.element
|
||||
|
||||
let nameData = [
|
||||
{
|
||||
name: '点',
|
||||
value: '点',
|
||||
},
|
||||
{
|
||||
name: '线',
|
||||
value: '线',
|
||||
},
|
||||
{
|
||||
name: '面',
|
||||
value: '面',
|
||||
}
|
||||
]
|
||||
|
||||
let nameDataLegpObject = legp(
|
||||
this._DialogObject._element.content.getElementsByClassName(
|
||||
'add-type-box'
|
||||
)[0],
|
||||
'.add-type'
|
||||
)
|
||||
if (nameDataLegpObject) {
|
||||
nameDataLegpObject.legp_search(nameData)
|
||||
let nameDataLegpElm = this._DialogObject._element.content
|
||||
.getElementsByClassName('add-type')[0]
|
||||
.getElementsByTagName('input')[0]
|
||||
this._elms.type = [nameDataLegpElm]
|
||||
nameDataLegpElm.value = this.options.type
|
||||
for (let i = 0; i < nameData.length; i++) {
|
||||
if (nameData[i].value === nameDataLegpElm.value) {
|
||||
nameDataLegpObject.legp_searchActive(nameData[i].value)
|
||||
break
|
||||
}
|
||||
}
|
||||
nameDataLegpElm.addEventListener('input', () => {
|
||||
for (let i = 0; i < nameData.length; i++) {
|
||||
if (nameData[i].value === nameDataLegpElm.value) {
|
||||
this.type = nameData[i].value
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// this._elms.color = [waterColorPicker]
|
||||
} else {
|
||||
// if (this._element_style) {
|
||||
// document.getElementsByTagName('head')[0].removeChild(this._element_style)
|
||||
// this._element_style = null
|
||||
// }
|
||||
// if (this._DialogObject && this._DialogObject.remove) {
|
||||
// this._DialogObject.remove()
|
||||
// this._DialogObject = null
|
||||
// }
|
||||
}
|
||||
}
|
||||
drawArea() {
|
||||
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.name = this.originalOptions.name
|
||||
this.type = this.originalOptions.type
|
||||
this.spacing = this.originalOptions.spacing
|
||||
this.show = this.originalOptions.show
|
||||
this.options.spacing = this.originalOptions.spacing
|
||||
}
|
||||
|
||||
/**
|
||||
* 飞到对应实体
|
||||
*/
|
||||
async flyTo(options = {}) {
|
||||
setActiveViewer(0)
|
||||
closeRotateAround(this.sdk)
|
||||
closeViewFollow(this.sdk)
|
||||
|
||||
if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) {
|
||||
let orientation = {
|
||||
heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0),
|
||||
pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0),
|
||||
roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0)
|
||||
}
|
||||
|
||||
let lng = this.options.customView.relativePosition.lng
|
||||
let lat = this.options.customView.relativePosition.lat
|
||||
let alt = this.options.customView.relativePosition.alt
|
||||
let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
|
||||
let position = { lng: 0, lat: 0 }
|
||||
if (this.options.position) {
|
||||
position = { ...this.options.position }
|
||||
}
|
||||
else if (this.options.positions) {
|
||||
position = { ...this.options.positions[0] }
|
||||
}
|
||||
else if (this.options.center) {
|
||||
position = { ...this.options.center }
|
||||
}
|
||||
else if (this.options.start) {
|
||||
position = { ...this.options.start }
|
||||
}
|
||||
else {
|
||||
if (this.options.hasOwnProperty('lng')) {
|
||||
position.lng = this.options.lng
|
||||
}
|
||||
if (this.options.hasOwnProperty('lat')) {
|
||||
position.lat = this.options.lat
|
||||
}
|
||||
if (this.options.hasOwnProperty('alt')) {
|
||||
position.alt = this.options.alt
|
||||
}
|
||||
}
|
||||
// 如果没有高度值,则获取紧贴高度计算
|
||||
// if (!position.hasOwnProperty('alt')) {
|
||||
// position.alt = await this.getClampToHeight(position)
|
||||
// }
|
||||
lng = this.options.customView.relativePosition.lng + position.lng
|
||||
lat = this.options.customView.relativePosition.lat + position.lat
|
||||
alt = this.options.customView.relativePosition.alt + position.alt
|
||||
destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
this.sdk.viewer.camera.flyTo({
|
||||
destination: destination,
|
||||
orientation: orientation
|
||||
})
|
||||
}
|
||||
else {
|
||||
let positionArray = []
|
||||
if (this.options.positions.length > 0) {
|
||||
for (let i = 0; i < this.options.positions.length; i++) {
|
||||
let a = Cesium.Cartesian3.fromDegrees(
|
||||
this.options.positions[i].lng,
|
||||
this.options.positions[i].lat,
|
||||
this.options.positions[i].alt
|
||||
)
|
||||
positionArray.push(a.x, a.y, a.z)
|
||||
}
|
||||
let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray)
|
||||
this.viewer.camera.flyToBoundingSphere(BoundingSphere, {
|
||||
offset: {
|
||||
heading: Cesium.Math.toRadians(0.0),
|
||||
pitch: Cesium.Math.toRadians(-20.0),
|
||||
roll: Cesium.Math.toRadians(0.0)
|
||||
}
|
||||
})
|
||||
} else if (this.options.positions.lng) {
|
||||
let orientation = {
|
||||
heading: Cesium.Math.toRadians(0.0),
|
||||
pitch: Cesium.Math.toRadians(-60.0),
|
||||
roll: Cesium.Math.toRadians(0.0)
|
||||
}
|
||||
this.sdk.viewer.camera.flyTo({
|
||||
destination: Cesium.Cartesian3.fromDegrees(this.options.positions.lng, this.options.positions.lat, this.options.positions.alt + 100),
|
||||
// orientation: orientation
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
async remove() {
|
||||
for (let i = 0; i < this.pointArr.length; i++) {
|
||||
this.pointArr[i].remove()
|
||||
}
|
||||
|
||||
this.pointArr = []
|
||||
this.positions = []
|
||||
this.entity = null
|
||||
|
||||
if (this._DialogObject && !this._DialogObject.isDestroy) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
await this.sdk.removeIncetance(this.options.id)
|
||||
await syncData(this.sdk, this.options.id)
|
||||
}
|
||||
|
||||
flicker() { }
|
||||
}
|
||||
|
||||
export default BatchModel
|
||||
335
src/Obj/Base/BillboardObject/_element.js
Normal file
335
src/Obj/Base/BillboardObject/_element.js
Normal file
@ -0,0 +1,335 @@
|
||||
import { attributeElm } from '../../Element/elm_html'
|
||||
|
||||
function html(that) {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label" style="flex: unset;">名称</span>
|
||||
<input class="input" type="text" @model="labelText">
|
||||
</div>
|
||||
<div class="col">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div style="width: 46%;">
|
||||
<div class="row">
|
||||
<p class="lable-left-line">WGS84坐标</p>
|
||||
</div>
|
||||
<div class="row" style="margin-bottom: 5px;">
|
||||
<div class="col">
|
||||
<span class="label">经度</span>
|
||||
<input class="input" type="number" title="" min="-180" max="180" @model="lng">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-bottom: 5px;">
|
||||
<div class="col">
|
||||
<span class="label">纬度</span>
|
||||
<input class="input" type="number" title="" min="-90" max="90" @model="lat">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">海拔高度</span>
|
||||
<div class="input-number input-number-unit-1 alt-box">
|
||||
<input class="input" type="number" title="" min="-9999999" max="999999999" @model="alt">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 50%;">
|
||||
<div class="row coordinate-select-box">
|
||||
<div class="lable-left-line">转换坐标选择
|
||||
<div class="input input-select coordinate-select" style="margin-left: 20px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-bottom: 5px;">
|
||||
<div class="col">
|
||||
<span class="label">X轴:</span>
|
||||
<input style="border: none;background: none;" class="input convert-x" readonly="readonly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-bottom: 5px;">
|
||||
<div class="col">
|
||||
<span class="label">Y轴:</span>
|
||||
<input style="border: none;background: none;" class="input convert-y" readonly="readonly">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">Z轴:</span>
|
||||
<input style="border: none;background: none;" class="input convert-z" readonly="readonly">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col" style="flex: 0 0 120px;">
|
||||
<span class="label">视野缩放</span>
|
||||
<input class="btn-switch" type="checkbox" @model="scaleByDistance">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">最近距离</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="1" max="99999999" @model="near">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">最远距离</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="1" max="99999999" @model="far">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<DIV-cy-tabs id="point-object-edit-tabs">
|
||||
<DIV-cy-tab-pane label="属性信息">
|
||||
${attributeElm(that)}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="空间信息">
|
||||
<div class="row">
|
||||
<div class="col height-mode-box">
|
||||
<span class="label" style="flex: 0 0 56px;">高度模式</span>
|
||||
<div class="height-mode"></div>
|
||||
</div>
|
||||
<div class="col height-box">
|
||||
<span class="label" style="flex: 0 0 56px;">高度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input height" type="number" title="" min="-9999999" max="999999999">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="YJ-custom-checkbox-box" style="display: flex;align-items: center;cursor: pointer;">
|
||||
<input type="checkbox" class="YJ-custom-checkbox">
|
||||
<span style="margin-left: 10px; margin-bottom: 1px;user-select: none;">小数格式</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="YJ-custom-checkbox-box" style="display: flex;align-items: center;cursor: pointer;">
|
||||
<input type="checkbox" class="YJ-custom-checkbox">
|
||||
<span style="margin-left: 10px; margin-bottom: 1px;user-select: none;">度分格式</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="YJ-custom-checkbox-box" style="display: flex;align-items: center;cursor: pointer;">
|
||||
<input type="checkbox" class="YJ-custom-checkbox">
|
||||
<span style="margin-left: 10px; margin-bottom: 1px;user-select: none;">度分秒格式</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div style="flex: 1;">
|
||||
<div class="proj-input-box">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span style="flex: 0 0 40px;">经度</span>
|
||||
<input class="input lng" readonly="readonly">
|
||||
</div>
|
||||
<div class="col">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span style="flex: 0 0 40px;">纬度</span>
|
||||
<input class="input lat" readonly="readonly">
|
||||
</div>
|
||||
<div class="col">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="proj-input-box" style="width: 56%;">
|
||||
<div class="row">
|
||||
<div class="col" style="flex-direction: column;">
|
||||
<div class="row" style="margin-bottom: 15px;">
|
||||
<span style="flex: 0 0 40px;">经度</span>
|
||||
<input class="input lng-dm-d" style="flex: 1;" readonly="readonly">
|
||||
<span class="label" style="flex: 0 0 14px;margin: 0 10px;">度</span>
|
||||
<input class="input lng-dm-m" style="flex: 1;" readonly="readonly">
|
||||
<span class="label" style="flex: 0 0 14px;margin: 0 10px;">分</span>
|
||||
<span class="top-line"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span style="flex: 0 0 40px;">纬度</span>
|
||||
<input class="input lat-dm-d" style="flex: 1;" readonly="readonly">
|
||||
<span class="label" style="flex: 0 0 14px;margin: 0 10px;">度</span>
|
||||
<input class="input lat-dm-m" style="flex: 1;" readonly="readonly">
|
||||
<span class="label" style="flex: 0 0 14px;margin: 0 10px;">分</span>
|
||||
<span class="bottom-line"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="proj-input-box" style="width: 70%;">
|
||||
<div class="row">
|
||||
<div class="col" style="flex-direction: column;">
|
||||
<div class="row" style="margin-bottom: 15px;">
|
||||
<span style="flex: 0 0 40px;">经度</span>
|
||||
<input class="input lng-dms-d" style="flex: 1;" readonly="readonly">
|
||||
<span class="label" style="flex: 0 0 14px;margin: 0 10px;">度</span>
|
||||
<input class="input lng-dms-m" style="flex: 1;" readonly="readonly">
|
||||
<span class="label" style="flex: 0 0 14px;margin: 0 10px;">分</span>
|
||||
<input class="input lng-dms-s" style="flex: 1;" readonly="readonly">
|
||||
<span class="label" style="flex: 0 0 14px;margin: 0 10px;">秒</span>
|
||||
<span class="top-line"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span style="flex: 0 0 40px;">纬度</span>
|
||||
<input class="input lat-dms-d" style="flex: 1;" readonly="readonly">
|
||||
<span class="label" style="flex: 0 0 14px;margin: 0 10px;">度</span>
|
||||
<input class="input lat-dms-m" style="flex: 1;" readonly="readonly">
|
||||
<span class="label" style="flex: 0 0 14px;margin: 0 10px;">分</span>
|
||||
<input class="input lat-dms-s" style="flex: 1;" readonly="readonly">
|
||||
<span class="label" style="flex: 0 0 14px;margin: 0 10px;">秒</span>
|
||||
<span class="bottom-line"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标注风格">
|
||||
<div>
|
||||
<h4>图标设置</h4>
|
||||
<div class="row" style="margin-bottom: 10px;">
|
||||
<div class="col" style="flex: 0 0 80px;">
|
||||
<span class="label" style="flex: none;">显隐</span>
|
||||
<input class="btn-switch" type="checkbox" @model="billboardShow">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 90px;">
|
||||
<span class="label" style="flex: none;">图标</span>
|
||||
<div class="image-box" @click="clickChangeImage">
|
||||
<img class="image" src="" alt="" @model="billboardImage">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 90px;">
|
||||
<span class="label" style="flex: none;">默认图标</span>
|
||||
<div class="image-box" @click="clickChangeDefaultImage">
|
||||
<img class="image" src="" alt="" @model="billboardDefaultImage">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">图标倍数</span>
|
||||
<div class="input-number input-number-unit-2">
|
||||
<input class="input" type="number" title="" min="0.1" max="99" @model="billboardScale">
|
||||
<span class="unit">倍</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>文字设置</h4>
|
||||
<div class="row">
|
||||
<div class="col" style="flex: 0 0 80px;">
|
||||
<span class="label" style="flex: none;">显隐</span>
|
||||
<input class="btn-switch" type="checkbox" @model="labelShow">
|
||||
</div>
|
||||
<div class="col font-select-box">
|
||||
<span class="label" style="flex: none;">字体选择</span>
|
||||
<div class="input input-select font-select"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">文字大小</span>
|
||||
<div class="input-number input-number-unit-2">
|
||||
<input class="input" type="number" title="" min="1" max="99" @model="labelFontSize" style="width: 70px;">
|
||||
<span class="unit">px</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">文字颜色</span>
|
||||
<div class="labelColor"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<!-- <DIV-cy-tab-pane label="效果">
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">扩散</span>
|
||||
<input class="btn-switch" type="checkbox" @model="diffuseShow">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">半径</span>
|
||||
<input class="input" type="number" title="" min="0" max="9999999" @model="diffuseRadius">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">时间</span>
|
||||
<input class="input" type="number" title="" min="100" max="99999" @model="diffuseDuration">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">颜色</span>
|
||||
<div class="diffuseColor"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">雷达</span>
|
||||
<input class="btn-switch" type="checkbox" @model="scanShow">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">半径</span>
|
||||
<input class="input" type="number" title="" min="0" max="9999999" @model="scanRadius">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">时间</span>
|
||||
<input class="input" type="number" title="" min="100" max="99999" @model="scanDuration">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">颜色</span>
|
||||
<div class="scanColor"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>-->
|
||||
<!-- <DIV-cy-tab-pane label="灯光控制">-->
|
||||
<!-- <div>-->
|
||||
<!-- <div class="row">-->
|
||||
<!-- <div class="col">-->
|
||||
<!-- <span class="label">指令</span>-->
|
||||
<!-- <input class="input" type="text" @model="instruct">-->
|
||||
<!-- <button class="primary" @click="instructSubmit">提交</button>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </DIV-cy-tab-pane>-->
|
||||
<!-- <DIV-cy-tab-pane label="设置操作点">-->
|
||||
<!-- <div>-->
|
||||
<!-- <div class="row">-->
|
||||
<!-- <div class="col">-->
|
||||
<!-- <span class="label">设置操作点</span>-->
|
||||
<!-- <input class="input" type="text" @model="operatingPoint">-->
|
||||
<!-- <button class="primary" @click="operatingPointSubmit">提交</button>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </DIV-cy-tab-pane>-->
|
||||
</DIV-cy-tabs>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
3175
src/Obj/Base/BillboardObject/index.js
Normal file
3175
src/Obj/Base/BillboardObject/index.js
Normal file
File diff suppressed because it is too large
Load Diff
85
src/Obj/Base/CircleDiffuse/_element.js
Normal file
85
src/Obj/Base/CircleDiffuse/_element.js
Normal file
@ -0,0 +1,85 @@
|
||||
import { attributeElm, labelStyleElm1, labelStyleElm2 } from '../../Element/elm_html'
|
||||
|
||||
function html(that) {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" maxlength="40" type="text" @model="name">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">透明度</span>
|
||||
<input type="range" min="0" max="1" step="0.01" @model="transparency">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">经度</span>
|
||||
<input class="input" type="number" title="" min="-180" max="180" @model="lng">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">波纹层数</span>
|
||||
<div class="input-number input-number-unit">
|
||||
<input class="input" type="number" title="" min="1" max="10" @model="count">
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">纬度</span>
|
||||
<input class="input" type="number" title="" min="-180" max="180" @model="lat">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">扩散速度</span>
|
||||
<div class="input-number input-number-unit">
|
||||
<input class="input" type="number" title="" min="0" max="20" @model="speed">
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col input-radius-unit-box" style="margin: 0;">
|
||||
<span class="label">半径单位</span>
|
||||
<div class="input-radius-unit"></div>
|
||||
</div>
|
||||
<div class="col" style="margin: 0;">
|
||||
</div>
|
||||
<div class="col" style="margin: 0;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row circle-content-box">
|
||||
<div class="col">
|
||||
<span class="label"></span>
|
||||
<input class="input" type="number" title="" min="-180" max="180" @model="lat">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<DIV-cy-tabs id="circle-diffuse-edit-tabs">
|
||||
<DIV-cy-tab-pane label="属性信息">
|
||||
${attributeElm(that)}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标注风格">
|
||||
${labelStyleElm1()}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标签风格">
|
||||
${labelStyleElm2()}
|
||||
</DIV-cy-tab-pane>
|
||||
</DIV-cy-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
1614
src/Obj/Base/CircleDiffuse/index.js
Normal file
1614
src/Obj/Base/CircleDiffuse/index.js
Normal file
File diff suppressed because it is too large
Load Diff
87
src/Obj/Base/CircleObject/_element.js
Normal file
87
src/Obj/Base/CircleObject/_element.js
Normal file
@ -0,0 +1,87 @@
|
||||
import { attributeElm, labelStyleElm1, labelStyleElm2 } from '../../Element/elm_html'
|
||||
|
||||
function html(that) {
|
||||
return `
|
||||
<div class="row" style="align-items: flex-start;">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" maxlength="40" type="text" @model="name">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 60%;">
|
||||
<div class="row">
|
||||
<div class="col input-select-unit-box">
|
||||
<span class="label" style="margin-right: 0px;">投影面积:</span>
|
||||
<input class="input input-text" readonly="readonly" type="text" @model="area">
|
||||
<div class="input-select-unit"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<DIV-cy-tabs id="circle-object-edit-tabs">
|
||||
<DIV-cy-tab-pane label="属性信息">
|
||||
${attributeElm(that)}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="空间信息">
|
||||
<div class="row">
|
||||
<div class="col height-mode-box">
|
||||
<span class="label" style="flex: 0 0 56px;">高度模式</span>
|
||||
<div class="height-mode"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">Z值统一增加</span>
|
||||
<div class="input-number input-number-unit-1 height-box">
|
||||
<input class="input height" type="number" title="" min="-9999999" max="999999999">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
<button class="confirm height-confirm" style="margin-left: 5px;">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="table spatial-info-table">
|
||||
<div class="table-head">
|
||||
<div class="tr">
|
||||
<div class="th"></div>
|
||||
<div class="th">经度(X)</div>
|
||||
<div class="th">纬度(Y)</div>
|
||||
<div class="th">高度(Z)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="面风格">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">面颜色</span>
|
||||
<div class="color"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">边线颜色</span>
|
||||
<div class="lineColor"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">边线宽度</span>
|
||||
<div class="input-number input-number-unit-2" style="width: 80px;">
|
||||
<input class="input" type="number" title="" min="0" max="99" @model="lineWidth">
|
||||
<span class="unit">px</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标注风格">
|
||||
${labelStyleElm1()}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标签风格">
|
||||
${labelStyleElm2()}
|
||||
</DIV-cy-tab-pane>
|
||||
</DIV-cy-tabs>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
2214
src/Obj/Base/CircleObject/index.js
Normal file
2214
src/Obj/Base/CircleObject/index.js
Normal file
File diff suppressed because it is too large
Load Diff
64
src/Obj/Base/Corridor/index.js
Normal file
64
src/Obj/Base/Corridor/index.js
Normal file
@ -0,0 +1,64 @@
|
||||
// /**
|
||||
// * 走廊
|
||||
// */
|
||||
// import Base from "../index";
|
||||
|
||||
// class Corridor extends Base {
|
||||
// /**
|
||||
// * @constructor
|
||||
// * @param sdk
|
||||
// * @param options {object} 线属性
|
||||
// * @param options.name{string} 名称
|
||||
// * @param options.image{string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} 指定 Image、URL、Canvas 或 Video 的属性
|
||||
// * @param options.width=10{number} 宽度
|
||||
// * @param options.height=0{number} 高度
|
||||
// * */
|
||||
// constructor(sdk, options = {}) {
|
||||
// super(sdk, options);
|
||||
// this.options.name = options.name || ''
|
||||
// this.options.image = options.image
|
||||
// this.options.width = options.width || 10
|
||||
// this.options.height = options.height || 0
|
||||
// }
|
||||
|
||||
// create(positions) {
|
||||
// let fromDegreesArray = []
|
||||
// for (let i = 0; i < positions.length; i++) {
|
||||
// fromDegreesArray.push(positions[i].lng, positions[i].lat)
|
||||
// }
|
||||
// this.entity = this.sdk.viewer.scene.primitives.add(new Cesium.Primitive({//GroundPrimitive贴地
|
||||
// geometryInstances: new Cesium.GeometryInstance({
|
||||
// geometry: new Cesium.CorridorGeometry({
|
||||
// positions: Cesium.Cartesian3.fromDegreesArray(fromDegreesArray),
|
||||
// width: this.options.width,
|
||||
// height: this.options.height,
|
||||
// cornerType: Cesium.CornerType.MITERED,
|
||||
// vertexFormat: Cesium.MaterialAppearance.MaterialSupport.ALL.vertexFormat
|
||||
// }),
|
||||
// attributes: {
|
||||
// color: Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(1.0, 1.0, 1.0, 0.5))
|
||||
// }
|
||||
// }),
|
||||
// appearance: new Cesium.MaterialAppearance({
|
||||
// material: Cesium.Material.fromType('Image', {
|
||||
// image: this.options.image,
|
||||
// repeat: new Cesium.Cartesian2(100, 1.0)
|
||||
// }),
|
||||
// faceForward: true,
|
||||
// renderState: {
|
||||
// blending: Cesium.BlendingState.ALPHA_BLEND
|
||||
// }
|
||||
// })
|
||||
// }));
|
||||
// }
|
||||
|
||||
// // 编辑框
|
||||
// async edit(state) { }
|
||||
|
||||
// remove() {
|
||||
// this.sdk.viewer.scene.primitives.remove(this.entity)
|
||||
// this.entity = null
|
||||
// }
|
||||
// }
|
||||
|
||||
// export default Corridor
|
||||
167
src/Obj/Base/CurvelineObject/_element.js
Normal file
167
src/Obj/Base/CurvelineObject/_element.js
Normal file
@ -0,0 +1,167 @@
|
||||
import { attributeElm, labelStyleElm1, labelStyleElm2 } from '../../Element/elm_html'
|
||||
|
||||
function html(that) {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row" style="align-items: flex-start;">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" maxlength="40" type="text" @model="name">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 56%;">
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col input-select-unit-box">
|
||||
<div class="input-select-unit"></div>
|
||||
<input class="input input-text" readonly="readonly" type="text" style="flex: 0 0 130px;" @model="length">
|
||||
<div class="input-select-unit"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<DIV-cy-tabs id="polyline-object-edit-tabs">
|
||||
<DIV-cy-tab-pane label="属性信息">
|
||||
${attributeElm(that)}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="空间信息">
|
||||
<div class="row">
|
||||
<div class="col height-mode-box">
|
||||
<span class="label" style="flex: 0 0 56px;">高度模式</span>
|
||||
<div class="height-mode"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">Z值统一增加</span>
|
||||
<div class="input-number input-number-unit-1 height-box">
|
||||
<input class="input height" type="number" title="" min="-9999999" max="999999999">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
<button class="confirm height-confirm" style="margin-left: 5px;">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="table spatial-info-table">
|
||||
<div class="table-head">
|
||||
<div class="tr">
|
||||
<div class="th"></div>
|
||||
<div class="th">经度(X)</div>
|
||||
<div class="th">纬度(Y)</div>
|
||||
<div class="th">高度(Z)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="线条风格">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">线条颜色</span>
|
||||
<div class="color"></div>
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 33%;">
|
||||
<span class="label">线条宽度</span>
|
||||
<div class="input-number input-number-unit-1" style="width: 80px;">
|
||||
<input class="input" type="number" title="" min="1" max="999" @model="lineWidth">
|
||||
<span class="unit">px</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col input-select-line-type-box" style="flex: 0 0 37%;">
|
||||
<span class="label">线条形式</span>
|
||||
<div class="input-select-line-type"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">线段缓冲</span>
|
||||
<input class="btn-switch" type="checkbox" @model="extend">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 33%;">
|
||||
<span class="label">缓冲宽度</span>
|
||||
<div class="input-number input-number-unit-1" style="width: 80px;">
|
||||
<input class="input" type="number" title="" min="0" data-min="0.01" max="999999" @model="extendWidth">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 37%;">
|
||||
<span class="label">缓冲颜色</span>
|
||||
<div class="extendColor"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" id="dashTextureDom">
|
||||
<div class="col">
|
||||
<span class="label">动画顺向</span>
|
||||
<input class="btn-switch" type="checkbox" @model="rotate">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 33%;">
|
||||
<span class="label">流动速率</span>
|
||||
<div class="input-number input-number-unit-1" style="width: 80px;">
|
||||
<input class="input" type="number" title="" min="0" max="999999" step="1" @model="speed">
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 37%;">
|
||||
<span class="label lineSpace">间距</span>
|
||||
<div class="input-number input-number-unit-1 lineSpace" style="width: 80px;">
|
||||
<input class="input" type="number" title="" min="0" max="4.5" step="0.1" @model="space">
|
||||
<span class="unit">倍</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">首尾相连</span>
|
||||
<input class="btn-switch" type="checkbox" @model="noseToTail">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 33%;">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 37%;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标注风格">
|
||||
${labelStyleElm1()}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标签风格">
|
||||
${labelStyleElm2()}
|
||||
</DIV-cy-tab-pane>
|
||||
<!-- <DIV-cy-tab-pane label="灯光控制">-->
|
||||
<!-- <div>-->
|
||||
<!-- <div class="row">-->
|
||||
<!-- <div class="col">-->
|
||||
<!-- <span class="label">指令</span>-->
|
||||
<!-- <input class="input" type="text" @model="instruct">-->
|
||||
<!-- <button class="primary" @click="instructSubmit">提交</button>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </DIV-cy-tab-pane>-->
|
||||
<!-- <DIV-cy-tab-pane label="设置操作点">-->
|
||||
<!-- <div>-->
|
||||
<!-- <div class="row">-->
|
||||
<!-- <div class="col">-->
|
||||
<!-- <span class="label">设置操作点</span>-->
|
||||
<!-- <input class="input" type="text" @model="operatingPoint">-->
|
||||
<!-- <button class="primary" @click="operatingPointSubmit">提交</button>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </DIV-cy-tab-pane>-->
|
||||
</DIV-cy-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
120
src/Obj/Base/CurvelineObject/eventBinding.js
Normal file
120
src/Obj/Base/CurvelineObject/eventBinding.js
Normal file
@ -0,0 +1,120 @@
|
||||
class eventBinding {
|
||||
constructor() {
|
||||
this.element = {}
|
||||
}
|
||||
static event = {}
|
||||
|
||||
getEvent(name) {
|
||||
return eventBinding.event[name]
|
||||
}
|
||||
|
||||
getEventAll() {
|
||||
return eventBinding.event
|
||||
}
|
||||
|
||||
setEvent(name, event) {
|
||||
eventBinding.event[name] = event
|
||||
}
|
||||
|
||||
on(that, elements) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let Event = []
|
||||
let isEvent = false
|
||||
let removeName = []
|
||||
if (!elements[i] ||!elements[i].attributes) {
|
||||
continue
|
||||
}
|
||||
for (let m of elements[i].attributes) {
|
||||
switch (m.name) {
|
||||
case '@model': {
|
||||
isEvent = true
|
||||
if (elements[i].type == 'checkbox') {
|
||||
Event.push((e) => { that[m.value] = e.target.checked })
|
||||
elements[i].checked = that[m.value]
|
||||
}
|
||||
else {
|
||||
Event.push((e) => {
|
||||
let value = e.target.value
|
||||
if (e.target.type == 'number') {
|
||||
if (e.data != '.' && (e.data != '-' || e.target.value)) {
|
||||
value = Number(value)
|
||||
if((e.target.max) && value>Number(e.target.max)) {
|
||||
value = Number(e.target.max)
|
||||
}
|
||||
if((e.target.min) && value<Number(e.target.min)) {
|
||||
value = Number(e.target.min)
|
||||
}
|
||||
if((e.target.dataset.min) && value<Number(e.target.dataset.min)) {
|
||||
value = Number(e.target.dataset.min)
|
||||
}
|
||||
that[m.value] = value
|
||||
}
|
||||
}
|
||||
else {
|
||||
that[m.value] = value
|
||||
}
|
||||
})
|
||||
if(elements[i].nodeName=='IMG') {
|
||||
elements[i].src = that[m.value]
|
||||
}
|
||||
else {
|
||||
elements[i].value = that[m.value]
|
||||
}
|
||||
}
|
||||
if(this.element[m.value]) {
|
||||
this.element[m.value].push(elements[i])
|
||||
}
|
||||
else {
|
||||
this.element[m.value] = [elements[i]]
|
||||
}
|
||||
removeName.push(m.name)
|
||||
break;
|
||||
}
|
||||
case '@click': {
|
||||
elements[i].addEventListener('click', (e) => {
|
||||
if (typeof (that[m.value]) === 'function') {
|
||||
that[m.value](e)
|
||||
}
|
||||
});
|
||||
removeName.push(m.name)
|
||||
// elements[i].attributes.removeNamedItem(m.name)
|
||||
break;
|
||||
}
|
||||
case '@change': {
|
||||
isEvent = true
|
||||
Event.push((e) => {
|
||||
let value = e.target.value
|
||||
if(e.target.type == 'number' && value!='') {
|
||||
value = Number(value)
|
||||
e.target.value = value
|
||||
}
|
||||
if (typeof (that[m.value]) === 'function') {
|
||||
that[m.value](e, value)
|
||||
}
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
// elements[i].attributes[m] = undefined
|
||||
}
|
||||
for(let n=0;n<removeName.length;n++) {
|
||||
elements[i].attributes.removeNamedItem(removeName[n])
|
||||
}
|
||||
|
||||
if (isEvent) {
|
||||
let ventType = 'input'
|
||||
if (elements[i].tagName != 'INPUT' || elements[i].type == 'checkbox') {
|
||||
ventType = 'change'
|
||||
}
|
||||
elements[i].addEventListener(ventType, (e) => {
|
||||
for (let t = 0; t < Event.length; t++) {
|
||||
Event[t](e)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const EventBinding = new eventBinding();
|
||||
export default EventBinding;
|
||||
3322
src/Obj/Base/CurvelineObject/index.js
Normal file
3322
src/Obj/Base/CurvelineObject/index.js
Normal file
File diff suppressed because it is too large
Load Diff
87
src/Obj/Base/EllipseObject/_element.js
Normal file
87
src/Obj/Base/EllipseObject/_element.js
Normal file
@ -0,0 +1,87 @@
|
||||
import { attributeElm, labelStyleElm1, labelStyleElm2 } from '../../Element/elm_html'
|
||||
|
||||
function html(that) {
|
||||
return `
|
||||
<div class="row" style="align-items: flex-start;">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" maxlength="40" type="text" @model="name">
|
||||
</div>
|
||||
<div class="col" style="flex: 0 0 60%;">
|
||||
<div class="row">
|
||||
<div class="col input-select-unit-box">
|
||||
<span class="label" style="margin-right: 0px;">投影面积:</span>
|
||||
<input class="input input-text" readonly="readonly" type="text" @model="area">
|
||||
<div class="input-select-unit"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<DIV-cy-tabs id="circle-object-edit-tabs">
|
||||
<DIV-cy-tab-pane label="属性信息">
|
||||
${attributeElm(that)}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="空间信息">
|
||||
<div class="row">
|
||||
<div class="col height-mode-box">
|
||||
<span class="label" style="flex: 0 0 56px;">高度模式</span>
|
||||
<div class="height-mode"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">Z值统一增加</span>
|
||||
<div class="input-number input-number-unit-1 height-box">
|
||||
<input class="input height" type="number" title="" min="-9999999" max="999999999">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
<button class="confirm height-confirm" style="margin-left: 5px;">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="table spatial-info-table">
|
||||
<div class="table-head">
|
||||
<div class="tr">
|
||||
<div class="th"></div>
|
||||
<div class="th">经度(X)</div>
|
||||
<div class="th">纬度(Y)</div>
|
||||
<div class="th">高度(Z)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="面风格">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">面颜色</span>
|
||||
<div class="color"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">边线颜色</span>
|
||||
<div class="lineColor"></div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">边线宽度</span>
|
||||
<div class="input-number input-number-unit-2" style="width: 80px;">
|
||||
<input class="input" type="number" title="" min="0" max="99" @model="lineWidth">
|
||||
<span class="unit">px</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标注风格">
|
||||
${labelStyleElm1()}
|
||||
</DIV-cy-tab-pane>
|
||||
<DIV-cy-tab-pane label="标签风格">
|
||||
${labelStyleElm2()}
|
||||
</DIV-cy-tab-pane>
|
||||
</DIV-cy-tabs>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
2205
src/Obj/Base/EllipseObject/index.js
Normal file
2205
src/Obj/Base/EllipseObject/index.js
Normal file
File diff suppressed because it is too large
Load Diff
51
src/Obj/Base/Explosion/_element.js
Normal file
51
src/Obj/Base/Explosion/_element.js
Normal file
@ -0,0 +1,51 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" maxlength="40" type="text" @model="name">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">随地图缩放</span>
|
||||
<input class="btn-switch" type="checkbox" @model="scaleByDistance">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">经度</span>
|
||||
<input class="input" type="number" title="" title="" min="-180" max="180" @model="lng">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">爆炸范围</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="1" max="999999" @model="size">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">纬度</span>
|
||||
<input class="input" type="number" title="" min="-90" max="90" @model="lat">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">高度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="-9999999" max="999999999" @model="alt">
|
||||
<span class="unit">m</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
391
src/Obj/Base/Explosion/index.js
Normal file
391
src/Obj/Base/Explosion/index.js
Normal file
@ -0,0 +1,391 @@
|
||||
import Base from "../index";
|
||||
import Dialog from '../../Element/Dialog';
|
||||
import EventBinding from '../../Element/Dialog/eventBinding';
|
||||
import { html } from "./_element";
|
||||
import MouseEvent from '../../../Event/index'
|
||||
import { syncData } from '../../../Global/MultiViewportMode'
|
||||
import MouseTip from '../../../MouseTip'
|
||||
import { setSplitDirection, syncSplitData, setActiveId } from '../../../Global/SplitScreen'
|
||||
import { setActiveViewer, closeRotateAround, closeViewFollow, getGroundCover} from '../../../Global/global'
|
||||
class Explosion extends Base {
|
||||
/**
|
||||
* @constructor
|
||||
* @description 爆炸
|
||||
* @param sdk
|
||||
* @param options {object} 爆炸属性
|
||||
* @param options.id {string} 唯一标识
|
||||
* @param options.show=true {boolean} 显隐
|
||||
* @param options.name {string} 名称
|
||||
* @param {object} options.position={} 位置
|
||||
* @param {number} options.position.lng 经度
|
||||
* @param {number} options.position.lat 纬度
|
||||
* @param {number} options.position.alt 高度
|
||||
* @param options.scaleByDistance=true {boolean} 是否开启跟随视野缩放
|
||||
* @param options.size=80 {number} 大小(爆炸范围)
|
||||
* */
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
super(sdk, options);
|
||||
this.viewer = sdk.viewer
|
||||
this.options.show = (options.show || options.show === false) ? options.show : true
|
||||
this.options.name = this.options.name || '未命名对象'
|
||||
this.options.size = (this.options.size || this.options.size === 0) ? this.options.size : 80
|
||||
this.options.scaleByDistance = (options.scaleByDistance || options.scaleByDistance === false) ? options.scaleByDistance : true
|
||||
this.event = new MouseEvent(this.sdk)
|
||||
this.Dialog = _Dialog
|
||||
this.operate = {}
|
||||
this._elms = {};
|
||||
this._EventBinding = new EventBinding()
|
||||
this.sdk.addIncetance(this.options.id, this)
|
||||
Explosion.create(this)
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'Explosion'
|
||||
}
|
||||
|
||||
// 创建
|
||||
static create(that) {
|
||||
let img_bz = []
|
||||
for (let i = 10001; i <= 10120; i++) {
|
||||
let src = that.getSourceRootPath() + `/img/frameAnimation/explosion/b${i}.png`
|
||||
img_bz.push(src)
|
||||
}
|
||||
let i = 0
|
||||
let flyEntity = new Cesium.Entity({
|
||||
id: that.options.id,
|
||||
show: that.options.show,
|
||||
position: new Cesium.CallbackProperty(() => {
|
||||
return Cesium.Cartesian3.fromDegrees(that.options.position.lng, that.options.position.lat, that.options.position.alt)
|
||||
}, false),
|
||||
billboard: {
|
||||
image: new Cesium.CallbackProperty(() => {
|
||||
let img = img_bz[flyEntity.imgIndex]
|
||||
flyEntity.imgIndex++
|
||||
if (flyEntity.imgIndex >= img_bz.length) {
|
||||
flyEntity.imgIndex = 0
|
||||
}
|
||||
return img
|
||||
}, false),
|
||||
// scale: that.options.size,
|
||||
disableDepthTestDistance: new Cesium.CallbackProperty(function () {
|
||||
return getGroundCover() ? undefined : Number.POSITIVE_INFINITY
|
||||
}, false),
|
||||
width: that.options.size,
|
||||
height: that.options.size,
|
||||
sizeInMeters: that.options.scaleByDistance,
|
||||
pixelOffset: { x: 0, y: -20 }
|
||||
},
|
||||
});
|
||||
flyEntity.imgIndex = 0
|
||||
that.entity = that.viewer.entities.add(flyEntity)
|
||||
syncData(that.sdk, that.options.id)
|
||||
if(that.options.show) {
|
||||
setSplitDirection(0, that.options.id)
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑框
|
||||
async edit(state) {
|
||||
let _this = this
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
if (state) {
|
||||
this._DialogObject = await new Dialog(this.sdk, this.options, {
|
||||
title: '爆炸属性', left: '180px', top: '100px',
|
||||
confirmCallBack: (options) => {
|
||||
this.name = this.name.trim()
|
||||
if(!this.name) {
|
||||
this.name = '未命名对象'
|
||||
}
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
this._DialogObject.close()
|
||||
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.originalOptions)
|
||||
syncData(this.sdk, this.options.id)
|
||||
syncSplitData(this.sdk, this.options.id)
|
||||
},
|
||||
resetCallBack: () => {
|
||||
this.reset()
|
||||
this.Dialog.resetCallBack && this.Dialog.resetCallBack()
|
||||
},
|
||||
removeCallBack: () => {
|
||||
this.Dialog.removeCallBack && this.Dialog.removeCallBack()
|
||||
},
|
||||
closeCallBack: () => {
|
||||
this.reset()
|
||||
this.positionEditing = false
|
||||
this.Dialog.closeCallBack && this.Dialog.closeCallBack()
|
||||
},
|
||||
showCallBack: (show) => {
|
||||
this.options.show = show
|
||||
this.originalOptions.show = show
|
||||
this.show = show
|
||||
this.Dialog.showCallBack && this.Dialog.showCallBack()
|
||||
},
|
||||
translationalCallBack: () => {
|
||||
this.positionEditing = !this.positionEditing
|
||||
},
|
||||
})
|
||||
this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' explosion'
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
this._DialogObject.contentAppChild(contentElm)
|
||||
|
||||
let all_elm = contentElm.getElementsByTagName("*")
|
||||
this._EventBinding.on(this, all_elm)
|
||||
this._elms = this._EventBinding.element
|
||||
this.scaleByDistance = this.options.scaleByDistance
|
||||
}
|
||||
}
|
||||
|
||||
async flyTo(options = {}) {
|
||||
setActiveViewer(0)
|
||||
closeRotateAround(this.sdk)
|
||||
closeViewFollow(this.sdk)
|
||||
|
||||
if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) {
|
||||
let orientation = {
|
||||
heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0),
|
||||
pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0),
|
||||
roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0)
|
||||
}
|
||||
|
||||
let lng = this.options.customView.relativePosition.lng
|
||||
let lat = this.options.customView.relativePosition.lat
|
||||
let alt = this.options.customView.relativePosition.alt
|
||||
let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
|
||||
let position = { lng: 0, lat: 0 }
|
||||
if (this.options.position) {
|
||||
position = { ...this.options.position }
|
||||
}
|
||||
else if (this.options.positions) {
|
||||
position = { ...this.options.positions[0] }
|
||||
}
|
||||
else if (this.options.center) {
|
||||
position = { ...this.options.center }
|
||||
}
|
||||
else if (this.options.start) {
|
||||
position = { ...this.options.start }
|
||||
}
|
||||
else {
|
||||
if (this.options.hasOwnProperty('lng')) {
|
||||
position.lng = this.options.lng
|
||||
}
|
||||
if (this.options.hasOwnProperty('lat')) {
|
||||
position.lat = this.options.lat
|
||||
}
|
||||
if (this.options.hasOwnProperty('alt')) {
|
||||
position.alt = this.options.alt
|
||||
}
|
||||
}
|
||||
// 如果没有高度值,则获取紧贴高度计算
|
||||
if (!position.hasOwnProperty('alt')) {
|
||||
position.alt = await this.getClampToHeight(position)
|
||||
}
|
||||
lng = this.options.customView.relativePosition.lng + position.lng
|
||||
lat = this.options.customView.relativePosition.lat + position.lat
|
||||
alt = this.options.customView.relativePosition.alt + position.alt
|
||||
destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
this.sdk.viewer.camera.flyTo({
|
||||
destination: destination,
|
||||
orientation: orientation
|
||||
})
|
||||
}
|
||||
else {
|
||||
this.sdk.viewer.camera.flyTo({
|
||||
destination: Cesium.Cartesian3.fromDegrees(this.options.position.lng, this.options.position.lat, this.options.position.alt + (this.options.size * 8)),
|
||||
orientation: options.orientation || {
|
||||
heading: Cesium.Math.toRadians(0.0),
|
||||
pitch: Cesium.Math.toRadians(-85.0),
|
||||
roll: Cesium.Math.toRadians(0.0)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (!this.entity) {
|
||||
return
|
||||
}
|
||||
this.previous = null
|
||||
this.options = this.deepCopyObj(this.originalOptions)
|
||||
this.name = this.originalOptions.name
|
||||
this.size = this.originalOptions.size
|
||||
this.scaleByDistance = this.originalOptions.scaleByDistance
|
||||
this.lng = this.options.position.lng
|
||||
this.lat = this.options.position.lat
|
||||
}
|
||||
|
||||
get scaleByDistance() {
|
||||
return this.options.scaleByDistance
|
||||
}
|
||||
set scaleByDistance(v) {
|
||||
this.options.scaleByDistance = v
|
||||
this.entity.billboard.sizeInMeters = v
|
||||
this._elms.scaleByDistance && this._elms.scaleByDistance.forEach((item) => {
|
||||
item.checked = v
|
||||
})
|
||||
}
|
||||
|
||||
get lng() {
|
||||
return this.options.position.lng
|
||||
}
|
||||
|
||||
set lng(v) {
|
||||
this.options.position.lng = v
|
||||
this._elms.lng && this._elms.lng.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get lat() {
|
||||
return this.options.position.lat
|
||||
}
|
||||
|
||||
set lat(v) {
|
||||
this.options.position.lat = v
|
||||
this._elms.lat && this._elms.lat.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get alt() {
|
||||
return this.options.position.alt
|
||||
}
|
||||
|
||||
set alt(v) {
|
||||
this.options.position.alt = v
|
||||
this._elms.alt && this._elms.alt.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.options.size
|
||||
}
|
||||
set size(v) {
|
||||
this.options.size = v
|
||||
this.entity.billboard.width = this.options.size
|
||||
this.entity.billboard.height = this.options.size
|
||||
this._elms.size && this._elms.size.forEach((item) => {
|
||||
item.value = v
|
||||
})
|
||||
}
|
||||
|
||||
/**@desc 打开平移功能
|
||||
*
|
||||
* @memberOf Source
|
||||
* @param status {boolean}
|
||||
*
|
||||
* */
|
||||
set positionEditing(status) {
|
||||
if (YJ.Measure.GetMeasureStatus() || !this.sdk || !this.sdk.viewer || !this.entity) {
|
||||
return
|
||||
}
|
||||
this.operate.positionEditing = status
|
||||
if (status === true) {
|
||||
this.tip && this.tip.destroy()
|
||||
this.tip = new MouseTip('点击鼠标左键确认,右键取消', this.sdk)
|
||||
this.previous = {
|
||||
position: { ...this.options.position }
|
||||
}
|
||||
this.event.mouse_move((movement, cartesian) => {
|
||||
let positions = this.cartesian3Towgs84(cartesian, this.sdk.viewer)
|
||||
this.lng = positions.lng
|
||||
this.lat = positions.lat
|
||||
this.alt = positions.alt
|
||||
this.tip.setPosition(
|
||||
cartesian,
|
||||
movement.endPosition.x,
|
||||
movement.endPosition.y
|
||||
)
|
||||
})
|
||||
this.event.mouse_left((movement, cartesian) => {
|
||||
let positions = this.cartesian3Towgs84(cartesian, this.sdk.viewer)
|
||||
this.lng = positions.lng
|
||||
this.lat = positions.lat
|
||||
this.alt = positions.alt
|
||||
this.previous = {
|
||||
position: { ...this.options.position }
|
||||
}
|
||||
this.event.mouse_move(() => { })
|
||||
this.event.mouse_left(() => { })
|
||||
this.event.mouse_right(() => { })
|
||||
this.event.gesture_pinck_start(() => { })
|
||||
this.event.gesture_pinck_end(() => { })
|
||||
this.positionEditing = false
|
||||
})
|
||||
this.event.mouse_right((movement, cartesian) => {
|
||||
this.positionEditing = false
|
||||
})
|
||||
|
||||
this.event.gesture_pinck_start((movement, cartesian) => {
|
||||
let startTime = new Date()
|
||||
this.event.gesture_pinck_end(() => {
|
||||
let endTime = new Date()
|
||||
if (endTime - startTime >= 500) {
|
||||
this.positionEditing = false
|
||||
}
|
||||
else {
|
||||
let positions = this.cartesian3Towgs84(cartesian, this.sdk.viewer)
|
||||
this.lng = positions.lng
|
||||
this.lat = positions.lat
|
||||
this.alt = positions.alt
|
||||
this.previous = {
|
||||
position: { ...this.options.position }
|
||||
}
|
||||
this.event.mouse_move(() => { })
|
||||
this.event.mouse_left(() => { })
|
||||
this.event.mouse_right(() => { })
|
||||
this.event.gesture_pinck_start(() => { })
|
||||
this.event.gesture_pinck_end(() => { })
|
||||
this.positionEditing = false
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
if (this.event) {
|
||||
this.event.mouse_move(() => { })
|
||||
this.event.mouse_left(() => { })
|
||||
this.event.mouse_right(() => { })
|
||||
this.event.gesture_pinck_start(() => { })
|
||||
this.event.gesture_pinck_end(() => { })
|
||||
}
|
||||
this.tip && this.tip.destroy()
|
||||
if (this.previous) {
|
||||
this.lng = this.previous.position.lng
|
||||
this.lat = this.previous.position.lat
|
||||
this.alt = this.previous.position.alt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get positionEditing() {
|
||||
return this.operate.positionEditing
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
async remove() {
|
||||
this.viewer.entities.remove(this.entity)
|
||||
this.entity = null
|
||||
if (this._DialogObject && !this._DialogObject.isDestroy) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
this.tip && this.tip.destroy()
|
||||
this.event && this.event.destroy()
|
||||
await this.sdk.removeIncetance(this.options.id)
|
||||
await syncData(this.sdk, this.options.id)
|
||||
}
|
||||
|
||||
flicker() {}
|
||||
}
|
||||
|
||||
export default Explosion
|
||||
76
src/Obj/Base/FlowLine/_element.js
Normal file
76
src/Obj/Base/FlowLine/_element.js
Normal file
@ -0,0 +1,76 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" maxlength="40" type="text" @model="name">
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">颜色</span>
|
||||
<div class="flowLine-color"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">飞线数量</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="1" max="99999" @model="pointNumber">
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">飞线宽度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" max="99999" min="1" step="1" @model="width">
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
<span class="label">飞线高度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" min="0" max="999999" step="1" @model="height">
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">飞线高度差</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" max="99999" min="0" step="1" @model="heightDifference">
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">单次运动时长(s)</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" max="999999999" min="1" step="1" @model="duration">
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">轨迹透明度</span>
|
||||
<div class="input-number input-number-unit-1">
|
||||
<input class="input" type="number" title="" max="1" min="0.01" step="0.01" @model="lineBackAlpha">
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
92
src/Obj/Base/FlowLine/eventBinding.js
Normal file
92
src/Obj/Base/FlowLine/eventBinding.js
Normal file
@ -0,0 +1,92 @@
|
||||
class eventBinding {
|
||||
constructor() {
|
||||
this.element = {}
|
||||
}
|
||||
static event = {}
|
||||
|
||||
getEvent(name) {
|
||||
return eventBinding.event[name]
|
||||
}
|
||||
|
||||
getEventAll() {
|
||||
return eventBinding.event
|
||||
}
|
||||
|
||||
setEvent(name, event) {
|
||||
eventBinding.event[name] = event
|
||||
}
|
||||
|
||||
on(that, elements) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let Event = []
|
||||
let isEvent = false
|
||||
let removeName = []
|
||||
if (!elements[i] || !elements[i].attributes) {
|
||||
continue;
|
||||
}
|
||||
for (let m of elements[i].attributes) {
|
||||
switch (m.name) {
|
||||
case '@model': {
|
||||
isEvent = true
|
||||
if (elements[i].type == 'checkbox') {
|
||||
Event.push((e) => { that[m.value] = e.target.checked })
|
||||
elements[i].checked = that[m.value]
|
||||
}
|
||||
else {
|
||||
Event.push((e) => {
|
||||
let value = e.target.value
|
||||
if (e.target.type == 'number') {
|
||||
value = Number(value)
|
||||
}
|
||||
that[m.value] = value
|
||||
})
|
||||
if (elements[i].nodeName == 'IMG') {
|
||||
elements[i].src = that[m.value]
|
||||
}
|
||||
else {
|
||||
elements[i].value = that[m.value]
|
||||
}
|
||||
}
|
||||
if (this.element[m.value]) {
|
||||
this.element[m.value].push(elements[i])
|
||||
}
|
||||
else {
|
||||
this.element[m.value] = [elements[i]]
|
||||
}
|
||||
removeName.push(m.name)
|
||||
break;
|
||||
}
|
||||
case '@click': {
|
||||
elements[i].addEventListener('click', (e) => {
|
||||
if (typeof (that.Dialog[m.value]) === 'function') {
|
||||
that.Dialog[m.value](e)
|
||||
}
|
||||
});
|
||||
removeName.push(m.name)
|
||||
// elements[i].attributes.removeNamedItem(m.name)
|
||||
break;
|
||||
}
|
||||
}
|
||||
// elements[i].attributes[m] = undefined
|
||||
}
|
||||
for (let n = 0; n < removeName.length; n++) {
|
||||
elements[i].attributes.removeNamedItem(removeName[n])
|
||||
}
|
||||
|
||||
if (isEvent) {
|
||||
let ventType = 'input'
|
||||
if (elements[i].tagName != 'INPUT' || elements[i].type == 'checkbox') {
|
||||
ventType = 'change'
|
||||
}
|
||||
elements[i].addEventListener(ventType, (e) => {
|
||||
for (let t = 0; t < Event.length; t++) {
|
||||
Event[t](e)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const EventBinding = new eventBinding();
|
||||
export default EventBinding;
|
||||
504
src/Obj/Base/FlowLine/index.js
Normal file
504
src/Obj/Base/FlowLine/index.js
Normal file
@ -0,0 +1,504 @@
|
||||
/**
|
||||
* @description 水面
|
||||
*/
|
||||
import Dialog from '../../Element/Dialog';
|
||||
import { html } from "./_element";
|
||||
import EventBinding from '../../Element/Dialog/eventBinding';
|
||||
import Base from "../index";
|
||||
import { syncData } from '../../../Global/MultiViewportMode'
|
||||
import DrawRect from '../../../Draw/drawRect'
|
||||
import drawPolygon from '../../../Draw/drawPolygon'
|
||||
import { setSplitDirection, syncSplitData, setActiveId } from '../../../Global/SplitScreen'
|
||||
import { setActiveViewer, closeRotateAround, closeViewFollow } from '../../../Global/global'
|
||||
import FlowLineMaterialProperty from "../../Materail/FlowLineMaterialProperty";
|
||||
|
||||
class FlowLine extends Base {
|
||||
/**
|
||||
* @constructor
|
||||
* @param sdk
|
||||
* @description 流光飞线
|
||||
* @param options {object} 流光飞线属性
|
||||
* @param options.name=未命名对象 {string} 名称
|
||||
* @param options.pointNumber=300 {number} 线数量
|
||||
* @param options.height=200 {number} 线高度
|
||||
* @param options.heightDifference=3000 {number} 线高度差
|
||||
* @param options.width=2 {number} 线宽
|
||||
* @param options.duration=10.0 {number} 单次运动时间
|
||||
* @param options.color=rgba(255,255,255,1) {string} 颜色
|
||||
* @param options.lineBackAlpha=0.05 {number} 轨迹颜色(不能为0)
|
||||
* @param Dialog {object} 弹框对象
|
||||
* @param Dialog.confirmCallBack {function} 弹框确认时的回调
|
||||
* */
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
super(sdk, options);
|
||||
this.viewer = this.sdk.viewer
|
||||
this.options.name = options.name || '飞线'
|
||||
this.options.pointNumber = options.pointNumber || 200
|
||||
this.options.height = options.height || 200
|
||||
this.options.heightDifference = options.heightDifference || 3000
|
||||
this.options.width = options.width || 2
|
||||
this.options.duration = options.duration || 10.0
|
||||
this.options.color = options.color || "rgba(255,255,255,1)"
|
||||
this.options.lineBackAlpha = options.lineBackAlpha || 0.05
|
||||
this.options.positions = options.positions || []
|
||||
this.options.show = (options.show || options.show === false) ? options.show : true
|
||||
this.Dialog = _Dialog
|
||||
this._EventBinding = new EventBinding()
|
||||
this._elms = {};
|
||||
this.positionArea = []
|
||||
this.positions = []
|
||||
this.sdk.addIncetance(this.options.id, this)
|
||||
// FlowLine.create(this)
|
||||
FlowLine.drawLine(this)
|
||||
}
|
||||
|
||||
// 创建水面
|
||||
static create(that) {
|
||||
// let Draw = new DrawRect(that.sdk)
|
||||
// Draw.start((a, positions) => {
|
||||
// that.positions = positions
|
||||
// that.getLine(that, that.positions)
|
||||
// that.edit(true)
|
||||
// })
|
||||
|
||||
let Draw = new drawPolygon(that.sdk)
|
||||
Draw.start((a, positions) => {
|
||||
that.positionArea = positions
|
||||
let posis = that.getRandomPointsInCesiumPolygon(positions, that.options.pointNumber)
|
||||
that.positions = posis
|
||||
that.getLine(that, posis)
|
||||
that.edit(true)
|
||||
})
|
||||
}
|
||||
static drawLine(that) {
|
||||
that.positionArea = that.options.positions
|
||||
let posis = that.getRandomPointsInCesiumPolygon(that.options.positions, that.options.pointNumber)
|
||||
that.positions = posis
|
||||
that.getLine(that, posis)
|
||||
// that.edit(true)
|
||||
}
|
||||
getRandomPointsInCesiumPolygon(positions, count) {
|
||||
let lons = [], lats = [], posi = []
|
||||
positions.forEach(item => {
|
||||
lons.push(item.lng)
|
||||
lats.push(item.lat)
|
||||
posi.push([item.lng, item.lat])
|
||||
})
|
||||
posi.push([posi[0][0], posi[0][1]])
|
||||
const minLon = Math.min(...lons), maxLon = Math.max(...lons);
|
||||
const minLat = Math.min(...lats), maxLat = Math.max(...lats);
|
||||
|
||||
const points = [];
|
||||
let that = this
|
||||
while (points.length < count) {
|
||||
const lon = minLon + Math.random() * (maxLon - minLon);
|
||||
const lat = minLat + Math.random() * (maxLat - minLat);
|
||||
// const cartesian = Cesium.Cartesian3.fromDegrees(lon, lat);
|
||||
let point = turf.point([lon, lat]);
|
||||
const polygon = turf.polygon([
|
||||
posi
|
||||
]);
|
||||
|
||||
const isInside = turf.booleanPointInPolygon(point, polygon);
|
||||
if (isInside) {
|
||||
let posi = Cesium.Cartesian3.fromDegrees(lon, lat);
|
||||
const cartographic = that.viewer.scene.globe.ellipsoid.cartesianToCartographic(posi);
|
||||
const height = cartographic.height;
|
||||
|
||||
points.push([
|
||||
lon,
|
||||
lat,
|
||||
height
|
||||
]);
|
||||
}
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
getLine(that, positions) {
|
||||
let num = 0
|
||||
let celiangEntity = null
|
||||
if (that.viewer.entities.getById(that.options.id)) {
|
||||
that.viewer.entities.getById(that.options.id)._children.forEach((item) => {
|
||||
that.viewer.entities.remove(item);
|
||||
});
|
||||
that.viewer.entities.remove(that.viewer.entities.getById(that.options.id))
|
||||
}
|
||||
celiangEntity = that.viewer.entities.add(new Cesium.Entity({ id: that.options.id, show: that.options.show }))
|
||||
|
||||
positions.forEach((item, index) => {
|
||||
let point = item
|
||||
//根据点设置起始点位置
|
||||
// let start = Cesium.Cartesian3.fromDegrees(point[0], point[1], 0)
|
||||
let start = Cesium.Cartesian3.fromDegrees(point[0], point[1], point[2])
|
||||
//根据点设置结束点位置
|
||||
let end = Cesium.Cartesian3.fromDegrees(point[0], point[1], point[2] + that.options.height + Math.random() * that.options.heightDifference)
|
||||
//创建线
|
||||
that.viewer.entities.add({
|
||||
parent: celiangEntity,
|
||||
id: that.options.id + '-' + new Date().getTime() + index,
|
||||
polyline: {
|
||||
positions: [start, end],
|
||||
width: that.options.width,
|
||||
// material:Cesium.Color.RED
|
||||
material: new Cesium.FlowLineMaterialProperty({
|
||||
color: that.options.color,
|
||||
duration: that.options.duration,
|
||||
lineBackAlpha: that.options.lineBackAlpha,
|
||||
num: num
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
get color() {
|
||||
return this.options.color
|
||||
}
|
||||
|
||||
set color(v) {
|
||||
this.options.color = v
|
||||
let entity = this.viewer.entities.getById(this.options.id)
|
||||
if (entity) {
|
||||
entity._children.forEach(item => {
|
||||
item.polyline.material.color = Cesium.Color.fromCssColorString(v)
|
||||
})
|
||||
}
|
||||
|
||||
if (this._elms.color) {
|
||||
this._elms.color.forEach((item, i) => {
|
||||
let picker = new YJColorPicker({
|
||||
el: item.el,
|
||||
size: 'mini',//颜色box类型
|
||||
alpha: true,//是否开启透明度
|
||||
defaultColor: v,
|
||||
disabled: false,//是否禁止打开颜色选择器
|
||||
openPickerAni: 'opacity',//打开颜色选择器动画
|
||||
sure: (c) => {
|
||||
this.color = c
|
||||
},//点击确认按钮事件回调
|
||||
clear: () => {
|
||||
this.color = 'rgba(255,255,255,1)'
|
||||
},//点击清空按钮事件回调
|
||||
})
|
||||
this._elms.color[i] = picker
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
get pointNumber() {
|
||||
return this.options.pointNumber
|
||||
}
|
||||
set pointNumber(v) {
|
||||
this.options.pointNumber = v
|
||||
let entity = this.viewer.entities.getById(this.options.id)
|
||||
if (entity) {
|
||||
let posis = this.getRandomPointsInCesiumPolygon(this.positionArea, this.options.pointNumber)
|
||||
this.positions = posis
|
||||
this.getLine(this, posis)
|
||||
}
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this.options.height
|
||||
}
|
||||
|
||||
set height(v) {
|
||||
this.options.height = v
|
||||
let entity = this.viewer.entities.getById(this.options.id)
|
||||
if (entity) {
|
||||
this.getLine(this, this.positions)
|
||||
}
|
||||
}
|
||||
get show() {
|
||||
return this.options.show
|
||||
}
|
||||
|
||||
set show(v) {
|
||||
this.options.show = v
|
||||
let entity = this.viewer.entities.getById(this.options.id)
|
||||
if (entity) {
|
||||
entity.show = v
|
||||
}
|
||||
}
|
||||
|
||||
get heightDifference() {
|
||||
return this.options.heightDifference
|
||||
}
|
||||
|
||||
set heightDifference(v) {
|
||||
this.options.heightDifference = v
|
||||
let entity = this.viewer.entities.getById(this.options.id)
|
||||
if (entity) {
|
||||
this.getLine(this, this.positions)
|
||||
}
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this.options.width
|
||||
}
|
||||
|
||||
set width(v) {
|
||||
this.options.width = v
|
||||
let entity = this.viewer.entities.getById(this.options.id)
|
||||
if (entity) {
|
||||
entity._children.forEach(item => {
|
||||
item.polyline.width = v
|
||||
})
|
||||
}
|
||||
}
|
||||
get duration() {
|
||||
return this.options.duration
|
||||
}
|
||||
set duration(v) {
|
||||
this.options.duration = v
|
||||
let entity = this.viewer.entities.getById(this.options.id)
|
||||
if (entity) {
|
||||
entity._children.forEach(item => {
|
||||
item.polyline.material.duration = v
|
||||
})
|
||||
}
|
||||
}
|
||||
get lineBackAlpha() {
|
||||
return this.options.lineBackAlpha
|
||||
}
|
||||
set lineBackAlpha(v) {
|
||||
this.options.lineBackAlpha = v
|
||||
let entity = this.viewer.entities.getById(this.options.id)
|
||||
if (entity) {
|
||||
entity._children.forEach(item => {
|
||||
item.polyline.material.lineBackAlpha = v
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 编辑框
|
||||
* @param state=false {boolean} 状态: true打开, false关闭
|
||||
*/
|
||||
async edit(state = false) {
|
||||
let _this = this
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
|
||||
// let elms = this.sdk.viewer._container.getElementsByClassName('YJ-custom-base-dialog')
|
||||
// for (let i = elms.length - 1; i >= 0; i--) {
|
||||
// this.sdk.viewer._container.removeChild(elms[i])
|
||||
// }
|
||||
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
|
||||
if (state) {
|
||||
this._DialogObject = await new Dialog(this.sdk, this.originalOptions, {
|
||||
title: '飞线属性', left: '180px', top: '100px',
|
||||
confirmCallBack: (options) => {
|
||||
this.name = this.name.trim()
|
||||
if (!this.name) {
|
||||
// this.name = '未命名对象'
|
||||
this.name = '飞线'
|
||||
}
|
||||
this.originalOptions = this.deepCopyObj(this.options)
|
||||
this._DialogObject.close()
|
||||
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.originalOptions)
|
||||
syncData(this.sdk, this.options.id)
|
||||
syncSplitData(this.sdk, this.options.id)
|
||||
},
|
||||
resetCallBack: () => {
|
||||
this.reset()
|
||||
this.Dialog.resetCallBack && this.Dialog.resetCallBack()
|
||||
},
|
||||
removeCallBack: () => {
|
||||
this.Dialog.removeCallBack && this.Dialog.removeCallBack()
|
||||
},
|
||||
closeCallBack: () => {
|
||||
this.reset()
|
||||
// this.entity.style = new Cesium.Cesium3DTileStyle({
|
||||
// color: "color('rgba(255,255,255," + this.newData.transparency + ")')",
|
||||
// show: true,
|
||||
// });
|
||||
this.Dialog.closeCallBack && this.Dialog.closeCallBack()
|
||||
},
|
||||
showCallBack: (show) => {
|
||||
this.show = show
|
||||
this.Dialog.showCallBack && this.Dialog.showCallBack()
|
||||
}
|
||||
}, true)
|
||||
this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' flow-line-surface'
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.innerHTML = html()
|
||||
this._DialogObject.contentAppChild(contentElm)
|
||||
// 颜色组件
|
||||
let waterColorPicker = new YJColorPicker({
|
||||
el: contentElm.getElementsByClassName("flowLine-color")[0],
|
||||
size: 'mini',//颜色box类型
|
||||
alpha: true,//是否开启透明度
|
||||
defaultColor: this.color,
|
||||
disabled: false,//是否禁止打开颜色选择器
|
||||
openPickerAni: 'opacity',//打开颜色选择器动画
|
||||
sure: (color) => {
|
||||
this.color = color
|
||||
},//点击确认按钮事件回调
|
||||
clear: () => {
|
||||
this.color = 'rgba(255,255,255,1)'
|
||||
},//点击清空按钮事件回调
|
||||
})
|
||||
|
||||
let all_elm = contentElm.getElementsByTagName("*")
|
||||
this._EventBinding.on(this, all_elm)
|
||||
this._elms = this._EventBinding.element
|
||||
this._elms.color = [waterColorPicker]
|
||||
} else {
|
||||
// if (this._element_style) {
|
||||
// document.getElementsByTagName('head')[0].removeChild(this._element_style)
|
||||
// this._element_style = null
|
||||
// }
|
||||
// if (this._DialogObject && this._DialogObject.remove) {
|
||||
// this._DialogObject.remove()
|
||||
// this._DialogObject = null
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (!this.viewer.entities.getById(this.options.id)) {
|
||||
return
|
||||
}
|
||||
this.name = this.originalOptions.name
|
||||
this.pointNumber = this.originalOptions.pointNumber
|
||||
this.height = this.originalOptions.height
|
||||
this.heightDifference = this.originalOptions.heightDifference
|
||||
this.width = this.originalOptions.width
|
||||
this.duration = this.originalOptions.duration
|
||||
this.color = this.originalOptions.color
|
||||
this.lineBackAlpha = this.originalOptions.lineBackAlpha
|
||||
}
|
||||
|
||||
/**
|
||||
* 飞到对应实体
|
||||
*/
|
||||
async flyTo(options = {}) {
|
||||
setActiveViewer(0)
|
||||
closeRotateAround(this.sdk)
|
||||
closeViewFollow(this.sdk)
|
||||
|
||||
if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) {
|
||||
let orientation = {
|
||||
heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0),
|
||||
pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0),
|
||||
roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0)
|
||||
}
|
||||
|
||||
let lng = this.options.customView.relativePosition.lng
|
||||
let lat = this.options.customView.relativePosition.lat
|
||||
let alt = this.options.customView.relativePosition.alt
|
||||
let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
|
||||
let position = { lng: 0, lat: 0 }
|
||||
if (this.options.position) {
|
||||
position = { ...this.options.position }
|
||||
}
|
||||
else if (this.options.positions) {
|
||||
position = { ...this.options.positions[0] }
|
||||
}
|
||||
else if (this.options.center) {
|
||||
position = { ...this.options.center }
|
||||
}
|
||||
else if (this.options.start) {
|
||||
position = { ...this.options.start }
|
||||
}
|
||||
else {
|
||||
if (this.options.hasOwnProperty('lng')) {
|
||||
position.lng = this.options.lng
|
||||
}
|
||||
if (this.options.hasOwnProperty('lat')) {
|
||||
position.lat = this.options.lat
|
||||
}
|
||||
if (this.options.hasOwnProperty('alt')) {
|
||||
position.alt = this.options.alt
|
||||
}
|
||||
}
|
||||
// 如果没有高度值,则获取紧贴高度计算
|
||||
// if (!position.hasOwnProperty('alt')) {
|
||||
// position.alt = await this.getClampToHeight(position)
|
||||
// }
|
||||
lng = this.options.customView.relativePosition.lng + position.lng
|
||||
lat = this.options.customView.relativePosition.lat + position.lat
|
||||
alt = this.options.customView.relativePosition.alt + position.alt
|
||||
destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
this.sdk.viewer.camera.flyTo({
|
||||
destination: destination,
|
||||
orientation: orientation
|
||||
})
|
||||
}
|
||||
else {
|
||||
let positionArray = []
|
||||
for (let i = 0; i < this.positions.length; i++) {
|
||||
let a = Cesium.Cartesian3.fromDegrees(
|
||||
this.positions[i][0],
|
||||
this.positions[i][1],
|
||||
this.positions[i][2] + this.options.height + this.options.heightDifference / 2
|
||||
)
|
||||
positionArray.push(a.x, a.y, a.z)
|
||||
}
|
||||
let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray)
|
||||
this.viewer.camera.flyToBoundingSphere(BoundingSphere, {
|
||||
offset: {
|
||||
heading: Cesium.Math.toRadians(0.0),
|
||||
pitch: Cesium.Math.toRadians(-20.0),
|
||||
roll: Cesium.Math.toRadians(0.0)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
getSphere() {
|
||||
return new Promise((resolve) => {
|
||||
// entity没有加载完成时 state 不会等于0 所以设置定时器直到获取到为止
|
||||
const interval = setInterval(() => {
|
||||
const sphere = new Cesium.BoundingSphere()
|
||||
const state = this.sdk.viewer._dataSourceDisplay.getBoundingSphere(
|
||||
this.viewer.entities.getById(this.options.id),
|
||||
false,
|
||||
sphere
|
||||
)
|
||||
if (state === Cesium.BoundingSphereState.DONE) {
|
||||
clearInterval(interval)
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
async remove() {
|
||||
if (this.viewer.entities.getById(this.options.id)) {
|
||||
this.viewer.entities.getById(this.options.id)._children.forEach((item) => {
|
||||
this.viewer.entities.remove(item);
|
||||
});
|
||||
this.viewer.entities.remove(this.viewer.entities.getById(this.options.id))
|
||||
}
|
||||
this.positions = []
|
||||
this.entity = null
|
||||
|
||||
if (this._DialogObject && !this._DialogObject.isDestroy) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
await this.sdk.removeIncetance(this.options.id)
|
||||
await syncData(this.sdk, this.options.id)
|
||||
}
|
||||
|
||||
flicker() { }
|
||||
}
|
||||
|
||||
export default FlowLine
|
||||
67
src/Obj/Base/FlyRoam/_element.js
Normal file
67
src/Obj/Base/FlyRoam/_element.js
Normal file
@ -0,0 +1,67 @@
|
||||
function html() {
|
||||
return `
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">名称</span>
|
||||
<input class="input" type="text" name="name">
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<input type="checkbox" name="isTotalTime" style="width: 16px; line-height: 15px; height: 15px; cursor: pointer; width: auto; margin-right: 5px;">
|
||||
<span class="label">设置总时长</span>
|
||||
<div class="input-number input-number-unit-3">
|
||||
<input class="input total-time" type="number" title="" min="0" max="999999.99" step="0.01" name="totalTime" value="0">
|
||||
<span class="unit" style="top: 6px;">秒(s)</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type="checkbox" name="repeat" style="width: 16px; line-height: 15px; height: 15px; cursor: pointer; width: auto; margin-right: 5px;">
|
||||
<span class="label">是否循环播放</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
<div class="div-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<button class="add-point"><svg class="icon-add"><use xlink:href="#yj-icon-add"></use></svg>增加视点</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button class="modify-point"><svg class="icon-edit"><use xlink:href="#yj-icon-edit"></use></svg>调整视点</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button class="afreshPlay"><svg class="icon-play"><use xlink:href="#yj-icon-play"></use></svg>播放</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button class="cease"><svg class="icon-pause"><use xlink:href="#yj-icon-pause"></use></svg>结束</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table">
|
||||
<div class="table-head">
|
||||
<div class="tr">
|
||||
<div class="th">序号</div>
|
||||
<div class="th">时长(s)</div>
|
||||
<div class="th">操作</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<div class="table-empty">
|
||||
<div class="empty-img"></div>
|
||||
<p>暂无数据</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="custom-divider"></span>
|
||||
`
|
||||
}
|
||||
|
||||
export { html }
|
||||
438
src/Obj/Base/FlyRoam/index.js
Normal file
438
src/Obj/Base/FlyRoam/index.js
Normal file
@ -0,0 +1,438 @@
|
||||
|
||||
/**
|
||||
* @description 飞行漫游
|
||||
*/
|
||||
import Dialog from '../../../BaseDialog';
|
||||
import { html } from "./_element";
|
||||
import Base from "../index";
|
||||
import { setActiveViewer, closeRotateAround, closeViewFollow} from '../../../Global/global'
|
||||
|
||||
class FlyRoam extends Base {
|
||||
#clickHandler = undefined
|
||||
/**
|
||||
* @constructor
|
||||
* @param sdk
|
||||
* @description 飞行漫游
|
||||
* @param options {object}
|
||||
* @param options.id {string} 标注id
|
||||
* @param options.name {string} 名称
|
||||
* @param options.repeat=0 {number} 重复次数
|
||||
* @param options.points=[]] {array} 视点列表
|
||||
* @param options.points[].position {object} 视点位置
|
||||
* @param options.points[].position.lng {number} 经度
|
||||
* @param options.points[].position.lat {number} 纬度
|
||||
* @param options.points[].position.alt {number} 高度
|
||||
* @param options.points[].orientation {object} 视点方向
|
||||
* @param options.points[].orientation.heading=0 {number} 视点航向角
|
||||
* @param options.points[].orientation.pitch=0 {number} 视点俯仰角
|
||||
* @param options.points[].orientation.roll=0 {number} 视点翻滚角
|
||||
* @param options.points[].duration=0 {number} 持续时间
|
||||
**/
|
||||
constructor(sdk, options = {}, _Dialog = {}) {
|
||||
super(sdk, options)
|
||||
this.options.id = options.id || this.randomString()
|
||||
this.options.name = options.name || '漫游路径'
|
||||
this.options.points = options.points || []
|
||||
if(this.options.repeat) {
|
||||
this.options.repeat = Number(this.options.repeat)
|
||||
}
|
||||
this.Dialog = _Dialog
|
||||
}
|
||||
|
||||
get repeat() {
|
||||
return this.options.repeat
|
||||
}
|
||||
|
||||
/**设置循环次数 (Infinity: 无限循环)*/
|
||||
set repeat(v) {
|
||||
if (this.options.repeat != Number(v)) {
|
||||
this.options.repeat = Number(v)
|
||||
if (this._DialogObject && this._DialogObject._element && this._DialogObject._element.content) {
|
||||
let repeatElm = this._DialogObject._element.content.querySelector("input[name='repeat']")
|
||||
if (v === Infinity) {
|
||||
repeatElm.checked = true
|
||||
}
|
||||
else {
|
||||
repeatElm.checked = false
|
||||
}
|
||||
this.Dialog.changeRepeatStateCallBack && this.Dialog.changeRepeatStateCallBack(repeatElm.checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async edit(state) {
|
||||
let _this = this
|
||||
let viewer = this.sdk.viewer
|
||||
let active = 0
|
||||
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
if (state) {
|
||||
this._DialogObject = await new Dialog(viewer._container, {
|
||||
title: '飞行漫游', left: '180px', top: '100px',
|
||||
closeCallBack: () => {
|
||||
this.cease()
|
||||
},
|
||||
})
|
||||
await this._DialogObject.init()
|
||||
let contentElm = document.createElement('div');
|
||||
contentElm.className = 'fly-roam'
|
||||
contentElm.innerHTML = html()
|
||||
this._DialogObject.contentAppChild(contentElm)
|
||||
|
||||
let all_elm = contentElm.getElementsByTagName("*")
|
||||
// EventBinding(all_elm)
|
||||
|
||||
let tableBody = contentElm.getElementsByClassName('table-body')[0];
|
||||
let tableEmpty = contentElm.getElementsByClassName('table-empty')[0]
|
||||
|
||||
let handler = {
|
||||
set: function (target, prop, value) {
|
||||
target[prop] = value;
|
||||
if (target.length > 0) {
|
||||
tableEmpty.style.display = 'none'
|
||||
}
|
||||
else {
|
||||
tableEmpty.style.display = 'flex'
|
||||
}
|
||||
return true;
|
||||
},
|
||||
};
|
||||
let i = 0
|
||||
let points = new Proxy([], handler);
|
||||
for (i = 0; i < this.options.points.length; i++) {
|
||||
points.push(this.options.points[i])
|
||||
addTrElm(this.options.points[i])
|
||||
}
|
||||
|
||||
|
||||
// let nameImputBoxElm = contentElm.getElementsByClassName('input-box')[0]
|
||||
// check(nameImputBoxElm, { validator: 'notEmpty', message: '名称不能为空!', trigger: 'input' })
|
||||
let nameElm = contentElm.querySelector("input[name='name']")
|
||||
nameElm.value = this.name
|
||||
nameElm.addEventListener('input', () => {
|
||||
this.name = nameElm.value
|
||||
})
|
||||
|
||||
let addListBtn = document.createElement('button');
|
||||
addListBtn.innerHTML = '保存'
|
||||
addListBtn.addEventListener('click', () => {
|
||||
if (!this.name) {
|
||||
this.name = '漫游路径'
|
||||
nameElm.value = this.name
|
||||
}
|
||||
let newPoints = []
|
||||
points.map((item) => {
|
||||
newPoints.push(item)
|
||||
})
|
||||
this._DialogObject.close()
|
||||
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(
|
||||
{
|
||||
id: this.options.id,
|
||||
name: this.name,
|
||||
points: newPoints,
|
||||
repeat: this.repeat+''
|
||||
}
|
||||
)
|
||||
})
|
||||
this._DialogObject.footAppChild(addListBtn)
|
||||
|
||||
let endBtn = contentElm.getElementsByClassName('cease')[0]
|
||||
endBtn.addEventListener('click', () => {
|
||||
viewer.camera.cancelFlight()
|
||||
})
|
||||
|
||||
let flyBtn = contentElm.getElementsByClassName('afreshPlay')[0]
|
||||
flyBtn.addEventListener('click', () => {
|
||||
if (points.length > 0) {
|
||||
this.flyTo(0)
|
||||
}
|
||||
})
|
||||
|
||||
let addBtn = contentElm.getElementsByClassName('add-point')[0]
|
||||
addBtn.addEventListener('click', () => {
|
||||
let position = this.cartesian3Towgs84(viewer.camera.position, viewer)
|
||||
let time = 0
|
||||
let data = {
|
||||
duration: time,
|
||||
position: position,
|
||||
orientation: {
|
||||
heading: viewer.camera.heading,
|
||||
pitch: viewer.camera.pitch,
|
||||
roll: viewer.camera.roll
|
||||
}
|
||||
}
|
||||
points.splice(active, 0, data)
|
||||
this.options.points.splice(active, 0, data)
|
||||
addTrElm(data)
|
||||
i++
|
||||
})
|
||||
let modifyBtn = contentElm.getElementsByClassName('modify-point')[0]
|
||||
modifyBtn.addEventListener('click', () => {
|
||||
if (!active) {
|
||||
return
|
||||
}
|
||||
let position = this.cartesian3Towgs84(viewer.camera.position, viewer)
|
||||
this.options.points[active - 1].position = points[active - 1].position = position
|
||||
this.options.points[active - 1].orientation = points[active - 1].orientation = {
|
||||
heading: viewer.camera.heading,
|
||||
pitch: viewer.camera.pitch,
|
||||
roll: viewer.camera.roll
|
||||
}
|
||||
this.message({text: '操作成功'})
|
||||
})
|
||||
|
||||
let totalTimeElm = contentElm.querySelector("input[name='totalTime']")
|
||||
let isTotalTimeElm = contentElm.querySelector("input[name='isTotalTime']")
|
||||
let repeatElm = contentElm.querySelector("input[name='repeat']")
|
||||
isTotalTimeElm.addEventListener('change', () => {
|
||||
let trList = tableBody.getElementsByClassName('tr')
|
||||
if (isTotalTimeElm.checked && trList.length > 0) {
|
||||
let time = Number((Number(totalTimeElm.value) / (trList.length - 1)).toFixed(2))
|
||||
for (let i = 0; i < trList.length - 1; i++) {
|
||||
points[i].duration = time
|
||||
this.options.points[i].duration = time
|
||||
trList[i].querySelector("input[name='time']").value = time
|
||||
}
|
||||
trList[trList.length - 1].querySelector("input[name='time']").value = 0
|
||||
}
|
||||
})
|
||||
totalTimeElm.addEventListener('blur', () => {
|
||||
let trList = tableBody.getElementsByClassName('tr')
|
||||
totalTimeElm.value = Number(totalTimeElm.value)
|
||||
if (totalTimeElm.value < 0) {
|
||||
totalTimeElm.value = 0
|
||||
}
|
||||
if (isTotalTimeElm.checked && trList.length > 0) {
|
||||
let time = Number((Number(totalTimeElm.value) / (trList.length - 1)).toFixed(2))
|
||||
for (let i = 0; i < trList.length - 1; i++) {
|
||||
points[i].duration = time
|
||||
this.options.points[i].duration = time
|
||||
trList[i].querySelector("input[name='time']").value = time
|
||||
}
|
||||
trList[trList.length - 1].querySelector("input[name='time']").value = 0
|
||||
}
|
||||
})
|
||||
repeatElm.checked = (this.repeat === Infinity ? true : false)
|
||||
repeatElm.addEventListener('change', () => {
|
||||
if (repeatElm.checked) {
|
||||
this.repeat = Infinity
|
||||
}
|
||||
else {
|
||||
this.repeat = 0
|
||||
}
|
||||
})
|
||||
|
||||
// Object.defineProperty(options, 'points', {
|
||||
// get() {
|
||||
// return e_allArea.value
|
||||
// },
|
||||
// set(value) {
|
||||
// e_allArea.value = value
|
||||
// }
|
||||
// })
|
||||
|
||||
function addTrElm(data) {
|
||||
let trList = tableBody.getElementsByClassName('tr')
|
||||
if (trList.length > 0) {
|
||||
trList[trList.length - 1].querySelector("input[name='time']").disabled = undefined
|
||||
}
|
||||
let tr_active = tableBody.getElementsByClassName('tr active')[0]
|
||||
tr_active && (tr_active.className = 'tr')
|
||||
let tr = document.createElement('div');
|
||||
tr.className = 'tr active'
|
||||
tr.innerHTML = `
|
||||
<div class="td" style="justify-content: center;">视点${i + 1}</div>
|
||||
<div class="td">
|
||||
<input class="input time" type="number" title="" min="0" max="999.99" step="0.01" name="time" value="${data.duration}">
|
||||
</div>
|
||||
<div class="td action">
|
||||
<button class="play">播放</span>
|
||||
<button class="delete">删除</span>
|
||||
</div>
|
||||
`
|
||||
tr.addEventListener('click', (v) => {
|
||||
if (v.target.parentNode === tr) {
|
||||
let tr_active = tableBody.getElementsByClassName('tr active')[0]
|
||||
tr_active && (tr_active.className = 'tr')
|
||||
tr.className = 'tr active'
|
||||
for (let m = 0; m < trList.length; m++) {
|
||||
if (trList[m] === tr) {
|
||||
active = m + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
tr.addEventListener('dblclick', (v) => {
|
||||
if (v.target.parentNode === tr) {
|
||||
for (let m = 0; m < trList.length; m++) {
|
||||
if (trList[m] === tr) {
|
||||
viewer.camera.flyTo({
|
||||
destination: Cesium.Cartesian3.fromDegrees(points[m].position.lng, points[m].position.lat, points[m].position.alt),
|
||||
orientation: points[m].orientation,
|
||||
duration: 1
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
let e_play = tr.getElementsByClassName('play')[0]
|
||||
let e_delete = tr.getElementsByClassName('delete')[0]
|
||||
let e_time = tr.querySelector("input[name='time']")
|
||||
e_play.addEventListener('click', () => {
|
||||
for (let m = 0; m < trList.length; m++) {
|
||||
if (trList[m] === e_delete.parentNode.parentNode) {
|
||||
_this.flyTo(m)
|
||||
}
|
||||
}
|
||||
})
|
||||
e_delete.addEventListener("click", (v) => {
|
||||
for (let m = 0; m < trList.length; m++) {
|
||||
if (trList[m] === e_delete.parentNode.parentNode) {
|
||||
points.splice(m, 1)
|
||||
points[points.length-1].duration = 0
|
||||
_this.options.points.splice(m, 1)
|
||||
tableBody.removeChild(tr)
|
||||
if (active > m + 1) {
|
||||
active--
|
||||
trList[active - 1].className = 'tr active'
|
||||
}
|
||||
else if (active == m + 1) {
|
||||
if (trList.length == m) {
|
||||
active -= 1
|
||||
}
|
||||
if (trList.length != 0) {
|
||||
trList[active - 1].className = 'tr active'
|
||||
}
|
||||
}
|
||||
// else if(active == m) {
|
||||
// console.log(trList.length-1, active)
|
||||
// if (trList.length == active-1) {
|
||||
// trList[active-2].className = 'tr active'
|
||||
// }
|
||||
// else {
|
||||
// trList[active-1].className = 'tr active'
|
||||
// }
|
||||
// }
|
||||
if (trList.length > 0) {
|
||||
let lastElm = trList[trList.length - 1].querySelector("input[name='time']")
|
||||
lastElm.disabled = 'disabled'
|
||||
lastElm.value = 0
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
// points.splice(i, 1)
|
||||
// tableBody.removeChild(tr)
|
||||
// if (trList.length > 0) {
|
||||
// trList[trList.length - 1].querySelector("input[name='time']").disabled = 'disabled'
|
||||
// }
|
||||
})
|
||||
e_time.addEventListener('input', (v) => {
|
||||
isTotalTimeElm.checked = false
|
||||
data.duration = Number(e_time.value)
|
||||
if (data.duration < 0) {
|
||||
data.duration = 0
|
||||
}
|
||||
})
|
||||
e_time.addEventListener('blur', () => {
|
||||
e_time.value = Number(Number(e_time.value).toFixed(2))
|
||||
if (e_time.value < 0) {
|
||||
e_time.value = 0
|
||||
}
|
||||
});
|
||||
tableBody.insertBefore(tr, trList[active])
|
||||
active++
|
||||
trList[trList.length - 1].querySelector("input[name='time']").disabled = 'disabled'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flyTo(i = 0) {
|
||||
setActiveViewer(0)
|
||||
let _this = this
|
||||
let points = this.options.points
|
||||
let currentRepeat = this.repeat
|
||||
|
||||
closeRotateAround(_this.sdk)
|
||||
const executeFlyTo = (index = 0, noStart) => {
|
||||
if (this.#clickHandler) {
|
||||
this.#clickHandler.destroy()
|
||||
}
|
||||
let _this = this
|
||||
this.#clickHandler = new Cesium.ScreenSpaceEventHandler(_this.sdk.viewer.canvas)
|
||||
this.#clickHandler.setInputAction((movement) => {
|
||||
this.cease()
|
||||
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
|
||||
let viewer = _this.sdk.viewer
|
||||
setActiveViewer(0)
|
||||
viewer.camera.cancelFlight()
|
||||
// function pauseExecution(seconds) {
|
||||
// return new Promise(resolve => setTimeout(resolve, seconds * 1000));
|
||||
// }
|
||||
viewer.camera.flyTo({
|
||||
destination: Cesium.Cartesian3.fromDegrees(points[index].position.lng, points[index].position.lat, points[index].position.alt),
|
||||
orientation: points[index].orientation,
|
||||
duration: noStart ? points[index - 1].duration : 0.5,
|
||||
maximumHeight: points[index].position.alt,
|
||||
complete: async () => {
|
||||
// if (!noStart) {
|
||||
// // await pauseExecution(2)
|
||||
// }
|
||||
index++
|
||||
|
||||
if (this.repeat === Infinity) {
|
||||
currentRepeat = Infinity
|
||||
}
|
||||
else if (currentRepeat === Infinity) {
|
||||
currentRepeat = this.repeat
|
||||
}
|
||||
if (index <= points.length - 1) {
|
||||
executeFlyTo(index, true)
|
||||
}
|
||||
else if (currentRepeat) {
|
||||
currentRepeat--
|
||||
executeFlyTo(0)
|
||||
}
|
||||
else {
|
||||
if (this.#clickHandler) {
|
||||
this.#clickHandler.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
easingFunction: noStart ? Cesium.EasingFunction.LINEAR_NONE : Cesium.EasingFunction.EXPONENTIAL_OUT
|
||||
})
|
||||
}
|
||||
|
||||
executeFlyTo(i)
|
||||
|
||||
}
|
||||
|
||||
/** 停止 */
|
||||
cease() {
|
||||
this.sdk && this.sdk.viewer && this.sdk.viewer.camera.cancelFlight()
|
||||
if (this.#clickHandler) {
|
||||
this.#clickHandler.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (this._DialogObject && this._DialogObject.close) {
|
||||
this._DialogObject.close()
|
||||
this._DialogObject = null
|
||||
}
|
||||
else {
|
||||
this.cease()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export default FlyRoam
|
||||
216
src/Obj/Base/GeoJson/index.js
Normal file
216
src/Obj/Base/GeoJson/index.js
Normal file
@ -0,0 +1,216 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-03-04 10:39
|
||||
* @description:index
|
||||
* @update: 2023-03-04 10:39
|
||||
*/
|
||||
import { getHost, getToken } from "../../../on";
|
||||
import Base from '../index'
|
||||
import Tools from '../../../Tools'
|
||||
import { syncSplitData } from "../../../Global/SplitScreen";
|
||||
import { syncData, getSdk as get2DSdk } from '../../../Global/MultiViewportMode'
|
||||
import { setActiveViewer, closeRotateAround, closeViewFollow} from '../../../Global/global'
|
||||
|
||||
class GeoJson extends Base {
|
||||
/**
|
||||
* @constructor
|
||||
* @param sdk
|
||||
* @param options {object} 参数
|
||||
* @param options.id {string} id
|
||||
* @param options.url {string} geojson地址
|
||||
* @param [options.color=#ef0606] {string} 线条颜色
|
||||
* @param [options.width=1] {number} 线条宽度
|
||||
* @example new YJ.Obj.GeoJson(earth,{id:"123",url:""})
|
||||
* */
|
||||
constructor(sdk, options = {}) {
|
||||
super(sdk, options)
|
||||
|
||||
this.primitive = undefined
|
||||
this.positions = []
|
||||
|
||||
|
||||
this.loading = true
|
||||
}
|
||||
|
||||
setDefaultValue() {
|
||||
super.setDefaultValue()
|
||||
this.options.host = this.options.host || getHost()
|
||||
// let url = this.options.url
|
||||
// if (this.options.host) {
|
||||
// let o = new URL(this.options.url, this.options.host)
|
||||
// url = o.href
|
||||
// }
|
||||
|
||||
// this.options.url = url
|
||||
this.options.color = this.options.color || 'rgb(239, 6, 6, 1)'
|
||||
this.options.width = this.options.width || 1
|
||||
}
|
||||
|
||||
get show() {
|
||||
return this.options.show
|
||||
}
|
||||
|
||||
// set show(status) {
|
||||
// let sdkD = get2DSdk().sdkD
|
||||
// if (!this.isShowView || !sdkD) {
|
||||
// this.options.show = status
|
||||
// }
|
||||
// if (this.entity) {
|
||||
// if (!this.showView || this.showView == 3 || !sdkD) {
|
||||
// for (let i = 0; i < this.entity.entities.values.length; i++) {
|
||||
// this.entity.entities.values[i].show = this.options.show
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// for (let i = 0; i < this.entity.entities.values.length; i++) {
|
||||
// this.entity.entities.values[i].show = false
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// syncData(this.sdk, this.options.id)
|
||||
// syncSplitData(this.sdk, this.options.id)
|
||||
// this.isShowView = false
|
||||
// }
|
||||
set show(status) {
|
||||
this.options.show = status
|
||||
if (this.entity) {
|
||||
for (let i = 0; i < this.entity.entities.values.length; i++) {
|
||||
this.entity.entities.values[i].show = status
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async on() {
|
||||
let url = ""
|
||||
if (this.options.host.endsWith("yjearth4.0"))
|
||||
url = this.options.host + '/data/service/getFile'
|
||||
else
|
||||
url = this.options.host + '/yjearth4.0/data/service/getFile'
|
||||
url = url + '?path=' + encodeURIComponent(this.options.url)
|
||||
let rsp = await fetch(url, {
|
||||
method: 'get',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"token": getToken(),
|
||||
"Authorization": "Bearer " + getToken(),
|
||||
}
|
||||
})
|
||||
let json = await rsp.json()
|
||||
this.geojson = json
|
||||
// this.sdk.addIncetance(this.options.id, this)
|
||||
return GeoJson.addDataToGlobe(this, json.features)
|
||||
}
|
||||
|
||||
/*geojosn暂时只用线的形式*/
|
||||
static addDataToGlobe(that) {
|
||||
const geoJsonDataSource = new Cesium.GeoJsonDataSource();
|
||||
let geojson = that.deepCopyObj(that.geojson)
|
||||
for (let i = 0; i < geojson.features.length; i++) {
|
||||
if (!geojson.features[i].id) {
|
||||
geojson.features[i].id = that.options.id + '_' + i
|
||||
}
|
||||
}
|
||||
// console.log(geojson)
|
||||
let promise = geoJsonDataSource.load(geojson, {
|
||||
clampToGround: true,
|
||||
});
|
||||
return promise.then(datasource => {
|
||||
that.entity = datasource
|
||||
datasource.entities.values.forEach(enetity => {
|
||||
// console.log(enetity)
|
||||
let color = Cesium.Color.fromCssColorString(that.options.color)
|
||||
let colorPolygon = color.withAlpha(0.2)
|
||||
enetity.show = that.options.show
|
||||
that.sdk.viewer.entities.add(enetity)
|
||||
if (enetity.billboard) {
|
||||
enetity.billboard.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND
|
||||
enetity.point = new Cesium.PointGraphics({
|
||||
show: true,
|
||||
color: color, // 点的颜色
|
||||
pixelSize: 10, // 点的大小
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY // 不应用深度测试
|
||||
})
|
||||
}
|
||||
|
||||
if (enetity.polyline) {
|
||||
enetity.polyline.material = color
|
||||
enetity.polyline.zIndex = that.sdk._entityZIndex
|
||||
that.sdk._entityZIndex++
|
||||
}
|
||||
|
||||
if (enetity.polygon) {
|
||||
enetity.polygon.perPositionHeight = false
|
||||
enetity.polygon.material = colorPolygon
|
||||
enetity.polygon.zIndex = that.sdk._entityZIndex
|
||||
|
||||
enetity.polyline = new Cesium.PolylineGraphics({
|
||||
positions: enetity.polygon.hierarchy._value.positions,
|
||||
width: 1,
|
||||
clampToGround: true,
|
||||
material: color,
|
||||
zIndex: that.sdk._entityZIndex
|
||||
})
|
||||
that.sdk._entityZIndex++
|
||||
}
|
||||
})
|
||||
that.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (this.entity) {
|
||||
this.entity.entities.values.forEach(enetity => {
|
||||
this.sdk.viewer.entities.remove(enetity)
|
||||
})
|
||||
this.entity = null
|
||||
this.geojson = {}
|
||||
}
|
||||
}
|
||||
|
||||
async flyTo() {
|
||||
if (!this.loading) {
|
||||
if (this.geojson) {
|
||||
setActiveViewer(0)
|
||||
closeRotateAround(this.sdk)
|
||||
closeViewFollow(this.sdk)
|
||||
|
||||
let range = turf.bbox(this.geojson);
|
||||
if (this.options.customView && this.options.customView.relativePosition && this.options.customView.orientation) {
|
||||
let orientation = {
|
||||
heading: Cesium.Math.toRadians(this.options.customView.orientation.heading || 0.0),
|
||||
pitch: Cesium.Math.toRadians(this.options.customView.orientation.pitch || -60.0),
|
||||
roll: Cesium.Math.toRadians(this.options.customView.orientation.roll || 0.0)
|
||||
}
|
||||
|
||||
let lng = this.options.customView.relativePosition.lng
|
||||
let lat = this.options.customView.relativePosition.lat
|
||||
let alt = this.options.customView.relativePosition.alt
|
||||
let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
|
||||
let position = { lng: range[0], lat: range[1] }
|
||||
// 如果没有高度值,则获取紧贴高度计算
|
||||
position.alt = await this.getClampToHeight(position)
|
||||
lng = this.options.customView.relativePosition.lng + position.lng
|
||||
lat = this.options.customView.relativePosition.lat + position.lat
|
||||
alt = this.options.customView.relativePosition.alt + position.alt
|
||||
destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
|
||||
this.sdk.viewer.camera.flyTo({
|
||||
destination: destination,
|
||||
orientation: orientation
|
||||
})
|
||||
}
|
||||
else {
|
||||
this.viewer.camera.flyTo({
|
||||
destination: Cesium.Rectangle.fromDegrees(...range)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flicker() { }
|
||||
}
|
||||
|
||||
export default GeoJson
|
||||
145
src/Obj/Base/GeoJson/index2.js
Normal file
145
src/Obj/Base/GeoJson/index2.js
Normal file
@ -0,0 +1,145 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-03-04 10:39
|
||||
* @description:index
|
||||
* @update: 2023-03-04 10:39
|
||||
*/
|
||||
import { getHost } from "../../../on";
|
||||
import Base from '../index'
|
||||
import Tools from '../../../Tools'
|
||||
|
||||
class GeoJson extends Base {
|
||||
/**
|
||||
* @constructor
|
||||
* @param sdk
|
||||
* @param options {object} 参数
|
||||
* @param options.id {string} id
|
||||
* @param options.url {string} geojson地址
|
||||
* @param [options.color=#ef0606] {string} 线条颜色
|
||||
* @param [options.width=1] {number} 线条宽度
|
||||
* @example new YJ.Obj.GeoJson(earth,{id:"123",url:""})
|
||||
* */
|
||||
constructor(sdk, options = {}) {
|
||||
super(sdk, options)
|
||||
|
||||
this.primitive = undefined
|
||||
this.positions = []
|
||||
|
||||
this.loading = true
|
||||
}
|
||||
|
||||
setDefaultValue() {
|
||||
super.setDefaultValue()
|
||||
this.options.host = this.options.host || getHost()
|
||||
let url = this.options.url
|
||||
if(this.options.host) {
|
||||
let o = new URL(this.options.url, this.options.host)
|
||||
url = o.href
|
||||
}
|
||||
|
||||
this.options.url = url
|
||||
this.options.color = this.options.color || '#ef0606'
|
||||
this.options.width = this.options.width || 1
|
||||
}
|
||||
|
||||
get show() {
|
||||
if (this.primitive) return this.primitive.show
|
||||
return undefined
|
||||
}
|
||||
|
||||
set show(status) {
|
||||
if (this.primitive) {
|
||||
this.primitive.show = status
|
||||
}
|
||||
}
|
||||
|
||||
async on() {
|
||||
let rsp = await fetch(this.options.url)
|
||||
let json = await rsp.json()
|
||||
return GeoJson.addDataToGlobe(this, json.features)
|
||||
}
|
||||
|
||||
/*geojosn暂时只用线的形式*/
|
||||
static addDataToGlobe(that, features) {
|
||||
const instances = []
|
||||
for (let i = 0; i < features.length; i++) {
|
||||
let positions = []
|
||||
if ('LineString' === features[i].geometry.type) {
|
||||
features[i].geometry.coordinates.forEach(c => {
|
||||
that.positions.push({ lng: c[0], lat: c[1] })
|
||||
positions.push(c[0], c[1])
|
||||
})
|
||||
}
|
||||
if ('Polygon' === features[i].geometry.type) {
|
||||
features[i].geometry.coordinates.forEach(polygon => {
|
||||
polygon.forEach(c => {
|
||||
that.positions.push({ lng: c[0], lat: c[1] })
|
||||
positions.push(c[0], c[1])
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const polyline = new Cesium.GroundPolylineGeometry({
|
||||
positions: Cesium.Cartesian3.fromDegreesArray(positions),
|
||||
width: that.options.width //线宽
|
||||
// vertexFormat: Cesium.PolylineColorAppearance.VERTEX_FORMAT
|
||||
})
|
||||
// let geometry = Cesium.PolylineGeometry.createGeometry(polyline)
|
||||
instances.push(
|
||||
new Cesium.GeometryInstance({
|
||||
geometry: polyline
|
||||
// attributes: {
|
||||
// color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.BLUE),
|
||||
// },
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
that.primitive = new Cesium.GroundPolylinePrimitive({
|
||||
geometryInstances: instances,
|
||||
// appearance: new Cesium.PerInstanceColorAppearance({ // 为每个instance着色
|
||||
// translucent: true,
|
||||
// closed: false
|
||||
// }),
|
||||
appearance: new Cesium.PolylineMaterialAppearance({
|
||||
material: Cesium.Material.fromType('Color', {
|
||||
color: Cesium.Color.fromCssColorString(that.options.color)
|
||||
})
|
||||
}),
|
||||
asynchronous: false, // 确定基元是异步创建还是阻塞直到准备就绪
|
||||
show: that.options.show ?? true
|
||||
})
|
||||
that.viewer.scene.primitives.add(that.primitive)
|
||||
that.loading = false
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (this.primitive) {
|
||||
super.remove()
|
||||
this.viewer.scene.primitives.remove(this.primitive)
|
||||
this.primitive = null
|
||||
}
|
||||
}
|
||||
|
||||
flyTo() {
|
||||
if (!this.loading) {
|
||||
if (this.positions) {
|
||||
let arr = new Tools().cal_envelope(this.positions)
|
||||
var rectangle = new Cesium.Rectangle.fromDegrees(
|
||||
arr[0][0],
|
||||
arr[0][1],
|
||||
arr[2][0],
|
||||
arr[2][1]
|
||||
)
|
||||
this.viewer.camera.flyTo({
|
||||
destination: rectangle
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flicker() {}
|
||||
}
|
||||
|
||||
export default GeoJson
|
||||
185
src/Obj/Base/GeoJson/index3.js
Normal file
185
src/Obj/Base/GeoJson/index3.js
Normal file
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: Administrator
|
||||
* @date: 2023-03-04 10:39
|
||||
* @description:index
|
||||
* @update: 2023-03-04 10:39
|
||||
*/
|
||||
import { getHost } from "../../../on";
|
||||
import Base from '../index'
|
||||
import Tools from '../../../Tools'
|
||||
|
||||
class GeoJson extends Base {
|
||||
/**
|
||||
* @constructor
|
||||
* @param sdk
|
||||
* @param options {object} 参数
|
||||
* @param options.id {string} id
|
||||
* @param options.url {string} geojson地址
|
||||
* @param [options.color=#ef0606] {string} 线条颜色
|
||||
* @param [options.width=1] {number} 线条宽度
|
||||
* @example new YJ.Obj.GeoJson(earth,{id:"123",url:""})
|
||||
* */
|
||||
constructor(sdk, options = {}) {
|
||||
super(sdk, options)
|
||||
|
||||
this.primitive = undefined
|
||||
this.positions = []
|
||||
|
||||
this.loading = true
|
||||
}
|
||||
|
||||
setDefaultValue() {
|
||||
super.setDefaultValue()
|
||||
this.options.host = this.options.host || getHost()
|
||||
let url = this.options.url
|
||||
if (this.options.host) {
|
||||
let o = new URL(this.options.url, this.options.host)
|
||||
url = o.href
|
||||
}
|
||||
|
||||
this.options.url = url
|
||||
this.options.color = this.options.color || 'rgb(239, 6, 6, 0.2)'
|
||||
this.options.width = this.options.width || 1
|
||||
}
|
||||
|
||||
get show() {
|
||||
if (this.primitive) return this.primitive.show
|
||||
return undefined
|
||||
}
|
||||
|
||||
set show(status) {
|
||||
if (this.primitive) {
|
||||
this.primitive.show = status
|
||||
}
|
||||
}
|
||||
|
||||
async on() {
|
||||
let rsp = await fetch(this.options.url)
|
||||
let json = await rsp.json()
|
||||
this.geojson = json
|
||||
return GeoJson.addDataToGlobe(this, json.features)
|
||||
}
|
||||
|
||||
/*geojosn暂时只用线的形式*/
|
||||
static addDataToGlobe(that, features) {
|
||||
const instancesList = []
|
||||
const instancesPolygon = []
|
||||
for (let i = 0; i < features.length; i++) {
|
||||
let color = Cesium.Color.fromRandom().withAlpha(0.2)
|
||||
if(features[i].geometry.type === 'LineString' || features[i].geometry.type === 'MultiLineString') {
|
||||
let coordinates = features[i].geometry.coordinates
|
||||
if(features[i].geometry.type === 'LineString') {
|
||||
coordinates = [features[i].geometry.coordinates]
|
||||
}
|
||||
for (let m = 0; m < coordinates.length; m++) {
|
||||
let item = coordinates[i]
|
||||
let positions = []
|
||||
item.forEach(c => {
|
||||
positions.push(c[0], c[1])
|
||||
})
|
||||
const polyline = new Cesium.GroundPolylineGeometry({
|
||||
positions: Cesium.Cartesian3.fromDegreesArray(positions),
|
||||
width: that.options.width //线宽
|
||||
})
|
||||
instancesList.push(
|
||||
new Cesium.GeometryInstance({
|
||||
geometry: polyline
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if(features[i].geometry.type === 'Polygon' || features[i].geometry.type === 'MultiPolygon') {
|
||||
let coordinates = features[i].geometry.coordinates
|
||||
if(features[i].geometry.type === 'Polygon') {
|
||||
coordinates = [features[i].geometry.coordinates]
|
||||
}
|
||||
for (let m = 0; m < coordinates.length; m++) {
|
||||
let item = coordinates[m]
|
||||
item.forEach(p => {
|
||||
let positions = []
|
||||
p.forEach(c => {
|
||||
positions.push(c[0], c[1])
|
||||
})
|
||||
let polygon = new Cesium.PolygonGeometry({
|
||||
polygonHierarchy: new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArray(positions)),
|
||||
});
|
||||
instancesPolygon.push(
|
||||
new Cesium.GeometryInstance({
|
||||
geometry: polygon,
|
||||
attributes: {
|
||||
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
||||
Cesium.Color.fromRandom().withAlpha(0.2)
|
||||
),
|
||||
show: new Cesium.ShowGeometryInstanceAttribute(that.options.show ?? true), //显示或者隐藏
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
const polyline = new Cesium.GroundPolylineGeometry({
|
||||
positions: Cesium.Cartesian3.fromDegreesArray(positions),
|
||||
width: 2
|
||||
})
|
||||
instancesList.push(
|
||||
new Cesium.GeometryInstance({
|
||||
geometry: polyline
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (instancesList.length > 0) {
|
||||
that.primitive = new Cesium.GroundPolylinePrimitive({
|
||||
geometryInstances: instancesList,
|
||||
appearance: new Cesium.PolylineMaterialAppearance({
|
||||
material: Cesium.Material.fromType('Color', {
|
||||
color: Cesium.Color.fromCssColorString(that.options.color)
|
||||
})
|
||||
}),
|
||||
asynchronous: false, // 确定基元是异步创建还是阻塞直到准备就绪
|
||||
show: that.options.show ?? true
|
||||
})
|
||||
that.viewer.scene.primitives.add(that.primitive)
|
||||
}
|
||||
if (instancesPolygon.length > 0) {
|
||||
that.primitive = new Cesium.GroundPrimitive({
|
||||
geometryInstances: instancesPolygon,
|
||||
appearance: new Cesium.PerInstanceColorAppearance({
|
||||
translucent: true, //false时透明度无效
|
||||
closed: false,
|
||||
}),
|
||||
asynchronous: false, // 确定基元是异步创建还是阻塞直到准备就绪
|
||||
show: that.options.show ?? true
|
||||
})
|
||||
that.viewer.scene.primitives.add(that.primitive)
|
||||
}
|
||||
|
||||
that.loading = false
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (this.primitive) {
|
||||
super.remove()
|
||||
this.viewer.scene.primitives.remove(this.primitive)
|
||||
this.primitive = null
|
||||
}
|
||||
}
|
||||
|
||||
flyTo() {
|
||||
if (!this.loading) {
|
||||
if(this.geojson) {
|
||||
let range = turf.bbox(this.geojson);
|
||||
this.viewer.camera.flyTo({
|
||||
destination: Cesium.Rectangle.fromDegrees(...range)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flicker() { }
|
||||
}
|
||||
|
||||
export default GeoJson
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user