由于entity加载大批量模型并实现平滑运动时,存在性能瓶颈(500-800以上很卡顿),现如今实现通过primitive方式实现(更底层,效率更高)
1、通过接口获取数据(2-8秒一次)
javascript
if (newValue.trucks) {
newValue.trucks.forEach((value) => {
if (realStore.truckShowSet[value.deviceId]) {
if (value.online) {
setTimeout(() => {
mapView.updateTruckPrimitive(value)
}, 10)
} else {
realStore.truckShowSet[value.deviceId] = false
mapView.showHideEntitySingle(value.deviceId, false)
}
} else {
mapView.showHideEntitySingle(value.deviceId, false)
}
})
}
javascript
updateTruckPrimitive(data) {
let truck = this.trucks.getById(data.deviceId)
if (truck) {
truck.addData(data)
} else {
truck = new Truck(this, data.deviceId)
truck.init()
truck.addData(data)
this.trucks.add(truck)
}
}
2、primitive的初始化和预加载
javascript
constructor(mapView, id, modelUri, options) {
this.mapView = mapView.map
this.mapView.clock.shouldAnimate = true
this.id = id
this.model = null
this.modelLabel = null
this.label = null
this.speed = 0
this.lon = 0
this.lat = 0
this.heading = 0
this.modelUri = modelUri
this.modelScale = 1
this.isDrill = false
this.groundHeight = 0
this.sampledPositionProperty = new Cesium.SampledPositionProperty()
this.velocityVectorProperty = new Cesium.VelocityVectorProperty(this.sampledPositionProperty, true)
if (options) {
this.modelScale = options.scale
this.isDrill = options.isDrill
this.is3d = options.is3d
}
this.cacheTime = null
this.cachePosition = null
this.lastSampledTime = null
this.lastOrientation = null
this.empty = null
this.equalCount = 0
this.lon = 0
this.lat = 0
this.notDeal = false
// this.init()
}
init() {
var that = this
var distanceDisplayCondition = null
if (this.id?.indexOf('.worker') != -1) {
distanceDisplayCondition = new Cesium.DistanceDisplayCondition(undefined, 2000.0)
} else {
distanceDisplayCondition = new Cesium.DistanceDisplayCondition(undefined, 8000.0)
}
this.model = Cesium.Model.fromGltf({
id: this.id,
url: this.modelUri,
scale: this.modelScale,
minimumPixelSize: 32,
maximumScale: 32,
allowPicking: true,
// incrementallyLoadTextures:true,
// maximumScreenSpaceError: 64, // 更高的值=更低的精度
// enableDebugWireframe: false,
distanceDisplayCondition: distanceDisplayCondition,
})
let data = this.mapView.scene.primitives.add(this.model)
data.id = this.id
data.name = this.id
Object.assign(this.model, { isFirstEnter: true })
//worker增加动画
// if (this.id?.indexOf('.worker') != -1) {
// this.model.activeAnimations.addAll({
// loop: Cesium.ModelAnimationLoop.REPEAT,
// animationTime: function (duration) {
// return Date.now() / 1000 / duration;
// },
// multiplier: 0.25,
// });
// }
}
3、实时位置改变和平滑运动
javascript
addData(data) {
if (!data.online) {
return
}
let heading = null
if (data?.heading) {
heading = -data.heading
} else {
heading = 0
}
const isNull = data.longitude == null || data.latitude == null
if (isNull || (floatsEqual(data.longitude, this.lon) && floatsEqual(data.latitude, this.lat))) {
this.notDeal = true
return
}
let height = this.mapView.scene.sampleHeight(Cesium.Cartographic.fromDegrees(data.longitude, data.latitude))
const newPosition = Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude, height)
const currentTime = Cesium.JulianDate.fromDate(new Date());
let text = ''
if (data.deviceId.includes('.mml') || data.deviceId.includes('.truck') || data.deviceId.includes('.cmd')) {
if (data.speed && data.speed > 0) {
data.speed = Math.round(data.speed * 100) / 100
text = data.speed + 'km/h' + '\n' + data.name
} else {
text = data.name
}
} else {
text = data.name
}
// var eyeSet = null
// if (this.id?.indexOf('.worker') != -1 || this.id?.indexOf('.cmd') != -1) {
// eyeSet = new Cesium.Cartesian3(0, 3, 0)
// } else {
// eyeSet = new Cesium.Cartesian3(0, 6, 0)
// }
if (this.model.isFirstEnter) {
// 位置变化
// const height = this.mapView.scene.sampleHeight(Cesium.Cartographic.fromDegrees(data.longitude, data.latitude)) || 0
//设定方向
let orientation = Cesium.Transforms.headingPitchRollQuaternion(newPosition, new Cesium.HeadingPitchRoll(heading, 0, 0))
this.lon = data.longitude
this.lat = data.latitude
this.heading = heading
this.cacheTime = currentTime
this.cachePosition = newPosition
this.model.isFirstEnter = false
this.sampledPositionProperty.addSample(this.cacheTime, newPosition)
this.lastOrientation = orientation
this.lastSampledTime = null
// var modelMatrix = Cesium.Matrix4.fromTranslationQuaternionRotationScale(
// newPosition,
// orientation,
// new Cesium.Cartesian3(1.0, 1.0, 1.0) // 缩放
// )
let textColor = Cesium.Color.fromCssColorString('#000')
let pixelOffset = new Cesium.Cartesian2(0, -50)
if (data.deviceId.includes('.worker')) {
pixelOffset = new Cesium.Cartesian2(0, -70)
}
var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(newPosition)
this.model.modelMatrix = modelMatrix
this.modelLabel = this.mapView.labels.add({
id: this.id + '-label',
position: Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude, height),
text: text,
font: '15px sans-serif',
showBackground: false,
// eyeOffset: eyeSet,
backgroundPadding: new Cesium.Cartesian2(3, 3),
fillColor: textColor,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 3,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
verticalOrigin: Cesium.VerticalOrigin.TOP,
pixelOffset: pixelOffset,
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(undefined, 2000.0),
show: true,
})
this.modelLabel.isRealShow = true
} else {
let duration, startTime
if (!this.lastSampledTime) {
duration = 7.5
startTime = currentTime
} else {
const diff = Cesium.JulianDate.secondsDifference(currentTime, this.lastSampledTime)
if (diff >= 0) {
// 当前时间比采样时间慢了
duration = 7.5
startTime = currentTime
} else if (diff <= -5) {
// 当前时间比采样时间快了超过5秒
duration = 2.5
startTime = this.lastSampledTime
} else {
// 当前时间比采样时间快了不超过5秒
duration = Cesium.JulianDate.secondsDifference(currentTime, this.cacheTime)
startTime = this.lastSampledTime
}
}
const samples = 20
for (let i = 0; i <= samples; ++i) {
const factor = i / samples
const sampleTime = Cesium.JulianDate.addSeconds(startTime, duration * factor, new Cesium.JulianDate())
const samplePosition = Cesium.Cartesian3.lerp(this.cachePosition, newPosition, factor, new Cesium.Cartesian3())
this.sampledPositionProperty.addSample(sampleTime, samplePosition)
this.lastSampledTime = sampleTime
}
// this.sampledPositionProperty.addSample(currentTime, newPosition)
this.cachePosition = newPosition.clone()
this.cacheTime = currentTime
this.lon = data.longitude
this.lat = data.latitude
this.heading = heading
var that = this
this.mapView.clock.onTick.addEventListener(function (clock) {
that.updateModel(clock.currentTime)
})
}
}
updateModel(time) {
const position = this.sampledPositionProperty.getValue(time)
if (!Cesium.defined(position)) return
const velocity = this.velocityVectorProperty.getValue(time)
if (Cesium.defined(velocity)) {
// this.cachePosition == this.newPosition
// 计算朝向(使箭头指向运动方向)
// const heading = Math.atan2(velocity.y, velocity.x) + Cesium.Math.PI_OVER_TWO
const heading = this.calculateHeadingBetweenPoints(this.cachePosition, position)
let orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(heading, 0, 0))
// const orientation = Cesium.Quaternion.fromHeadingPitchRoll(new Cesium.HeadingPitchRoll(heading, 0, 0))
this.lastOrientation = orientation
Cesium.Matrix4.fromTranslationQuaternionRotationScale(position, orientation, new Cesium.Cartesian3(1.0, 1.0, 1.0), this.model.modelMatrix)
} else {
Cesium.Matrix4.fromTranslationQuaternionRotationScale(position, this.lastOrientation, new Cesium.Cartesian3(1.0, 1.0, 1.0), this.model.modelMatrix)
}
this.modelLabel.position = position
}
calculateHeadingBetweenPoints(startPosition, endPosition) {
// 将笛卡尔坐标转换为地理坐标(弧度)
const startCartographic = Cesium.Cartographic.fromCartesian(startPosition);
const endCartographic = Cesium.Cartographic.fromCartesian(endPosition);
const lon1 = startCartographic.longitude;
const lat1 = startCartographic.latitude;
const lon2 = endCartographic.longitude;
const lat2 = endCartographic.latitude;
// 计算经度差
const dLon = lon2 - lon1;
// 使用球面三角公式计算朝向
const y = Math.sin(dLon) * Math.cos(lat2);
const x = Math.cos(lat1) * Math.sin(lat2) -
Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
// 计算角度(-π 到 π)
let heading = Math.atan2(y, x);
// 转换为0到2π范围
heading = (heading + Cesium.Math.PI_OVER_TWO + Cesium.Math.TWO_PI) % Cesium.Math.TWO_PI;
return heading;
}