cesium实现鹰眼图

这个需求算是比较简单的,不涉及到着色器,但鉴于我到的文章都是直接简单的给一个和主视角同步的俯视角小窗 ,而不是给小窗一个大的全局范围,使用entity在这个范围中同步渲染主视角视口位置,所以索性就写一个相关功能的教程,供大家参考

第一步 创建第二个地图

这一段应该没啥好说的,我是用了自己的底图,你可以加载天地图

php 复制代码
async init() {
    this.HawkeyeViewer = new Cesium.Viewer(containerId, {
            animation: false, // 左下角的动画仪表盘
            baseLayerPicker: false, // 右下角的图层选择按钮
            geocoder: false, // 搜索框,
            homeButton: false, // home按钮
            sceneModePicker: false, // 模式切换按钮
            timeline: false, // 底部的时间轴
            navigationHelpButton: false, // 右上角的帮助按钮,
            fullscreenButton: false, // 右下角的全屏按钮
            shouldAnimate: true,
            selectionIndicator: false,
            infoBox: false
        })
        this.HawkeyeViewer.imageryLayers.removeAll() // 清除所有图层
        const env = import.meta.env
        let url = 'abcd1234'
        const layer = await new Cesium.WebMapTileServiceImageryProvider({
            url: url,
            layer: 'nbrmap_nbrmapcgcs2000',
            style: 'default',
            tileMatrixSetID: 'default',
            format: 'image/png',
            tilingScheme: new Cesium.GeographicTilingScheme(),
            maximumLevel: 21,
            tileMatrixLabels: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21']
        })
        this.setimageryLayers(layer) // 重新加载自定义的图层(底图)
        const topLayer = viewer.imageryLayers.get(0)
        topLayer.show = true
        document.querySelector('#'+containerId).querySelector('.cesium-viewer-bottom').remove()
}

第二步 初始化设置

这一步完成鹰眼视角的固定视角和相机前往初始位置,向init函数中添加以下代码

ini 复制代码
        // 前往中心
        this.HawkeyeViewer.camera.flyTo({
            destination: Cesium.Cartesian3.fromDegrees(121.408887, 29.8313129, 75000.0),
        }) 
        // 阻止鹰眼视角被拖动
        let control = this.HawkeyeViewer.scene.screenSpaceCameraController;
        control.enableRotate = false;
        control.enableTranslate = false;
        control.enableZoom = false;
        control.enableTilt = false;
        control.enableLook = false;

第三步 创建展示主视角位置的实体

当鹰眼图底图创建完成之后,在init方法里调用下面方法

csharp 复制代码
    async createRectangleEntity() {
        if(this.rectangleEntity) {
            this.HawkeyeViewer.entities.remove(this.rectangleEntity)
            this.rectangleEntity = null
        }
        // 先不去设置实体的位置
        this.rectangleEntity = new Cesium.Entity({
            id: 'rectangleEntity',
            polygon: new Cesium.PolygonGraphics({
                hierarchy: new Cesium.PolygonHierarchy([]),
                fill: true,
                material: Cesium.Color.ORANGE.withAlpha(0.3),
            }),
            polyline: new Cesium.PolylineGraphics({
                material: Cesium.Color.ORANGE,
                width: 2
            })
        })
        this.HawkeyeViewer.entities.add(this.rectangleEntity)
    }
    createRectangleEntity()

第四步 获取主视口的范围数据

通过屏幕坐标转笛卡尔3坐标,我们能得到左上和右下的经纬度,拆开之后就可以获取到左下,右上的经纬度,将四个位置转为笛卡尔3并放入数组即是实体位置

kotlin 复制代码
getRange() {
        const leftTop = CusMap.windowPositionToCartesian3({x:0,y:0})
        if(!leftTop) return
        const {clientWidth, clientHeight} = this.mainViewer.canvas
        const rightBottom = CusMap.windowPositionToCartesian3({x: clientWidth, y: clientHeight})
        const [l, t] = CusMap.cartesian3ToDegrees(leftTop)
        const [r, b] = CusMap.cartesian3ToDegrees(rightBottom)
        const leftBottom = Cesium.Cartesian3.fromDegrees(l, b)
        const rightTop = Cesium.Cartesian3.fromDegrees(r, t)
        this.rectangleEntity._p = [leftTop, rightTop, rightBottom, leftBottom, leftTop]
    }

这里列一下坐标转换方法

  1. 屏幕坐标转笛卡尔3
