Files
sdk4.0/src/Obj/Base/LabelObject/index.js

590 lines
18 KiB
JavaScript
Raw Normal View History

2025-07-03 13:54:01 +08:00
/**
* 标注
*/
import Base from '../index'
import MouseEvent from '../../../Event/index'
import {
getGroundCover
} from '../../../Global/global'
import { getFontFamily } from '../../Element/fontSelect'
import {
addCluster,
remove_entity_from_cluster
} from '../../../Global/cluster/cluster'
class LabelObject extends Base {
#updateBillboardImageTimeout
constructor(sdk, options = {}, model) {
super(sdk, options)
this.model = model
this.options.near = options.near || options.near === 0 ? options.near : 2000
this.options.far = options.far || options.far === 0 ? options.far : 100000
this.options.scaleByDistance = options.scaleByDistance || false
this.options.show =
options.show || options.show === false ? options.show : true
this.options.text = options.text
let textArray = this.options.text.split('\n')
for (let i = 0; i < textArray.length; i++) {
if (textArray[i].length > 40) {
textArray[i] = textArray[i].slice(0, 40 - textArray[i].length)
}
}
if (textArray.length > 10) {
textArray.splice(10 - textArray.length)
}
this.options.text = textArray.join('\n')
this.options.fontFamily = options.fontFamily || 0
2025-07-09 11:15:53 +08:00
this.font = getFontFamily(this.options.fontFamily) || 'SimHei'
2025-07-03 13:54:01 +08:00
this.options.fontSize = options.fontSize || 20
this.options.lineWidth = options.lineWidth || 4
2025-07-09 11:15:53 +08:00
this.options.lineColor = options.lineColor || '#00ffff80'
2025-07-03 13:54:01 +08:00
this.options.color = options.color || '#ffffff'
this.options.ground =
options.ground || options.ground === false ? options.ground : true
this.options.pixelOffset =
options.pixelOffset || options.pixelOffset === 0
? options.pixelOffset
: 20
this.options.backgroundColor = options.backgroundColor || [
2025-07-09 11:15:53 +08:00
'#00ffff80',
'#00ffff80'
2025-07-03 13:54:01 +08:00
]
this.event = new MouseEvent(this.sdk)
this.entity
this.create(this.options.position)
this.picking = true
}
async create() {
let _this = this
if (!this.options.position[2] && this.options.position[2] !== 0) {
this.options.position[2] = await this.getClampToHeight({
lng: this.options.position[0],
lat: this.options.position[1]
})
}
this.originalOptions = copyObj(this.options)
this.entity = this.sdk.viewer.entities.add({
show: this.options.show,
id: this.options.id + '-label',
position: new Cesium.CallbackProperty(function () {
if (_this.model) {
// return Cesium.Cartesian3.fromDegrees(_this.options.position[0], _this.options.position[1], _this.model.originalBoundingSphereRadius*2*_this.model.customScale.z + _this.options.position[2])
if (_this.model.isMove) {
let scale = _this.model.customScale.x
if (_this.model.customScale.y > scale) {
scale = _this.model.customScale.y
}
if (_this.model.customScale.z > scale) {
scale = _this.model.customScale.z
}
let point1 = Cesium.Cartesian3.fromDegrees(
_this.options.position[0],
_this.options.position[1],
_this.options.position[2] +
(_this.model.originalBoundingSphereRadius || 1) *
2 *
(scale || 0.01)
)
// 点2的位置也使用经纬高表示
let point2 = Cesium.Cartesian3.fromDegrees(
_this.options.position[0],
_this.options.position[1],
_this.options.position[2] -
(_this.model.originalBoundingSphereRadius || 1) *
2 *
(scale || 0.01)
)
let direction = Cesium.Cartesian3.subtract(
point2,
point1,
new Cesium.Cartesian3()
)
let c = Cesium.Cartesian3.normalize(direction, direction)
let ray = new Cesium.Ray(point1, c)
let pickedObjects = _this.viewer.scene.drillPickFromRay(ray, 5)
for (let i = 0; i < pickedObjects.length; i++) {
if (
pickedObjects[i].object &&
pickedObjects[i].object.id &&
pickedObjects[i].object.id === _this.model.id
) {
let pos84 = _this.cartesian3Towgs84(pickedObjects[i].position, _this.sdk.viewer)
_this.options.position[0] = pos84.lng
_this.options.position[1] = pos84.lat
_this.options.position[2] = pos84.alt
break
}
}
}
return Cesium.Cartesian3.fromDegrees(
_this.options.position[0],
_this.options.position[1],
_this.options.position[2]
)
} else {
return Cesium.Cartesian3.fromDegrees(..._this.options.position)
}
}, false),
billboard: {
image: this.getcanvas(),
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: new Cesium.CallbackProperty(function () {
return getGroundCover() ? undefined : Number.POSITIVE_INFINITY
}, false),
scaleByDistance: this.options.scaleByDistance
? new Cesium.NearFarScalar(this.options.near, 1, this.options.far, 0)
: undefined,
pixelOffsetScaleByDistance: this.options.scaleByDistance
? new Cesium.NearFarScalar(this.options.near, 1, this.options.far, 0)
: undefined
}
// label: {
// show: this.options.show,
// text: new Cesium.CallbackProperty(function () {
// return _this.options.text
// }, false),
// font: this.options.fontSize + "px Helvetica",
// fillColor: Cesium.Color.fromCssColorString(this.options.color),
// pixelOffset: new Cesium.Cartesian2(0, -this.options.pixelOffset),
// outlineColor: Cesium.Color.BLACK,
// backgroundColor: Cesium.Color.fromCssColorString('#42c6ef'),
// backgroundPadding: new Cesium.Cartesian2(12, 12),
// showBackground: true,
// verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
// outlineWidth: 1,
// style: Cesium.LabelStyle.FILL_AND_OUTLINE,
// },
})
}
get position() {
return this.options.position
}
set position(v) {
// console.log(v)
this.options.position = v
if (!v[2] && v[2] !== 0) {
let objectsToExclude = [...this.sdk.viewer.entities.values]
this.getClampToHeight({
lng: v[0],
lat: v[1]
}, objectsToExclude).then(height => {
v[2] = height
this.options.position = [...v]
})
// let point1 = Cesium.Cartesian3.fromDegrees(this.options.position[0], this.options.position[1], 0);
// let point2 = Cesium.Cartesian3.fromDegrees(this.options.position[0], this.options.position[1], 10000000);
// let direction = Cesium.Cartesian3.subtract(point2, point1, new Cesium.Cartesian3());
// let c = Cesium.Cartesian3.normalize(direction, direction);
// let ray = new Cesium.Ray(point1, c);
// let r = {}
// let pickedObjects = this.sdk.viewer.scene.drillPickFromRay(ray);
// for (let i = 0; i < pickedObjects.length; i++) {
// if (pickedObjects[i].position) {
// r = pickedObjects[i]
// break
// }
// }
// if (r && r.position) {
// this.options.position[2] = this.cartesian3Towgs84(r.position, this.sdk.viewer).alt
// }
// else {
// try {
// let promise = Cesium.sampleTerrainMostDetailed(this.sdk.viewer.terrainProvider, [Cesium.Cartographic.fromDegrees(this.options.position[0], this.options.position[1])]);
// promise.then((p) => {
// this.options.position[2] = p[0].height
// }).catch((e)=>{
// })
// } catch (error) {
// }
// }
} else {
this.options.position = [...v]
}
}
get show() {
return this.options.show
}
set show(v) {
this.options.show = v
if (!this.entity) {
return
}
this.entity.show = v
if (this.model) {
// return Cesium.Cartesian3.fromDegrees(this.options.position[0], this.options.position[1], this.model.originalBoundingSphereRadius*2*this.model.customScale.z + this.options.position[2])
let scale = this.model.customScale.x
if (this.model.customScale.y > scale) {
scale = this.model.customScale.y
}
if (this.model.customScale.z > scale) {
scale = this.model.customScale.z
}
let point1 = Cesium.Cartesian3.fromDegrees(
this.options.position[0],
this.options.position[1],
this.options.position[2] +
(this.model.originalBoundingSphereRadius || 1) *
2 *
(scale || 0.01)
)
// 点2的位置也使用经纬高表示
let point2 = Cesium.Cartesian3.fromDegrees(
this.options.position[0],
this.options.position[1],
this.options.position[2] -
(this.model.originalBoundingSphereRadius || 1) *
2 *
(scale || 0.01)
)
let direction = Cesium.Cartesian3.subtract(
point2,
point1,
new Cesium.Cartesian3()
)
let c = Cesium.Cartesian3.normalize(direction, direction)
let ray = new Cesium.Ray(point1, c)
let pickedObjects = this.viewer.scene.drillPickFromRay(ray, 5)
for (let i = 0; i < pickedObjects.length; i++) {
if (
pickedObjects[i].object &&
pickedObjects[i].object.id &&
pickedObjects[i].object.id === this.model.id
) {
let pos84 = this.cartesian3Towgs84(pickedObjects[i].position, this.sdk.viewer)
this.options.position[0] = pos84.lng
this.options.position[1] = pos84.lat
this.options.position[2] = pos84.alt
break
}
}
}
else if (this.options.ground) {
let objectsToExclude = [...this.sdk.viewer.entities.values]
this.getClampToHeight({
lng: this.options.position[0],
lat: this.options.position[1]
}, objectsToExclude).then(height => {
this.options.position[2] = height
})
}
}
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 > 40) {
textArray[i] = textArray[i].slice(0, 40 - textArray[i].length)
}
}
if (textArray.length > 10) {
textArray.splice(10 - textArray.length)
}
this.options.text = textArray.join('\n')
this.entity && (this.updateBillboardImage())
}
get color() {
return this.options.color
}
set color(v) {
this.options.color = v
this.entity && (this.entity.billboard.image = this.getcanvas())
}
get scaleByDistance() {
return this.options.scaleByDistance
}
set scaleByDistance(v) {
this.options.scaleByDistance = v
if (!this.entity) {
return
}
if (this.options.scaleByDistance) {
this.entity.billboard.scaleByDistance = new Cesium.NearFarScalar(
this.options.near,
1,
this.options.far,
0
)
this.entity.billboard.pixelOffsetScaleByDistance = new Cesium.NearFarScalar(
this.options.near,
1,
this.options.far,
0
)
} else {
this.entity.billboard.scaleByDistance = undefined
this.entity.billboard.pixelOffsetScaleByDistance = undefined
}
}
get near() {
return this.options.near
}
set near(v) {
let near = v
if (near > this.far) {
near = this.far
}
this.options.near = near
if (!this.entity) {
return
}
if (this.options.scaleByDistance) {
this.entity.billboard.scaleByDistance = new Cesium.NearFarScalar(
this.options.near,
1,
this.options.far,
0
)
this.entity.billboard.pixelOffsetScaleByDistance = new Cesium.NearFarScalar(
this.options.near,
1,
this.options.far,
0
)
} else {
this.entity.billboard.scaleByDistance = undefined
this.entity.billboard.pixelOffsetScaleByDistance = undefined
}
}
get far() {
return this.options.far
}
set far(v) {
let far = v
if (far < this.near) {
far = this.near
}
this.options.far = far
if (!this.entity) {
return
}
if (this.options.scaleByDistance) {
this.entity.billboard.scaleByDistance = new Cesium.NearFarScalar(
this.options.near,
1,
this.options.far,
0
)
this.entity.billboard.pixelOffsetScaleByDistance = new Cesium.NearFarScalar(
this.options.near,
1,
this.options.far,
0
)
} else {
this.entity.billboard.scaleByDistance = undefined
this.entity.billboard.pixelOffsetScaleByDistance = undefined
}
}
get fontSize() {
return this.options.fontSize
}
set fontSize(v) {
this.options.fontSize = Number(v)
if (!this.entity) {
return
}
this.updateBillboardImage()
}
get fontFamily() {
return this.options.fontFamily
}
set fontFamily(v) {
this.options.fontFamily = v || 0
2025-07-09 11:15:53 +08:00
this.font = getFontFamily(this.options.fontFamily) || 'SimHei'
2025-07-03 13:54:01 +08:00
this.updateBillboardImage()
}
get lineWidth() {
return this.options.lineWidth
}
set lineWidth(v) {
2025-07-09 11:15:53 +08:00
this.options.lineWidth = ((Number(v) || Number(v) === 0) ? Number(v) : 4)
2025-07-03 13:54:01 +08:00
if (!this.entity) {
return
}
this.updateBillboardImage()
}
get pixelOffset() {
return this.options.pixelOffset
}
set pixelOffset(v) {
this.options.pixelOffset = Number(v)
if (!this.entity) {
return
}
this.updateBillboardImage()
}
updateBillboardImage() {
clearTimeout(this.#updateBillboardImageTimeout)
this.#updateBillboardImageTimeout = setTimeout(() => {
clearTimeout(this.#updateBillboardImageTimeout)
this.entity.billboard.image = this.getcanvas()
}, 500)
}
get lineColor() {
return this.options.pixelOffset
}
set lineColor(v) {
2025-07-09 11:15:53 +08:00
this.options.lineColor = v || '#00ffff80'
2025-07-03 13:54:01 +08:00
if (!this.entity) {
return
}
this.entity.billboard.image = this.getcanvas()
}
get backgroundColor() {
return this.options.backgroundColor
}
set backgroundColor(v) {
this.options.backgroundColor = v
if (!this.entity) {
return
}
this.entity.billboard.image = this.getcanvas()
}
get ground() {
return this.options.ground
}
set ground(v) {
this.options.ground = v
}
// get backgroundColorStart() {
// return this.options.backgroundColor[0]
// }
// set backgroundColorStart(v) {
// this.options.backgroundColor[0] = v
// this.entity.billboard.image = this.getcanvas()
// }
// get backgroundColorEnd() {
// return this.options.backgroundColor[1]
// }
// set backgroundColorEnd(v) {
// this.options.backgroundColor[1] = v
// this.entity.billboard.image = this.getcanvas()
// }
getcanvas() {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
ctx.font = this.options.fontSize + 'px ' + this.font
let texts = this.options.text.split('\n')
let canvasWidth = 0
let canvasHeight = 0
for (let i = 0; i < texts.length; i++) {
const text = texts[i]
const width = ctx.measureText(text).width
if (width > canvasWidth) {
canvasWidth = width
}
canvasHeight += this.options.fontSize
}
canvasHeight = canvasHeight + 20 + (texts.length - 1) * 5
canvasWidth = canvasWidth + 30
if (canvasWidth < this.options.lineWidth) {
canvasWidth = this.options.lineWidth
}
canvas.width = canvasWidth
canvas.height = this.options.pixelOffset + canvasHeight
const linearGradient = ctx.createLinearGradient(
0,
0,
canvasWidth,
canvasHeight + 20
)
linearGradient.addColorStop(0, this.options.backgroundColor[0])
linearGradient.addColorStop(1, this.options.backgroundColor[1])
ctx.fillStyle = linearGradient
ctx.fillRect(0, 0, canvasWidth, canvasHeight)
ctx.fillStyle = this.options.color
ctx.font = this.options.fontSize + 'px ' + this.font
let maxWidth = 0
for (let i = 0; i < texts.length; i++) {
let width = ctx.measureText(texts[i]).width
if (maxWidth < width) {
maxWidth = width
}
}
maxWidth = maxWidth + 30
let centerDistance = (canvasWidth - maxWidth) / 2
for (let i = 0; i < texts.length; i++) {
const text = texts[i]
if (this.options.fontSize < 10) {
ctx.fillText(text, 15 + centerDistance, this.options.fontSize * (i + 1) + 10 + i * 5)
} else {
ctx.fillText(
text,
15 + centerDistance,
this.options.fontSize * (i + 1) +
(10 * 10) / this.options.fontSize +
i * 5
)
}
}
// 虚线
ctx.strokeStyle = this.options.lineColor
ctx.setLineDash([4, 4]) //设置虚线长度4间隔为4
ctx.lineWidth = this.options.lineWidth
ctx.beginPath()
ctx.moveTo(canvasWidth / 2, canvasHeight)
ctx.lineTo(canvasWidth / 2, canvasHeight + this.options.pixelOffset)
ctx.stroke()
ctx.closePath()
const canvas2 = document.createElement('canvas')
const ctx2 = canvas2.getContext('2d')
canvas2.width = canvas.width + 10
canvas2.height = canvas.height + 10
ctx2.drawImage(canvas, 5, 5);
// const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// ctx.putImageData(imageData, 40, 40);
return canvas2
}
remove() {
this.sdk.viewer.entities.remove(this.entity)
this.entity = null
}
flicker() { }
}
export default LabelObject
const copyObj = (obj = {}) => {
//变量先置空
let newobj = null
//判断是否需要继续进行递归
if (typeof obj == 'object' && obj !== null) {
newobj = obj instanceof Array ? [] : {} //进行下一层递归克隆
for (var i in obj) {
newobj[i] = copyObj(obj[i])
} //如果不是对象直接赋值
} else newobj = obj
return newobj
}