Cesium+Vue3学习教程系列(4)---图层管理功能封装

  • 时间:2025-11-08 22:25 作者: 来源: 阅读:0
  • 扫一扫,手机访问
摘要:本学习系列将以Cesium + Vue3 + Vite +Typescript+elementplus作为主要技术栈展开,后续会循序渐进,持续探索Cesium的高级功能,相关源码全部免费公开。 请关注文中图片右下角gzh,关注后私信“Cesium学习系列源码”,即可看到全部代码。 Cesium能够加载各种GIS数据,如wms、wmts、xyz切片、kml、czml、gltf、glb、3dtiles

本学习系列将以Cesium + Vue3 + Vite +Typescript+elementplus作为主要技术栈展开,后续会循序渐进,持续探索Cesium的高级功能,相关源码全部免费公开。 请关注文中图片右下角gzh,关注后私信“Cesium学习系列源码”,即可看到全部代码。
Cesium能够加载各种GIS数据,如wms、wmts、xyz切片、kml、czml、gltf、glb、3dtiles等。本篇介绍如何将这些加载的数据图层进行封装管理,并以管理底图数据和注记为例进行介绍。

1、新建CesiumViewer类,许多功能都需要用到Cesium里的viewer,这里先简单将其封装,后续待功能增加后再对其进行持续优化。其他功能如果用到viewer只需将CesiumViewer.viewer注入进去即可。

import * as Cesium from "cesium";
import MouseStatusInViewer from "./MouseStatusInViewer";
import { CESIUM_TOKEN, TERRAIN_URL } from "@/system/Config/SystemConfig";
export default class CesiumViewer {
    public static viewer: Cesium.Viewer | undefined
    public static handler: Cesium.ScreenSpaceEventHandler
    public static ellipsoid:Cesium.Ellipsoid
    constructor() { }
    static async CreateViewer(containerId: string) {
        Cesium.Ion.defaultAccessToken = CESIUM_TOKEN
        CesiumViewer.viewer = await CesiumViewer.InitViewer(containerId)
        CesiumViewer.handler = new Cesium.ScreenSpaceEventHandler(CesiumViewer.viewer.canvas)
        CesiumViewer.ellipsoid = CesiumViewer.viewer.scene.globe.ellipsoid
    }
    static async InitViewer(containerId: string) {
        const viewer = new Cesium.Viewer(containerId, {
            baseLayerPicker: false, // 基础影响图层选择器
            navigationHelpButton: false, // 导航协助按钮
            animation: false, // 动画控件
            timeline: false, // 时间控件
            shadows: true, // 显示阴影
            shouldAnimate: true, // 模型动画效果 大气
            skyBox: false,
            infoBox: false, // 显示 信息框
            fullscreenButton: true, // 是否显示全屏按钮
            homeButton: false, // 是否显示首页按钮
            geocoder: false, // 默认不显示搜索栏地址
            sceneModePicker: false, // 是否显示视角切换按钮
            selectionIndicator: false, // 是否显示选择框
        });
        viewer.terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(TERRAIN_URL, {
            requestWaterMask: true,
            requestVertexNormals: true,
        })
        viewer.scene.globe.depthTestAgainstTerrain = true
        // 暂时隐藏 fps
        viewer.scene.debugShowFramesPerSecond = false
        const creditContainer = viewer.cesiumWidget.creditContainer as HTMLElement
        creditContainer.style.display =  none  // 隐藏logo
        //抗锯齿  
        viewer.scene.postProcessStages.fxaa.enabled = true;
        viewer.camera.flyTo({
            destination: Cesium.Cartesian3.fromDegrees(110.535314, 40.960521, 20000000.0),
            duration: 2
        })
        viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
      
        return viewer
    }
 
}

2、新建LayerManager类,用于管理加载的GIS图层。通过layersIdMap 建立ID与数据图层的映射。其他功能模块可能也会用到LayerManager对象,因此这里用了单例模式。通过id对图层进行删除操作。

