This commit is contained in:
zh
2025-09-01 16:17:11 +08:00
parent d802602200
commit 6fa99df21c
1035 changed files with 377083 additions and 1 deletions

View 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

View 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

View 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
View 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
View 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
View 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类型')
}
}
}

View 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
*/
// 新增航点 beforeafterend
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
}
}

View 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 }

View 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;

View 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);
}
}
}
}`;

View 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

View 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;

View 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;

View 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 }

View 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;

View 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;

View 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;

View File

@ -0,0 +1,8 @@
function html() {
return `
<span class="custom-divider"></span>
<div class="profile-echarts"></div>
`
}
export { html }

View 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;

View 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;

View 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;

View 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 }

View 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

View 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 }

View 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;

View 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 }

View 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;

View 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);
}
}
}
}`;

View 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);
}
}
}
}`;

View 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;

View 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 }

View 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;

View 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);
}
}
}
}`;

View 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;

View 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 }

View 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;

View 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);
}
}
}
}`;

View 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;

View 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 }

View 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
View 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}

View 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 }

View 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;

View 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);
}
}`;

View 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;

View 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;

View 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 }

View 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;

View 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);
}
}`;

File diff suppressed because one or more lines are too long

View 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

View 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 }

File diff suppressed because it is too large Load Diff

View 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 }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,74 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-11-20 19:00
* @descriptionindex
* @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}

View 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
* @descriptionindex
* @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}

View File

@ -0,0 +1,147 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-11-20 15:51
* @descriptionindex
* @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

View File

@ -0,0 +1,58 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-11-20 15:51
* @descriptionindex
* @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

View File

@ -0,0 +1,322 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-11-20 18:06
* @descriptionindex
* @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

View 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 }

File diff suppressed because it is too large Load Diff

View 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 }

File diff suppressed because it is too large Load Diff

View 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

View File

@ -0,0 +1,293 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-11-20 17:54
* @descriptionindex
* @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

View 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 }

View 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 }

View File

@ -0,0 +1,877 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-11-20 16:05
* @descriptionindex
* @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}&#10;`
}
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

View 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 }

View 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;

View File

@ -0,0 +1,373 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-11-20 16:05
* @descriptionindex
* @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

View File

@ -0,0 +1,650 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-11-20 17:54
* @descriptionindex
* @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

View File

@ -0,0 +1,45 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-11-20 18:31
* @descriptionindex
* @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

View 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 }

View 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 }

View 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;

View 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

View 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 }

File diff suppressed because it is too large Load Diff

View 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 }

File diff suppressed because it is too large Load Diff

View 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 }

File diff suppressed because it is too large Load Diff

View 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

View 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 }

View 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;

File diff suppressed because it is too large Load Diff

View 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 }

File diff suppressed because it is too large Load Diff

View 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 }

View 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

View 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 }

View 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;

View 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

View 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 }

View 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

View File

@ -0,0 +1,216 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-03-04 10:39
* @descriptionindex
* @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

View File

@ -0,0 +1,145 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-03-04 10:39
* @descriptionindex
* @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

View File

@ -0,0 +1,185 @@
/**
* @name: index
* @author: Administrator
* @date: 2023-03-04 10:39
* @descriptionindex
* @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