javascript 复制代码
static windowPositionToCartesian3(windowPosition, viewer = window.viewer) {
    // 球面
    if (viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider) { // 还是默认的,没有加载地表高程信息
        // 如果选择了椭球或地图,则返回椭球或地图在世界坐标中的点。如果没有选择了椭球或地图,返回undefined。
        return viewer.scene.camera.pickEllipsoid(windowPosition); // windowPosition	Cartesian2	一个像素的x和y坐标。-> Cartesian3
    }
    // 地形
    else {
        const ray = viewer.camera.getPickRay(windowPosition); // 在世界坐标中,通过像素位置windowPosition,从相机位置创建一条射线
        return viewer.scene.globe.pick(ray, viewer.scene); // 拾取射线与地球表面的交点。射线必须以世界坐标表示。→ Cartesian3|undefined
    }
  }
  1. 笛卡尔3转经纬度
ini 复制代码
static cartesian3ToDegrees(cartesian3) {
    const cartographic = Cesium.Cartographic.fromCartesian(cartesian3);
    const longitude = Cesium.Math.toDegrees(cartographic.longitude);
    const latitude = Cesium.Math.toDegrees(cartographic.latitude);
    const height = cartographic.height;
    return [longitude, latitude, height];
  }

第五步 同步实体在鹰眼视角中的位置

createRectangleEntity 方法最后添加以下代码

kotlin 复制代码
        // 完成实体创建后,监听主视角移动事件,获取实体位置
        this.mainViewer.scene.preRender.addEventListener(this.getRange, this) 
        this.updateRectangleEntityPosition() // 调用检测到位置变换就去更新的方法

updateRectangleEntityPosition 使用CallbackProperty实现自动更新

kotlin 复制代码
updateRectangleEntityPosition() {
        this.rectangleEntity.polyline.positions = new Cesium.CallbackProperty(() => {
            if(this.rectangleEntity._p?.length) {
                return this.rectangleEntity._p
            }
        })
        this.rectangleEntity.polygon.hierarchy = new Cesium.CallbackProperty(() => {
            if(this.rectangleEntity._p?.length) {
                return new Cesium.PolygonHierarchy(this.rectangleEntity._p)
            }
        })
    }

最后贴一下完整代码

kotlin 复制代码
import { CusMap } from './map.js'
export default class HawkeyeView {
    constructor(mainViewer, containerId) {
        this.mainViewer = mainViewer
        this.HawkeyeViewer = null
        this.rectangleEntity = null
        this.init(containerId)
    }

    async init(containerId) {
        this.HawkeyeViewer = new Cesium.Viewer(containerId, {
            animation: false, // 左下角的动画仪表盘
            baseLayerPicker: false, // 右下角的图层选择按钮
            geocoder: false, // 搜索框,
            homeButton: false, // home按钮
            sceneModePicker: false, // 模式切换按钮
            timeline: false, // 底部的时间轴
            navigationHelpButton: false, // 右上角的帮助按钮,
            fullscreenButton: false, // 右下角的全屏按钮
            shouldAnimate: true,
            selectionIndicator: false,
            infoBox: false
        })
        this.HawkeyeViewer.imageryLayers.removeAll()
        const env = import.meta.env
        let url = env.VITE_APP_SERVER_URL + 'map/wmts?tilename=yx&imgtype=png&level={TileMatrix}&row={TileRow}&col={TileCol}'
        const layer = await new Cesium.WebMapTileServiceImageryProvider({
            url: url,
            layer: 'nbrmap_nbrmapcgcs2000',
            style: 'default',
            tileMatrixSetID: 'default',
            format: 'image/png',
            tilingScheme: new Cesium.GeographicTilingScheme(),
            maximumLevel: 21,
            tileMatrixLabels: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21']
        })
        this.setimageryLayers(layer)
        const topLayer = viewer.imageryLayers.get(0)
        topLayer.show = true
        this.HawkeyeViewer.camera.flyTo({
            destination: Cesium.Cartesian3.fromDegrees(121.408887, 29.8313129, 75000.0),
        })
        let control = this.HawkeyeViewer.scene.screenSpaceCameraController;
        control.enableRotate = false;
        control.enableTranslate = false;
        control.enableZoom = false;
        control.enableTilt = false;
        control.enableLook = false;
        this.createRectangleEntity()
        document.querySelector('#'+containerId).querySelector('.cesium-viewer-bottom').remove()
    }

