cesium 实时地图叠加,实时影像回传绘制

  • 国际惯例效果先放前面,满足往下看不满足跳过。

请添加图片描述
请添加图片描述

应用场景,无人机或者卫星实时传回的影像,实时显示。一般传回来的图需要经过服务器处理,然后再提供给显示端。可以提供所示区域包围盒范围内的地图。蓝色所示区域
在这里插入图片描述
地图是在不断更新中的怎么能保证,加载到最新的地图了?暂时想的是,有新区域影像形成时,通过通信的方式告知需重新绘制地图。即重新加载图层,将前一个图层清除。

//删除指定图层
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);
});

不断添加单一图片图层

  • 不断添加SingleTileImageryProvider图片
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()
         })
     }
 }
}

Frustum代码

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>
你可以使用Cesium叠加多源影像Cesium是一个基于Web的地理信息系统(GIS)库,它可以在3D场景中显示地理数据。要叠加多源影像,你需要先准备好影像数据,并将其转换为Cesium支持的格式,如TMS(Tile Map Service)或WMS(Web Map Service)。然后,你可以使用Cesium的ImageryLayer来加载和显示这些影像。 下面是一个简单的示例代码,展示如何在Cesium叠加多源影像: ```javascript var viewer = new Cesium.Viewer('cesiumContainer'); // 创建一个ImageryLayerCollection对象来存储多个影像图层 var layers = viewer.scene.imageryLayers; // 添加第一个影像图层 var imageryProvider1 = new Cesium.TileMapServiceImageryProvider({ url: 'path/to/imagery1' // 替换为第一个影像的URL }); layers.addImageryProvider(imageryProvider1); // 添加第二个影像图层 var imageryProvider2 = new Cesium.TileMapServiceImageryProvider({ url: 'path/to/imagery2' // 替换为第二个影像的URL }); layers.addImageryProvider(imageryProvider2); // 添加更多的影像图层... // 调整图层顺序(可选) layers.raiseToTop(imageryProvider1); // 将第一个图层置于顶层 ``` 在上面的代码中,你需要将`path/to/imagery1`和`path/to/imagery2`替换为你实际的影像URL。你可以根据需要添加更多的影像图层,然后使用`layers.raiseToTop()`方法调整它们的顺序。 请注意,Cesium还支持其他类型的影像提供者,如WMS。你可以查阅Cesium的文档以了解更多信息和示例代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值