应用场景,无人机或者卫星实时传回的影像,实时显示。一般传回来的图需要经过服务器处理,然后再提供给显示端。可以提供所示区域包围盒范围内的地图。蓝色所示区域
地图是在不断更新中的怎么能保证,加载到最新的地图了?暂时想的是,有新区域影像形成时,通过通信的方式告知需重新绘制地图。即重新加载图层,将前一个图层清除。
//删除指定图层
viewer.imageryLayers.remove(layer, destroy)
//加载指定图层
viewer.imageryLayers.addImageryProvider(layer)
const l = new Cesium.ArcGisMapServerImageryProvider({
url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer",
})
viewer.imageryLayers.addImageryProvider(l)
layer = viewer.imageryLayers.addImageryProvider(new Cesium.UrlTemplateImageryProvider({
url: "http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}"
}))
layer.alpha = 0.7;
layer.cutoutRectangle = Cesium.Rectangle.fromDegrees(
103.759293, 31.098898,
103.765995, 31.102884
);
let mat4: Cesium.Matrix4; let pos: Cesium.Matrix4
; (viewer as any).frameUpdate.addEventListener(() => {
const travelingRectangle = layer.cutoutRectangle;
if (
flags.moveNorth &&
travelingRectangle.north + moveIncrement < Cesium.Math.PI_OVER_TWO
) {
travelingRectangle.north += moveIncrement;
travelingRectangle.south += moveIncrement;
}
if (
flags.moveSouth &&
travelingRectangle.south - moveIncrement > -Cesium.Math.PI_OVER_TWO
) {
travelingRectangle.north -= moveIncrement;
travelingRectangle.south -= moveIncrement;
}
if (flags.moveEast) {
travelingRectangle.east += moveIncrement;
travelingRectangle.west += moveIncrement;
}
if (flags.moveWest) {
travelingRectangle.east -= moveIncrement;
travelingRectangle.west -= moveIncrement;
}
const center = Cesium.Rectangle.center(travelingRectangle, new Cesium.Cartographic())
const car3 = viewer.scene.globe.ellipsoid.cartographicToCartesian(center)
mat4 = Cesium.Matrix4.multiplyByTranslation(
Cesium.Transforms.headingPitchRollToFixedFrame(
car3,
hpRollForUAV,
Cesium.Ellipsoid.WGS84,
fixedFrameTransform
),
new Cesium.Cartesian3(0, 0, 3000),
new Cesium.Matrix4
)
model.modelMatrix = mat4
const center1 = Cesium.Cartographic.clone(center)
center1.height = 3000
const pos1 = viewer.scene.globe.ellipsoid.cartographicToCartesian(center1)
let orientation = Cesium.Transforms.headingPitchRollQuaternion(pos1, hpRoll)
//@ts-ignore
planePrimitive.update(pos1, orientation)
// lookAt()
travelingRectangle.east = wrapLongitude(travelingRectangle.east);
travelingRectangle.west = wrapLongitude(travelingRectangle.west);
});
async function test3() {
function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let lat1 = 31.098747;
let lng1 = 103.770218
let index = 0
for (let i = 0; i < 26; i++) {
lat1 = 31.098747
lng1 += 0.0002
index = 0
for (let j = 0; j < 16; j++) {
lat1 += 0.0002
await sleep(400); // 等待 17没毫秒
var provider = new Cesium.SingleTileImageryProvider({
url: `Map/pengzhou/${206612 + i}_0${88176 + index}.png`,
rectangle: Cesium.Rectangle.fromDegrees(lng1 - 0.0001, lat1 - 0.0001, lng1 + 0.0001, lat1 + 0.0001)
});
index++
const layer = viewer.imageryLayers.addImageryProvider(provider)
layer.alpha = 0.0
let alpha = 0
viewer.imageryLayers.layerAdded.addEventListener(layer => {
const fn = () => {
if (alpha >= 1.0) return
alpha += 0.01
layer.alpha = alpha
requestAnimationFrame(fn)
}
fn()
})
}
}
}
import {
Cartesian3, Property, Primitive, Viewer, PerspectiveFrustum,
Math as CesiumMath, FrustumGeometry, VertexFormat, GeometryInstance,
ColorGeometryInstanceAttribute, Color, PerInstanceColorAppearance,
FrustumOutlineGeometry, Quaternion
} from 'cesium'
type FrustumOtion = {
position: Cartesian3,
orientation?: Property | Quaternion,
fov?: number, //视场(FOV)的角度,单位为弧度。
near?: number, //最近距离
far?: number, //最远距离
aspectRatio?: number, //截头体的宽度与高度的纵横比。
xOffset?:number, //X偏离距离
yOffset?:number //Y偏离距离
}
export class CreateFrustum{
private position: Cartesian3
private viewer: Viewer
private orientation: Property | Quaternion
private fov: number
private near: number
private far: number
private aspectRatio: number
private xOffset: number
private yOffset: number
private frustumPrimitive: Primitive | null = null
private outlinePrimitive: Primitive | null = null
constructor(viewer: Viewer, options: FrustumOtion){
this.viewer = viewer
this.position = options.position;
this.orientation = options.orientation || Quaternion.IDENTITY;
this.fov = options.fov || 30;
this.near = options.near || 10;
this.far = options.far || 100;
this.aspectRatio = options.aspectRatio || viewer.scene.canvas.clientWidth / viewer.scene.canvas.clientHeight;
this.xOffset = options.xOffset || 0
this.yOffset = options.yOffset || 0
this.add();
}
// 更新视锥体的姿态
update(position: Cartesian3, orientation: Property){
this.position = position;
this.orientation = orientation;
this.add();
}
// 创建视锥体和轮廓线
add(){
this.clear();
this.addFrustum();
this.addOutline();
}
// 清除视锥体和轮廓线
clear(){
this.clearFrustum();
this.clearOutline();
}
// 清除视锥体
clearFrustum(){
if(this.frustumPrimitive){
this.viewer.scene.primitives.remove(this.frustumPrimitive);
this.frustumPrimitive = null;
}
}
// 清除轮廓线
clearOutline(){
if(this.outlinePrimitive){
this.viewer.scene.primitives.remove(this.outlinePrimitive);
this.outlinePrimitive = null;
}
}
// 创建视锥体
addFrustum(){
let frustum = new PerspectiveFrustum({
// 查看的视场角,绕Z轴旋转,以弧度方式输入
// fov: Cesium.Math.PI_OVER_THREE,
fov: CesiumMath.toRadians(this.fov),
// 视锥体的宽度/高度
aspectRatio: this.aspectRatio,
// 近面距视点的距离
near: this.near,
// 远面距视点的距离
far: this.far,
xOffset: this.xOffset,
yOffset: this.yOffset,
});
let geometry = new FrustumGeometry({
frustum: frustum,
origin: this.position,
orientation: (this.orientation as any),
vertexFormat: VertexFormat.POSITION_ONLY,
});
let instance = new GeometryInstance({
geometry: geometry,
attributes: {
color: ColorGeometryInstanceAttribute.fromColor(
new Color(1.0, 0.0, 0.0, 0.1)
),
},
});
let primitive = new Primitive({
geometryInstances: instance,
appearance: new PerInstanceColorAppearance({
closed: true,
flat: true,
}),
asynchronous: false,
});
this.frustumPrimitive = this.viewer.scene.primitives.add(primitive);
}
// 创建轮廓线
addOutline(){
let frustum = new PerspectiveFrustum({
// 查看的视场角度,绕Z轴旋转,以弧度方式输入
// The angle of the field of view (FOV), in radians.
// This angle will be used as the horizontal FOV if the width is greater than the height, otherwise it will be the vertical FOV.
fov: CesiumMath.toRadians(this.fov),
// 视锥体的宽度/高度
aspectRatio: this.aspectRatio,
// 近面距视点的距离
near: this.near,
// 远面距视点的距离
far: this.far,
xOffset: this.xOffset,
yOffset: this.yOffset,
});
let geometry = new FrustumOutlineGeometry({
frustum: frustum,
origin: this.position,
orientation: (this.orientation as any),
//@ts-ignore
vertexFormat: VertexFormat.POSITION_ONLY,
});
let instance = new GeometryInstance({
geometry: geometry,
attributes: {
color: ColorGeometryInstanceAttribute.fromColor(
new Color(1.0, 0.0, 0.0, 0.1)
),
},
});
let primitive = new Primitive({
geometryInstances: instance,
appearance: new PerInstanceColorAppearance({
closed: true,
flat: true,
}),
asynchronous: false,
});
this.outlinePrimitive = this.viewer.scene.primitives.add(primitive);
}
}
export default CreateFrustum;
<template>
<Map @onViewerLoaded="onViewerLoaded" :options="options" />
<div class="position-center-top">
<p>
<a-button @click="clear">清除图片</a-button>
<a-button @click="test3">循环加载图片</a-button>
</p>
<p>
lng:
<a-input-number style="width: 200px;" v-model:value="lng" :min="-180" :step="0.0002" :max="180"
@change="change" />
</p>
<p>
lat:
<a-input-number style="width: 200px;" v-model:value="lat" :min="-90" :step="0.0002" :max="90"
@change="change" />
</p>
<p>
alt:
<a-input-number style="width: 200px;" v-model:value="alt" :min="0" :step="1" @change="change" />
</p>
<p>
heading:
<a-slider v-model:value="heading" :min="0" :max="360" @change="change" />
</p>
<p>
pitch:
<a-slider v-model:value="pitch" :min="0" :max="360" @change="change" />
</p>
<p>
roll:
<a-slider v-model:value="roll" :min="0" :max="360" @change="change" />
</p>
<p>
<a-button type="primary" @mousedown="mousedown('S')" @mouseup="mouseup('S')"><template #icon>
<DownOutlined />
</template></a-button>
<a-button type="primary" @mousedown="mousedown('W')" @mouseup="mouseup('W')"><template #icon>
<UpOutlined />
</template></a-button>
<a-button type="primary" @mousedown="mousedown('A')" @mouseup="mouseup('A')"><template #icon>
<LeftOutlined />
</template></a-button>
<a-button type="primary" @mousedown="mousedown('D')" @mouseup="mouseup('D')"><template #icon>
<RightOutlined />
</template></a-button>
</p>
</div>
</template>
<script lang="ts" setup>
import Map from "@/components/Cesium/lib/Map.vue";
import * as Cesium from "cesium";
import type { Option } from '@/components/Cesium/typing'
import Frustum from './Frustum'
import { ref } from 'vue'
import { DownOutlined, UpOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue'
import LCesiumApi from "@lib/main";
//@ts-ignore
const options: Option = {
// terrainProvider: Cesium.createWorldTerrain(),
navigatorOptions: {
show: true
}
}
const flags = {
moveEast: false,
moveWest: false,
moveNorth: false,
moveSouth: false,
};
const lat = ref<number>(31.100891);
const lng = ref<number>(103.762644);
const alt = ref<number>(3000);
const heading = ref<number>(180);
const pitch = ref<number>(180);
const roll = ref<number>(0);
function mousedown(keyCode: string) {
switch (keyCode) {
case "W":
flags["moveNorth"] = true
return "moveNorth";
case "S":
flags["moveSouth"] = true
return
case "D":
flags["moveEast"] = true
return
case "A":
flags["moveWest"] = true
return
}
}
function mouseup(keyCode: string) {
switch (keyCode) {
case "W":
flags["moveNorth"] = false
return "moveNorth";
case "S":
flags["moveSouth"] = false
return
case "D":
flags["moveEast"] = false
return
case "A":
flags["moveWest"] = false
return
}
}
const change = (): void => {
position = Cesium.Cartesian3.fromDegrees(lng.value, lat.value, alt.value)
model.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(
position
);
hpRoll.heading = Cesium.Math.toRadians(heading.value);
hpRoll.roll = Cesium.Math.toRadians(roll.value)
hpRoll.pitch = Cesium.Math.toRadians(pitch.value)
let orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpRoll)
//@ts-ignore
planePrimitive.update(position, orientation)
};
let viewer: Cesium.Viewer
let layer: Cesium.ImageryLayer
let model: Cesium.Model
let position = Cesium.Cartesian3.fromDegrees(103.762644, 31.100891, 3000)
let hpRoll = new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(heading.value), Cesium.Math.toRadians(roll.value), Cesium.Math.toRadians(pitch.value));
let hpRollForUAV = new Cesium.HeadingPitchRoll();
let planePrimitive: Frustum
const fixedFrameTransform = Cesium.Transforms.localFrameToFixedFrameGenerator(
"north",
"west"
);
const onViewerLoaded = (Viewer: Cesium.Viewer) => {
viewer = Viewer
viewer.imageryLayers.removeAll()
const l = new Cesium.ArcGisMapServerImageryProvider({
url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer",
})
viewer.imageryLayers.addImageryProvider(l)
layer = viewer.imageryLayers.addImageryProvider(new Cesium.UrlTemplateImageryProvider({
url: "http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}"
}))
viewer.imageryLayers.layerAdded.addEventListener(function(collection, index, layer1) {
if (layer1 === layer) {
// 图层加载完成后执行的代码
console.log('图层加载完成');
}
});
let p = new LCesiumApi.GetPosition(viewer)
p.getPositionByClick((e: any) => {
console.log(e)
const ellipsoid = viewer.scene.globe.ellipsoid;
const cartographic = ellipsoid.cartesianToCartographic(viewer.camera.position);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
const height = cartographic.height;
console.log(longitude, latitude, height)
// viewer.camera.position = Cesium.Cartesian3.fromDegrees(e.lng, e.lat, e.carmeraAlt)
// console.log(viewer.camera.position)
})
layer.alpha = 0.7;
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(103.759293, 31.098898, 5000),
complete: () => {
loadModel()
}
})
}
function loadModel() {
model = Cesium.Model.fromGltf({
url: 'models/Drone.glb',
scale: 10,
id: 'UAV',
modelMatrix: Cesium.Transforms.headingPitchRollToFixedFrame(
position,
hpRollForUAV,
Cesium.Ellipsoid.WGS84,
fixedFrameTransform
),
})
model.readyPromise.then(() => {
test2()
model.activeAnimations.addAll({
multiplier: 0.5,
loop: Cesium.ModelAnimationLoop.REPEAT,
});
})
viewer.scene.primitives.add(model)
const heading1 = Cesium.Math.toRadians(heading.value);
const pitch1 = Cesium.Math.toRadians(pitch.value);
const roll1 = Cesium.Math.toRadians(roll.value);
const hpr = new Cesium.HeadingPitchRoll(heading1, pitch1, roll1);
planePrimitive = new Frustum(viewer, {
position: position,
far: 2900,
near: 0,
fov: 12,
aspectRatio: 1.5,
orientation: Cesium.Transforms.headingPitchRollQuaternion(
position,
hpr
)
})
}
const lookAt = () => {
const center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, model.boundingSphere.center, new Cesium.Cartesian3());
//保存当前相机位置
// const position = Cesium.Cartesian3.clone(viewer.camera.position);
// const direction = Cesium.Cartesian3.clone(viewer.camera.direction);
// const up = Cesium.Cartesian3.clone(viewer.camera.up);
// //设置当前相机位置定位到模型处
viewer.camera.lookAt(center, new Cesium.Cartesian3(0, 0, 1000));
//在还原相机位置
// Cesium.Cartesian3.clone(position, viewer.camera.position);
// Cesium.Cartesian3.clone(direction, viewer.camera.direction);
// Cesium.Cartesian3.clone(up, viewer.camera.up);
// Cesium.Cartesian3.cross(direction, up, viewer.camera.right);
}
function test2() {
const moveIncrement = 0.000001;
function wrapLongitude(value: number) {
if (value < -Cesium.Math.PI) {
return value + Cesium.Math.TWO_PI;
}
if (value > Cesium.Math.PI) {
return value - Cesium.Math.TWO_PI;
}
return value;
}
layer.cutoutRectangle = Cesium.Rectangle.fromDegrees(
103.759293, 31.098898,
103.765995, 31.102884
);
let mat4: Cesium.Matrix4; let pos: Cesium.Matrix4
; (viewer as any).frameUpdate.addEventListener(() => {
const travelingRectangle = layer.cutoutRectangle;
if (
flags.moveNorth &&
travelingRectangle.north + moveIncrement < Cesium.Math.PI_OVER_TWO
) {
travelingRectangle.north += moveIncrement;
travelingRectangle.south += moveIncrement;
}
if (
flags.moveSouth &&
travelingRectangle.south - moveIncrement > -Cesium.Math.PI_OVER_TWO
) {
travelingRectangle.north -= moveIncrement;
travelingRectangle.south -= moveIncrement;
}
if (flags.moveEast) {
travelingRectangle.east += moveIncrement;
travelingRectangle.west += moveIncrement;
}
if (flags.moveWest) {
travelingRectangle.east -= moveIncrement;
travelingRectangle.west -= moveIncrement;
}
const center = Cesium.Rectangle.center(travelingRectangle, new Cesium.Cartographic())
const car3 = viewer.scene.globe.ellipsoid.cartographicToCartesian(center)
mat4 = Cesium.Matrix4.multiplyByTranslation(
Cesium.Transforms.headingPitchRollToFixedFrame(
car3,
hpRollForUAV,
Cesium.Ellipsoid.WGS84,
fixedFrameTransform
),
new Cesium.Cartesian3(0, 0, 3000),
new Cesium.Matrix4
)
model.modelMatrix = mat4
const center1 = Cesium.Cartographic.clone(center)
center1.height = 3000
const pos1 = viewer.scene.globe.ellipsoid.cartographicToCartesian(center1)
let orientation = Cesium.Transforms.headingPitchRollQuaternion(pos1, hpRoll)
//@ts-ignore
planePrimitive.update(pos1, orientation)
// lookAt()
travelingRectangle.east = wrapLongitude(travelingRectangle.east);
travelingRectangle.west = wrapLongitude(travelingRectangle.west);
});
}
async function test3() {
function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let lat1 = 31.098747;
let lng1 = 103.770218
let index = 0
for (let i = 0; i < 26; i++) {
lat1 = 31.098747
lng1 += 0.0002
index = 0
for (let j = 0; j < 16; j++) {
lat1 += 0.0002
await sleep(400); // 等待 17没毫秒
var provider = new Cesium.SingleTileImageryProvider({
url: `Map/pengzhou/${206612 + i}_0${88176 + index}.png`,
rectangle: Cesium.Rectangle.fromDegrees(lng1 - 0.0001, lat1 - 0.0001, lng1 + 0.0001, lat1 + 0.0001)
});
index++
const layer = viewer.imageryLayers.addImageryProvider(provider)
layer.alpha = 0.0
let alpha = 0
viewer.imageryLayers.layerAdded.addEventListener(layer => {
const fn = () => {
if (alpha >= 1.0) return
alpha += 0.01
layer.alpha = alpha
requestAnimationFrame(fn)
}
fn()
})
}
}
}
function clear() {
// primitives.removeAll()
let a = viewer.imageryLayers.get(0) as any as Cesium.ImageryProvider
let b = viewer.imageryLayers.get(1) as any as Cesium.ImageryProvider
viewer.imageryLayers.removeAll()
viewer.imageryLayers.addImageryProvider(a)
viewer.imageryLayers.addImageryProvider(b)
}
</script>
<style scoped>
p {
color: #fff;
}
</style>