    setimageryLayers (options) {
      const layer = this.HawkeyeViewer.scene.imageryLayers
      if(Array.isArray(options) | options instanceof Cesium.ImageryLayer || options instanceof Cesium.ImageryProvider || options instanceof Cesium.WebMapTileServiceImageryProvider) {
        const addImageryLayer = (imagery) =>{
          if (imagery instanceof Cesium.ImageryLayer){
            layer.add(imagery)
          } else if (imagery instanceof Cesium.ImageryProvider){
            layer.addImageryProvider(imagery)
            if (Array.isArray(options)) {
              options.forEach(item => {
                addImageryLayer(item)
              })
            } else {
              addImageryLayer(options)
            }
          } else if (imagery instanceof Cesium.WebMapTileServiceImageryProvider){
            layer.addImageryProvider(imagery)
          }
        }
        addImageryLayer(options)
      }
    }

    getRange() {
        const leftTop = CusMap.windowPositionToCartesian3({x:0,y:0})
        if(!leftTop) return
        const {clientWidth, clientHeight} = this.mainViewer.canvas
        const rightBottom = CusMap.windowPositionToCartesian3({x: clientWidth, y: clientHeight})
        const [l, t] = CusMap.cartesian3ToDegrees(leftTop)
        const [r, b] = CusMap.cartesian3ToDegrees(rightBottom)
        const leftBottom = Cesium.Cartesian3.fromDegrees(l, b)
        const rightTop = Cesium.Cartesian3.fromDegrees(r, t)
        this.rectangleEntity._p = [leftTop, rightTop, rightBottom, leftBottom, leftTop]
    }

    async createRectangleEntity() {
        if(this.rectangleEntity) {
            this.HawkeyeViewer.entities.remove(this.rectangleEntity)
            this.rectangleEntity = null
        }
        this.rectangleEntity = new Cesium.Entity({
            id: 'rectangleEntity',
            polygon: new Cesium.PolygonGraphics({
                hierarchy: new Cesium.PolygonHierarchy([]),
                fill: true,
                material: Cesium.Color.ORANGE.withAlpha(0.3),
            }),
            polyline: new Cesium.PolylineGraphics({
                material: Cesium.Color.ORANGE,
                width: 2
            })
        })
        this.HawkeyeViewer.entities.add(this.rectangleEntity)
        this.mainViewer.scene.preRender.addEventListener(this.getRange, this)
        this.updateRectangleEntityPosition()
    }

    updateRectangleEntityPosition() {
        this.rectangleEntity.polyline.positions = new Cesium.CallbackProperty(() => {
            if(this.rectangleEntity._p?.length) return this.rectangleEntity._p
        })
        this.rectangleEntity.polygon.hierarchy = new Cesium.CallbackProperty(() => {
            if(this.rectangleEntity._p?.length) return new Cesium.PolygonHierarchy(this.rectangleEntity._p)
        })
    }

    destroy() {
        this.mainViewer.scene.preRender.removeEventListener(this.getRange)
        this.HawkeyeViewer.entities.remove(this.rectangleEntity)
        this.rectangleEntity = null
        this.HawkeyeViewer.destroy()
        this.HawkeyeViewer = null
    }
}
相关推荐
云边散步1 小时前
《校园生活平台从 0 到 1 的搭建》第四篇:微信授权登录前端
前端·javascript·后端
讨厌吃蛋黄酥1 小时前
React样式冲突终结者:CSS模块化+Vite全链路实战指南🔥
前端·javascript·react.js
星眠1 小时前
学习低代码编辑器第四天
javascript·面试
Hilaku1 小时前
原生<dialog>元素:别再自己手写Modal弹窗了!
前端·javascript·html
GISer_Jing1 小时前
Coze:字节跳动AI开发平台功能和架构解析
javascript·人工智能·架构·开源
AndyGoWei2 小时前
Web Worker 简单使用,看这篇文章就够了
javascript
OEC小胖胖2 小时前
组件化(一):重新思考“组件”:状态、视图和逻辑的“最佳”分离实践
前端·javascript·html5·web
拾光拾趣录2 小时前
用 Web Worker 计算大视频文件 Hash:从“页面卡死”到流畅上传
前端·javascript
伟大的兔神3 小时前
cesium绘制动态柱状图
前端·gis·cesium
索西引擎3 小时前
HTML5 新特性:MutationObserver 详解
javascript