作者: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.pathsconst _positionProperty = new Cesium.SampledPositionProperty()const _rEntity = new Cesium.Entity()const _directionProperty = new Cesium.SampledPositionProperty()const _startTime = new Cesium.JulianDate()let _direction = nulllet _stopTime = nulllet _increment = nulllet _time = nullif (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 || _startTimeconst stopTime = options.stopTime || _stopTimewindow.viewer.clock.startTime = startTime.clone()window.viewer.clock.currentTime = startTime.clone()window.viewer.clock.stopTime = stopTime.clone()window.viewer.clock.multiplier = options.multiplier || 10window.viewer.clock.clockRange = options.clockRange || Cesium.ClockRange.LOOP_STOPfor (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 = nullif (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 = _directionPropertyif (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_OPTSconst 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)
})