import { ILayerItem } from "@/system/Common/interfaces";
import * as Cesium from "cesium";
export default class LayerManager {
    private static instance: LayerManager;
    private viewer: Cesium.Viewer
    private layersIdMap = new Map<string, ILayerItem & { handle?: any }>()
    constructor(viewer: Cesium.Viewer) {
        this.viewer = viewer
    }
    // 获取单例实例
    public static getInstance(viewer?: Cesium.Viewer): LayerManager {
        if (!LayerManager.instance) {
            if (!viewer) throw new Error("LayerManager需要传入viewer实例");
            LayerManager.instance = new LayerManager(viewer);
        }
        return LayerManager.instance;
    }
    /* 添加图层 */
    async Add(item: ILayerItem): Promise<void> {
        if (this.layersIdMap.has(item.id)) {
            return
        }
        const { type, show = true, alpha = 1, zIndex = 0 } = item
        let handle: any
        let provider: Cesium.ImageryProvider
        switch (type) {
            /* ---- 影像 ---- */
            case  imagery_wmts :
                provider = new Cesium.WebMapTileServiceImageryProvider({
                    url: item.url!,
                    layer: item.layer!,
                    style: item.style ||  default ,
                    format: item.format ||  image/png ,
                    tileMatrixSetID: item.tileMatrixSetID!,
                });
                handle = this.AddImageryLayer(provider, show, alpha, zIndex)
                break
            case  imagery_wms :
                provider = new Cesium.WebMapServiceImageryProvider({
                    url: item.url!,
                    layers: item.layer!,
                });
                handle = this.AddImageryLayer(provider, show, alpha, zIndex)
                break
            case  imagery_xyz :
                provider = new Cesium.UrlTemplateImageryProvider({ url: item.url! })
                handle = this.AddImageryLayer(provider, show, alpha, zIndex)
                break
            /* ---- 地形 ---- */
            case  terrain :
                this.viewer.terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(
                    item.url!,
                    {
                        requestWaterMask: item.requestWaterMask ?? false,
                        requestVertexNormals: item.requestVertexNormals ?? false,
                    }
                );
                handle = { __terrain: true }; // 占位,方便 remove 时切回 ellipsoid
                break
            /* ---- 矢量 ---- */
            case  geojson :
                handle = await Cesium.GeoJsonDataSource.load(item.url!, {
                    clampToGround: true,
                });
                (handle as Cesium.GeoJsonDataSource).show = show;
                this.viewer.dataSources.add(handle)
                break
            case  kml :
                handle = await Cesium.KmlDataSource.load(item.url!, {
                    clampToGround: true,
                });
                (handle as Cesium.KmlDataSource).show = show
                this.viewer.dataSources.add(handle)
                break
            case  czml :
                handle = await Cesium.CzmlDataSource.load(item.url!);
                (handle as Cesium.CzmlDataSource).show = show
                this.viewer.dataSources.add(handle)
                break
            /* ---- 3DTiles ---- */
            case  3dtiles :
                handle = await Cesium.Cesium3DTileset.fromUrl(item.url!)
                handle.show = show;
                this.viewer.scene.primitives.add(handle)
                break
            default:
                throw new Error(`[LayerManager] 未知类型 ${type}`)
        }
        this.layersIdMap.set(item.id, { ...item, handle })
    }
    /**增加影像数据 */
    AddImageryLayer(provider: Cesium.ImageryProvider, show = true, alpha = 1, zIndex = 0) {
        let handle = this.viewer.imageryLayers.addImageryProvider(provider)
        handle.alpha = alpha
        handle.show = show
        this.viewer.imageryLayers.raiseToTop(handle) // 先置顶,再按 zIndex 微调
        if (zIndex)
            this.SetImageryLayerIndex(handle, zIndex)
        return handle
    }
    /**设置影像数据的叠加顺序 */
    SetImageryLayerIndex(layer: Cesium.ImageryLayer, targetIndex: number) {
        const imageryLayers = this.viewer.imageryLayers
        const curIndex = imageryLayers.indexOf(layer)
        if (curIndex === targetIndex) return
        if (targetIndex === 0) {
            imageryLayers.lowerToBottom(layer)          // 直接到底
        } else if (targetIndex === imageryLayers.length - 1) {
            imageryLayers.raiseToTop(layer)            // 直接到顶
        } else {
            // 逐层移动,直到索引匹配
            while (imageryLayers.indexOf(layer) > targetIndex) imageryLayers.lower(layer)
            while (imageryLayers.indexOf(layer) < targetIndex) imageryLayers.raise(layer)
        }
    }
    /* 移除图层 */
    Remove(id: string): void {
        const item = this.layersIdMap.get(id)
        console.log(item)
        if (!item) return;
        const { type, handle } = item;
        switch (type) {
            case  imagery_wms :
            case  imagery_wmts :
            case  imagery_xyz :
                this.viewer.imageryLayers.remove(handle)
                break
            case  terrain :
                // 回到默认椭球
                this.viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider()
                break
            case  geojson :
            case  kml :
            case  czml :
                this.viewer.dataSources.remove(handle)
                break;
            case  3dtiles :
                this.viewer.scene.primitives.remove(handle)
                break;
        }
        this.layersIdMap.delete(id);
    }
    RemoveAllImageLayer() {
        this.viewer.imageryLayers.removeAll()
    }
}

