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.show=true {boolean} 显示/隐藏 * @param options.text {string} 文字 * @param options.angle=0 {number} 旋转角度 * @param options.scale=1 {number} 比例 * @param {object} options.position 经纬度{lon,lat} * @param {object} options.positions 经纬度集[{lon,lat}]仅在未定义 position 时有效 * @param _Dialog {object} 弹框事件 * @param _Dialog.confirmCallBack {function} 弹框确认时的回调 * */ 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.name = this.options.text 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 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.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 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.options.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 = v this._elms.angle && this._elms.angle.forEach(item => { item.value = v }) } get scale() { return this.options.scale } set scale(v) { this.options.scale = 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 = 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.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.options.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.options.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.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 } /** * 飞到 */ 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