Files
sdk4.0_new/src/Obj/Base/TextObject/GroundText/index.js
2025-09-11 14:59:12 +08:00

690 lines
24 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

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

import Dialog from '../../../Element/Dialog'
import EventBinding from '../../../Element/Dialog/eventBinding'
import { html } from './_element'
import Base from '../../index'
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 } from '../../../../Global/global'
class GroundText extends Base {
/**
* @constructor
* @param sdk
* @description 贴地文字
* @param options {object} 属性
* @param options.id {string} 唯一标识
* @param options.show=true {boolean} 显示/隐藏
* @param options.text {string} 文字
* @param options.color=#FFC107 {string} 颜色
* @param options.angle=0 {number} 旋转角度
* @param options.scale=1 {number} 缩放比例
* @param options.speed=1 {number} 文字滚动速度
* @param {object} options.position 位置
* @param {number} options.position.lng 经度
* @param {number} options.position.lat 纬度
* @param {object} options.positions 坐标集[{lon,lat}]仅在未定义 position 时有效
* @param options.positions[].lng {number} 经度
* @param options.positions[].lat {number} 纬度
* @param options.customView {object} 默认视角
* @param options.customView.orientation {object} 默认视角方位
* @param options.customView.orientation.heading {number} 航向角
* @param options.customView.orientation.pitch {number} 俯仰角
* @param options.customView.orientation.roll {number} 翻滚角
* @param options.customView.relativePosition {object} 视角相对位置
* @param options.customView.relativePosition.lng {number} 经度
* @param options.customView.relativePosition.lat {number} 纬度
* @param options.customView.relativePosition.alt {number} 高度
* */
constructor(sdk, options = {}, _Dialog = {}) {
super(sdk, options)
this.options.text = options.text || '未命名对象'
let textArray = this.options.text.split('\n')
for (let i = 0; i < textArray.length; i++) {
if (textArray[i].length > 80) {
textArray[i] = textArray[i].slice(0, 80 - textArray[i].length)
}
}
if (textArray.length > 70) {
textArray.splice(70 - textArray.length)
}
this.options.text = textArray.join('\n')
this.options.show =
options.show || options.show === false ? options.show : true
this.options.angle = options.angle || 0
this.options.scale =
options.scale || options.scale === 0 ? options.scale : 1
// this.options.fontSize = options.fontSize || 20
// this.options.duration =
// options.duration || options.duration === 0 ? options.duration : 50000
this.options.speed =
options.speed || options.speed === 0 ? options.speed : 1
this.options.color = options.color || '#FFC107'
this.options.position = options.position
delete this.options.name
if (!this.options.position && this.options.positions) {
this.options.position = { lng: (this.options.positions[0].lng + this.options.positions[1].lng) / 2, lat: (this.options.positions[0].lat + this.options.positions[1].lat) / 2 }
let point1 = turf.point([this.options.positions[0].lng, this.options.positions[0].lat]);
let point2 = turf.point([this.options.positions[1].lng, this.options.positions[1].lat]);
let options = { units: 'miles' };
let distance1 = turf.rhumbDistance(point1, point2, options);
// 计算两点与x轴正方向的夹角弧度
function calculateAngle(pointA, pointB) {
let dx = pointB[0] - pointA[0];
let dy = pointB[1] - pointA[1];
return Math.atan2(dy, dx);
}
let angleRadians = calculateAngle([this.options.positions[0].lng, this.options.positions[0].lat], [this.options.positions[1].lng, this.options.positions[1].lat]);
this.options.angle = (360 + Cesium.Math.toDegrees(angleRadians)) % 360
let gap =
Math.abs(Math.cos((Math.PI / 180) * this.options.position.lat)) *
0.0001
let canvas = this.getcanvas()
let ratio = canvas.height / canvas.width
let lng1 = this.options.position.lng - 0.0001 / ratio
let lat1 = this.options.position.lat - gap
let lng2 = this.options.position.lng + 0.0001 / ratio
let lat2 = this.options.position.lat + gap
let lng = (lng1 + lng2) / 2
let lat = (lat1 + lat2) / 2
let from = turf.point([lng1, lat]);
let to = turf.point([lng2, lat]);
let distance2 = turf.rhumbDistance(from, to, options);
let latRadians = Cesium.Math.toRadians(this.options.position.lat)
distance2 = distance2 * (1 + (Math.abs(Math.sin(angleRadians) * Math.tan(latRadians) * Math.sin(latRadians) * Math.sin(latRadians))))
this.options.scale = distance1 / distance2
}
this.duration = 50000
this.entity
this._positionEditing = false
this.Dialog = _Dialog
this._EventBinding = new EventBinding()
this._elms = {}
this.previous = {
position: { ...this.options.position }
}
this.event = new MouseEvent(this.sdk)
this.sdk.addIncetance(this.options.id, this)
this.create()
}
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 name() {
return
}
get text() {
return this.options.text
}
set text(v) {
this.options.text = v
let textArray = this.options.text.split('\n')
for (let i = 0; i < textArray.length; i++) {
if (textArray[i].length > 80) {
let _error = '行超过80个字符,请按回车Enter继续输入'
window.ELEMENT && window.ELEMENT.Message({
message: _error,
type: 'warning',
duration: 1000
});
textArray[i] = textArray[i].slice(0, 80 - textArray[i].length)
}
}
if (textArray.length > 70) {
textArray.splice(70 - textArray.length)
let _error = '超过最大输入字符'
window.ELEMENT && window.ELEMENT.Message({
message: _error,
type: 'warning',
duration: 1000
});
}
this.options.text = textArray.join('\n')
if (this.entity) {
let canvas = this.getcanvas()
let ratio = canvas.height / canvas.width
this.entity.rectangle.material = new Cesium.CustomMaterialSource({
image: canvas.toDataURL('image/png'),
color: this.options.color,
repeat: new Cesium.Cartesian2(1.0, 1.0),
duration: this.duration / this.options.speed,
fltr: false,
is2D: this.sdk.viewer.scene.mode === Cesium.SceneMode.SCENE2D ? true : false
})
this.entity.rectangle.coordinates = new Cesium.CallbackProperty(() => {
let gap =
Math.abs(Math.cos((Math.PI / 180) * this.options.position.lat)) *
(0.0001 * this.options.scale)
let fromDegreesArray = [
this.options.position.lng - (0.0001 * this.options.scale) / ratio,
this.options.position.lat - gap,
this.options.position.lng + (0.0001 * this.options.scale) / ratio,
this.options.position.lat + gap
]
return Cesium.Rectangle.fromDegrees(...fromDegreesArray)
}, false)
}
this._elms.text &&
this._elms.text.forEach(item => {
item.value = this.options.text
})
}
get angle() {
return this.options.angle
}
set angle(v) {
this.options.angle = Number(v)
this._elms.angle &&
this._elms.angle.forEach(item => {
item.value = v
})
}
get scale() {
return this.options.scale
}
set scale(v) {
this.options.scale = Number(v)
this._elms.scale &&
this._elms.scale.forEach(item => {
item.value = v
})
}
// get duration() {
// return this.options.duration
// }
// set duration(v) {
// this.options.duration = v
// let canvas = this.getcanvas()
// this.entity.rectangle.material = new Cesium.CustomMaterialSource({
// image: canvas.toDataURL('image/png'),
// color: this.options.color,
// repeat: new Cesium.Cartesian2(1.0, 1.0),
// duration: this.options.duration / this.options.speed,
// fltr: false,
// is2D: this.sdk.viewer.scene.mode === Cesium.SceneMode.SCENE2D ? true : false
// })
// this._elms.duration &&
// this._elms.duration.forEach(item => {
// item.value = v
// })
// }
get speed() {
return this.options.speed
}
set speed(v) {
this.options.speed = Number(v)
let canvas = this.getcanvas()
this.entity.rectangle.material = new Cesium.CustomMaterialSource({
image: canvas.toDataURL('image/png'),
color: this.options.color,
repeat: new Cesium.Cartesian2(1.0, 1.0),
duration: this.duration / this.options.speed,
fltr: false,
is2D: this.sdk.viewer.scene.mode === Cesium.SceneMode.SCENE2D ? true : false
})
this._elms.speed &&
this._elms.speed.forEach(item => {
item.value = v
})
}
get color() {
return this.options.color
}
set color(v) {
this.options.color = v
let canvas = this.getcanvas()
this.entity.rectangle.material = new Cesium.CustomMaterialSource({
image: canvas.toDataURL('image/png'),
color: this.options.color,
repeat: new Cesium.Cartesian2(1.0, 1.0),
duration: this.duration / this.options.speed,
fltr: false,
is2D: this.sdk.viewer.scene.mode === Cesium.SceneMode.SCENE2D ? true : false
})
if (this._elms.color) {
this._elms.color.forEach((item, i) => {
let colorPicker = 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] = colorPicker
})
}
}
create() {
// let gap = Math.abs(Math.cos(Math.PI/180 * this.options.position.lat)) * (0.0001*this.options.scale)
// let fromDegreesArray = [
// this.options.position.lng - 0.05, this.options.position.lat - 0.05,
// this.options.position.lng + 0.05, this.options.position.lat - 0.05,
// this.options.position.lng + 0.05, this.options.position.lat + 0.05,
// this.options.position.lng - 0.05, this.options.position.lat + 0.05,
// ]
let canvas = this.getcanvas()
let ratio = canvas.height / canvas.width
this.entity = this.sdk.viewer.entities.add({
id: this.options.id,
show: this.options.show,
rectangle: {
coordinates: new Cesium.CallbackProperty(() => {
let gap =
Math.abs(Math.cos((Math.PI / 180) * this.options.position.lat)) *
(0.0001 * this.options.scale)
let fromDegreesArray = [
this.options.position.lng - (0.0001 * this.options.scale) / ratio,
this.options.position.lat - gap,
// this.options.position.lng + 0.05, this.options.position.lat - 0.05,
this.options.position.lng + (0.0001 * this.options.scale) / ratio,
this.options.position.lat + gap
// this.options.position.lng - 0.05, this.options.position.lat + 0.05,
]
return Cesium.Rectangle.fromDegrees(...fromDegreesArray)
}, false),
material: new Cesium.CustomMaterialSource({
image: canvas.toDataURL('image/png'),
color: this.options.color,
repeat: new Cesium.Cartesian2(1.0, 1.0),
duration: this.duration / this.options.speed,
fltr: false,
is2D: this.sdk.viewer.scene.mode === Cesium.SceneMode.SCENE2D ? true : false
}),
rotation: new Cesium.CallbackProperty(() => {
return Cesium.Math.toRadians(this.options.angle)
}, false),
stRotation: new Cesium.CallbackProperty(() => {
return Cesium.Math.toRadians(this.options.angle)
}, false)
}
})
if (this.sdk.viewer._element.className === 'cesium-viewer 2d') {
this.entity.rectangle.height = 10
}
syncData(this.sdk, this.options.id)
if (this.options.show) {
setSplitDirection(0, this.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.originalOptions,
{
title: '贴地文字属性',
left: '180px',
top: '100px',
confirmCallBack: options => {
this.text = this.text.trim()
if (!this.text) {
this.text = '未命名对象'
}
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.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()
},
translationalCallBack: () => {
this.positionEditing = !this.positionEditing
}
},
true
)
this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' ground-text'
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
// 颜色组件
let colorPicker = new YJColorPicker({
el: contentElm.getElementsByClassName('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)'
} //点击清空按钮事件回调
})
this._elms.color = [colorPicker]
} else {
if (this._DialogObject && this._DialogObject.remove) {
this._DialogObject.remove()
this._DialogObject = null
}
}
}
/**@desc 打开平移功能
*
* @memberOf Source
* @param status {boolean}
*
* */
set positionEditing(status) {
if (!this.sdk || !this.sdk.viewer || !this.entity) {
return
}
this._positionEditing = status
this.previous = {
position: { ...this.options.position }
}
if (status === true) {
this.tip && this.tip.destroy()
this.tip = new MouseTip('点击鼠标左键确认,右键取消', this.sdk)
this.event.mouse_move((movement, cartesian) => {
let position = this.cartesian3Towgs84(cartesian, this.sdk.viewer)
this.lng = position.lng
this.lat = position.lat
this.tip.setPosition(
cartesian,
movement.endPosition.x,
movement.endPosition.y
)
})
this.event.mouse_left((movement, cartesian) => {
let position = this.cartesian3Towgs84(cartesian, this.sdk.viewer)
this.lng = position.lng
this.lat = position.lat
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.lng = this.previous.position.lng
this.lat = this.previous.position.lat
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.lng = this.previous.position.lng
this.lat = this.previous.position.lat
this.positionEditing = false
}
else {
let position = this.cartesian3Towgs84(cartesian, this.sdk.viewer)
this.lng = position.lng
this.lat = position.lat
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._positionEditingCallback) {
this._positionEditingCallback()
this._positionEditingCallback = null
}
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()
}
}
get positionEditing() {
return this._positionEditing
}
openPositionEditing(cd) {
this.positionEditing = true
this._positionEditingCallback = cd
}
/**
* 飞到
*/
async flyTo(options = {}) {
let canvas = this.getcanvas()
let ratio = canvas.height / canvas.width
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 gap =
Math.abs(Math.cos((Math.PI / 180) * this.options.position.lat)) *
(0.0001 * this.options.scale)
let fromDegreesArray = [
[
this.options.position.lng - (0.0001 * this.options.scale) / ratio,
this.options.position.lat - gap
],
[
this.options.position.lng + (0.0001 * this.options.scale) / ratio,
this.options.position.lat + gap
]
]
let height = await this.getClampToHeight(this.options.position)
let positionArray = []
for (let i = 0; i < fromDegreesArray.length; i++) {
let a = Cesium.Cartesian3.fromDegrees(...fromDegreesArray[i], height)
positionArray.push(a.x, a.y, a.z)
}
let BoundingSphere = Cesium.BoundingSphere.fromVertices(positionArray)
this.sdk.viewer.camera.flyToBoundingSphere(BoundingSphere, {
offset: options.orientation || {
heading: Cesium.Math.toRadians(0.0),
pitch: Cesium.Math.toRadians(-90.0),
roll: Cesium.Math.toRadians(0.0)
}
})
}
}
reset() {
if (!this.entity) {
return
}
this.options = this.deepCopyObj(this.originalOptions)
this.text = this.originalOptions.text
this.angle = this.originalOptions.angle
this.scale = this.originalOptions.scale
this.color = this.originalOptions.color
}
async remove() {
this.event && this.event.destroy()
this.tip && this.tip.destroy()
this.sdk.viewer.entities.remove(this.entity)
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)
}
getcanvas() {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
let textArray = this.options.text.split('\n')
for (let i = 0; i < textArray.length; i++) {
if (textArray[i].length > 80) {
textArray[i] = textArray[i].slice(0, 80 - textArray[i].length)
}
}
if (textArray.length > 70) {
textArray.splice(70 - textArray.length)
}
this.options.text = textArray.join('\n')
let maxWidth = 0
for (let i = 0; i < textArray.length; i++) {
ctx.font = 200 + 'px serif'
const width = ctx.measureText(textArray[i]).width
if (maxWidth < width) {
maxWidth = width
}
}
canvas.width = maxWidth
canvas.height = 220 * textArray.length
for (let i = 0; i < textArray.length; i++) {
ctx.font = 200 + 'px serif'
ctx.fillStyle = 'rgba(255, 255, 255, 0)'
ctx.fillRect(0, 0, maxWidth + 30, 210)
ctx.fillStyle = 'rgba(255, 255, 255, 1)'
ctx.font = '200px serif'
ctx.fillText(textArray[i], 0, 210 * (i + 1))
}
return canvas
}
}
export default GroundText