iClent3D for Cesium 实现无人机巡检飞行效果
作者:gaogy
1、背景
随着地理信息技术的发展,三维地球技术逐渐成为了许多领域中的核心工具,尤其是在城市规划、环境监测、航空航天以及军事领域。三维地图和场景的应用正在帮助人们更加直观地理解空间数据,提供更高效的决策支持。
iClient3D for Cesium 是由 SuperMap 提供的一款开发工具,旨在将三维地理信息系统 (3D GIS) 技术应用于大规模的地理信息可视化与分析,帮助开发者通过 Web 平台展示三维地图,还提供了强大的数据分析功能,包括对建筑物、地形、设施等的空间分析。
本文将利用 iClient3D for Cesium 实现三维场景下的无人机巡检飞行效果,模拟无人机的飞行,并能将无人机拍摄范围以光源形式实时展示于三维场景之中,为低空经济与发展提供有效的无人机可视化飞行效果。
2、无人机飞行效果演示
iClent3D for Cesium 实现无人机巡检飞行效
3、实现过程
3.1、初始化 viewer 场景
function initViewer(viewerContainer) {
window.viewer = new Cesium.Viewer(viewerContainer, {
shouldAnimate: true,
useDefaultRenderLoop: true,
infoBox: false,
contextOptions: {
webgl: {
alpha: false,
antialias: true,
preserveDrawingBuffer: true,
failIfMajorPerformanceCaveat: false,
depth: true,
stencil: false,
anialias: false
}
}
})
viewer.imageryLayers.addImageryProvider(
new Cesium.BingMapsImageryProvider({
url: 'https://dev.virtualearth.net',
mapStyle: Cesium.BingMapsStyle.AERIAL,
key: 'AiCyZH6DplpqBK5CViec7lYcLq941OtnIAvlcVojsyxBfZwUDvp0CsHzZr--U2KY'
})
)
viewer.scene.open('http://www.supermapol.com/realspace/services/3D-CBD/rest/realspace')
}
3.2、自定义 Cesium 飞行 Entity 的 vue hook 如下:
import { onMounted } from 'vue'
function getDirection(tagPosition, position) {
return Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(tagPosition, position, new Cesium.Cartesian3()),
new Cesium.Cartesian3()
)
}
function getModelGraphics(options) {
if (options) {
return new Cesium.ModelGraphics({
uri: options.m_url,
scale: options.m_scale || 28,
minimumPixelSize: options.m_minimumPixelSize || 30,
color: options.m_color || Cesium.Color.WHITE
})
}
}
function getLabelGraphics(options) {
if (options && options.l_text) {
return new Cesium.LabelGraphics({
text: options.l_text,
font: options.l_font || '14px sans-serif',
fillColor: options.l_fillColor || Cesium.Color.GOLD,
style: options.l_style || Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: options.l_outlineWidth || 2,
outlineColor: options.l_outlineColor || undefined,
showBackground: options.l_showBackground || false,
backgroundColor: options.l_backgroundColor || new Cesium.Color(0.165, 0.165, 0.165, 0.8),
verticalOrigin: options.l_verticalOrigin || Cesium.VerticalOrigin.BOTTOM,
pixelOffset: options.l_pixelOffset || new Cesium.Cartesian2(0, -30)
//heightReference:Cesium.HeightReference.RELATIVE_TO_GROUND
})
}
}
function getBillboardGraphics(options) {
if (options && options.b_img) {
return new Cesium.BillboardGraphics({
image: options.b_img,
width: options.b_width || 35,
height: options.b_height || 35,
clampToGround: options.b_clampToGround || true,
scale: options.b_scale || 1,
// eyeOffset :new Cesium.Cartesian2(0, -20),
pixelOffset: options.b_pixelOffset || new Cesium.Cartesian2(0, -20),
scaleByDistance: options.b_scaleByDistance || undefined
// heightReference:Cesium.HeightReference.RELATIVE_TO_GROUND
})
}
}
export default function useCustomEntity(options) {
if (window.viewer && options && options.paths) {
const _paths = options.paths
const _positionProperty = new Cesium.SampledPositionProperty()
const _rEntity = new Cesium.Entity()
const _directionProperty = new Cesium.SampledPositionProperty()
const _startTime = new Cesium.JulianDate()
let _direction = null
let _stopTime = null
let _increment = null
let _time = null
if (options.times) {
let _times = options.times - (options.times % (_paths.length - 1))
;(_stopTime = Cesium.JulianDate.addSeconds(_startTime, _times, new Cesium.JulianDate())),
(_increment = _times / (_paths.length - 1))
} else {
_stopTime = Cesium.JulianDate.addSeconds(
_startTime,
(_paths.length - 1) * (options.step || 120),
new Cesium.JulianDate()
)
}
const startTime = options.startTime || _startTime
const stopTime = options.stopTime || _stopTime
window.viewer.clock.startTime = startTime.clone()
window.viewer.clock.currentTime = startTime.clone()
window.viewer.clock.stopTime = stopTime.clone()
window.viewer.clock.multiplier = options.multiplier || 10
window.viewer.clock.clockRange = options.clockRange || Cesium.ClockRange.LOOP_STOP
for (let i = 0; i < _paths.length; i++) {
const cartesian = Cesium.Cartesian3.fromDegrees(_paths[i].lon, _paths[i].lat, _paths[i].alt)
if (options.times) _time = Cesium.JulianDate.addSeconds(startTime, i * _increment, new Cesium.JulianDate())
else _time = Cesium.JulianDate.addSeconds(startTime, _paths[i].time, new Cesium.JulianDate())
_positionProperty.addSample(_time, cartesian)
let directionCartesian = null
if (i === _paths.length - 1) {
_directionProperty.addSample(_time, _direction)
continue
} else {
directionCartesian = Cesium.Cartesian3.fromDegrees(_paths[i + 1].lon, _paths[i + 1].lat, _paths[i + 1].alt)
}
_direction = getDirection(directionCartesian, cartesian)
_directionProperty.addSample(_time, _direction)
}
_rEntity.name = options.name || '漫游Entity'
_rEntity.availability = new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({ start: startTime, stop: stopTime })
])
_rEntity.position = _positionProperty
_rEntity.orientation = new Cesium.VelocityOrientationProperty(_positionProperty)
_rEntity.direction = _directionProperty
if (options.model) _rEntity.model = getModelGraphics(options)
if (options.label) _rEntity.label = getLabelGraphics(options)
if (options.billboard) _rEntity.billboard = getBillboardGraphics(options)
onMounted(() => window.viewer.entities.add(_rEntity))
return { _rEntity }
}
}
3.3、自定义 Cesium 的移动光源的 vue hook 如下:
import { onMounted } from 'vue'
export default function useCustomLight(position, options) {
if (window.viewer && position) {
const DEF_OPTS = {
color: options.color || new Cesium.Color(1, 1, 2, 0.8),
cutoffDistance: options.cutoffDistance || 1000,
decay: options.decay || 0.5,
intensity: options.intensity || 1
}
const _options = options || DEF_OPTS
const customLight = new Cesium.PointLight(position, _options)
onMounted(() => window.viewer.scene.addLightSource(customLight))
return { customLight }
}
}
3.4、向场景添加飞行无人机 Entity
const { _rEntity: flyEntity } = useCustomEntity({
paths: [
{ lon: 116.44596605973072, lat: 39.90275976224633, alt: 400, time: 0 },
{ lon: 116.470769862146, lat: 39.90961660773017, alt: 400, time: 120 },
{ lon: 116.44621270736882, lat: 39.912427615595874, alt: 400, time: 240 },
{ lon: 116.45867843557505, lat: 39.92072065356812, alt: 400, time: 360 },
{ lon: 116.469697344222, lat: 39.91736853889283, alt: 400, time: 480 },
{ lon: 116.46625570699818, lat: 39.91100981903596, alt: 400, time: 600 }
],
model: true,
m_url: '/model/CesiumDrone.gltf',
label: true,
l_text: '无人机一号',
l_outlineWidth: 3,
l_fillColor: Cesium.Color.CYAN
})
3.5、向场景添加移动光源
const { customLight: light } = useCustomLight(flyEntity.position.getValue(viewer.clock.currentTime), {
color: new Cesium.Color(0, 140, 140, 0.8),
cutoffDistance: 500,
decay: 2,
intensity: 5
})
3.6、设置光源跟随无人机 Entity 移动
window.viewer.clock.onTick.addEventListener(() => {
light.position = flyEntity.position.getValue(viewer.clock.currentTime)
})