3、管理底图数据和注记。通过mLayerManager = LayerManager.getInstance(viewer)创建对象,然后在UI中调用。

<template>
    <div class="layer-manager-container">
        <span class="layer-manager-title"> 全球数据 </span>
        <div style="text-align: left;">
            <div class="layer-items">
                <span>底图</span>
                <div style="margin-left:20px;margin-bottom: 10px;margin-top:5px;">
                    <el-radio-group v-model="imageDataRadio" size="small" @change="changeImageData">
                        <el-radio-button value="bingImage">Bing影像</el-radio-button>
                        <el-radio-button value="tdtImage"> 天地图-影像</el-radio-button>
                        <el-radio-button value="tdtVec">天地图-地图</el-radio-button>
                    </el-radio-group>
                </div>
            </div>
            <div class="layer-items">
                <span>注记</span>
                <div>
                    <el-checkbox v-model="tdtAnnotationChecked" label="天地图-注记" size="large" @change="changeTdtAnnotation"/>
                </div>
            </div>
        </div>
    </div>
</template>
<script lang="ts" setup>
import { LayerIdFlag, tdtAnnotationInfo, tdtImageLayerInfo, tdtVecLayerInfo } from  @/system/LayerManager/LayerConfig 
import LayerManager from  @/system/LayerManager/LayerManager 
import CesiumViewer from  @/Viewer/CesiumViewer 
const imageDataRadio = ref( bingImage )
const tdtAnnotationChecked = ref(false)
const viewer = CesiumViewer.viewer
let mLayerManager: LayerManager | null = null
onMounted(() => {
    mLayerManager = LayerManager.getInstance(viewer!)
})
const changeImageData = (val: string | number | boolean | undefined) => {
    imageDataRadio.value = val as string
    // mLayerManager?.RemoveAllImageLayer()
    switch (val) {
        case  bingImage :
            mLayerManager?.Remove(LayerIdFlag.TDT_IMAGERY_WMTS)
            mLayerManager?.Remove(LayerIdFlag.TDT_VECTOR_WMTS)
            break
        case  tdtImage :
            mLayerManager?.Remove(LayerIdFlag.TDT_VECTOR_WMTS)
            mLayerManager?.Add(tdtImageLayerInfo)
            break
        case  tdtVec :
            mLayerManager?.Remove(LayerIdFlag.TDT_IMAGERY_WMTS)
            mLayerManager?.Add(tdtVecLayerInfo)
            break
        default:
            break
    }
}
const changeTdtAnnotation = (val: string | number | boolean) => {
    tdtAnnotationChecked.value = val as boolean
    if (val) {
        mLayerManager?.Add(tdtAnnotationInfo)
    } else {
        mLayerManager?.Remove(LayerIdFlag.TDT_ANNOTATION_WMTS)
    }
}
</script>
<style lang="scss" scoped>
.layer-manager-container {
    padding: 10px;
    .layer-manager-title {
        font-size: 16px;
        font-weight: bold;
        margin-bottom: 10px;
    }
    .image-radio-container {
        text-align: left;
    }
    .layer-items {
        .el-checkbox {
            margin-left: 20px;
            height: 30px;
        }
        :deep(.el-checkbox__label) {
            color: white;
        }
    }
}
</style>

效果如下:


Cesium+Vue3学习教程系列(4)---图层管理功能封装

  • 全部评论(0)
手机二维码手机访问领取大礼包
返回顶部