• Cesium随笔:鹰眼功能


    0.前言

    leaf2vue.png

    本文记录一下Cesium引擎鹰眼功能的实现过程,项目采用vue框架(修改自cesiumlab的开源项目),小地图采用leaflet。阅读本文需要Cesium和Vue相关知识。

    gifeditor_20190516_232158.gif

    1.基本思路

    鹰眼的基本需求是:在三维地图中镶嵌一个迷你二维地图,在迷你地图中绘制三维地图中用户的可视域、鼠标的位置等,并且随着三维地图画面的更新二维地图也跟着实时变化。
    首先是二三维地图的实时联动问题,通过监听Cesium的postRender或者鼠标移动事件,将数据变化实时更新到二维地图即可。
    然后是二维地图的选择,需求只需要绘制简单的几何图形,因此这里选择简单好用的leaflet地图。
    最后是二维地图绘制逻辑,经过试验,笔者采用二维地图实时聚焦在三维地图鼠标坐标的方式,并且在二维地图中绘制出三维摄像头的可见范围,二维地图的缩放级别随着三维摄像机的离地高度改变。

    2.leaflet2vue

    项目使用leaflet2vue
    组件安装:
    npm install vue2-leaflet leaflet --save
    在小地图组件中使用leaflet:

    <template>
        <div class="vue-leaflet">
            <l-map style="position: absolute; top: 110px; right: 10px; padding: 0px;  250px; height: 250px" :zoom="zoom"
             :center="center">
                <l-tile-layer :url="url" :attribution="attribution"></l-tile-layer>
                <l-marker :lat-lng="marker">
                    <l-popup :content="text"></l-popup>
                </l-marker>
                <l-polygon :lat-lngs="viewRect" :color="viewRectColor">
                </l-polygon>
            </l-map>
        </div>
    </template>
    
    <script>
        import {
            LMap,
            LTileLayer,
            LMarker,
            LPopup,
            LPolygon
        } from 'vue2-leaflet'
    
        export default {
            name: 'leafletMini',
            components: {
                LMap,
                LTileLayer,
                LMarker,
                LPopup,
                LPolygon
            },
            data() {
                return {
                    url: 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
                    attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
                    text: '光标的位置',
                    viewRectColor: 'orange'
                }
            },
            props: ['center', 'marker', 'zoom', 'viewRect']
        }
    </script>
    
    
    

    在本例中几乎不需要对leaflet进行任何编程,只需要把地图中心、大头针、视域多边形的数据绑定给相应的组件即实现了leaflet负责的功能。

    3.Cesium与leaflet联动

    在Cesium窗口中添加小地图组件

    <template>
        <div style=" 100%; height: 100%">
            <div style="position: relative;  100%; height: 100%" ref="coreViewer">
                <div ref="viewer" style=" 100%; height: 100%" id="viewer"></div>
         
                <leafletMini :center="minimapCenter" :marker="minimapCrusor" :zoom="minimapZoom" :viewRect="minimapViewRect"/>
            </div>
        </div>
    </template>
    
    <script>
    import bindMinimap from '../scripts/eagleEye';
    import leafletMini from '../components/leafletMini.vue';
    import {
            LMap,
            LTileLayer,
            LMarker,
            LPopup
        } from 'vue2-leaflet'
    export default {
        name: "CesiumView",
        components: {
            'leafletMini': leafletMini
        },
        mounted() {
            this.$nextTick(() => {
                const viewer = initViewer(this.$refs.viewer);
    
                this.freezedViewer = Object.freeze({viewer});
    
                var that = this;
                document.addEventListener(Cesium.Fullscreen.changeEventName, () => {
                    that.isFullscreen = Cesium.Fullscreen.fullscreen;
                });
            });
        },
        data() {
            return {
                freezedViewer: undefined,
                minimapCenter: undefined,
                minimapZoom: 2,
                minimapCrusor: L.latLng(0.0, 0.0),
                minimapViewRect:[]
            };
        },
        methods: {
            
            bindEagleEyeOnMinimap(){
                let h=parseInt(window.getComputedStyle(this.$refs.coreViewer).height);
                let w=parseInt(window.getComputedStyle(this.$refs.coreViewer).width);
                bindMinimap(this.freezedViewer && this.freezedViewer.viewer,(x,y,zoom,viewRectCoords)=>{
                    this.minimapCenter=L.latLng(y, x);
                    this.minimapZoom=zoom;
                    this.minimapCrusor=L.latLng(y, x);
                    this.minimapViewRect=viewRectCoords;
                },w,h);
            }
        }
    };
    </script>
    
    

    实现联动的代码:eagleEye.js

    import Cesium from 'Cesium';
    //启动鹰眼功能
    function bindMinimap(cesiumViewer, funcWithCursorPos,windowWidth,windowHeight) {
        var handler = new Cesium.ScreenSpaceEventHandler(cesiumViewer.scene.canvas);
        handler.setInputAction(function(movement) {
            let dynamicPosition = undefined;
            let ray = cesiumViewer.camera.getPickRay(movement.endPosition);
            dynamicPosition = cesiumViewer.scene.globe.pick(ray, cesiumViewer.scene);
            let corners=getViewRect(cesiumViewer,cesiumViewer.scene.camera,windowWidth,windowHeight);
            if (Cesium.defined(dynamicPosition)) {
                funcWithCursorPos(Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(dynamicPosition).longitude),
                    Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(dynamicPosition).latitude),
                    getZoomLevel(cesiumViewer.scene.camera),
                    corners
                );
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }
    //计算相机视域
    function getViewRect(cesiumViewer,camera,windowWidth,windowHeight){
        let cornerPos = undefined;
        let ray=undefined;
        let positions=[];
        
        ray = cesiumViewer.camera.getPickRay(new Cesium.Cartesian2(0,0));
        cornerPos = cesiumViewer.scene.globe.pick(ray, cesiumViewer.scene);
        if (!Cesium.defined(cornerPos)){
            return [];
        }
        positions.push([Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cornerPos).latitude),
        Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cornerPos).longitude)]);
        
        ray = cesiumViewer.camera.getPickRay(new Cesium.Cartesian2(0,windowHeight));
        cornerPos = cesiumViewer.scene.globe.pick(ray, cesiumViewer.scene);
        if (!Cesium.defined(cornerPos)){
            return [];
        }
        positions.push([Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cornerPos).latitude),
        Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cornerPos).longitude)]);
        
        ray = cesiumViewer.camera.getPickRay(new Cesium.Cartesian2(windowWidth,windowHeight));
        cornerPos = cesiumViewer.scene.globe.pick(ray, cesiumViewer.scene);
        if (!Cesium.defined(cornerPos)){
            return [];
        }
        positions.push([Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cornerPos).latitude),
        Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cornerPos).longitude)]);
        
        ray = cesiumViewer.camera.getPickRay(new Cesium.Cartesian2(windowWidth,0));
        cornerPos = cesiumViewer.scene.globe.pick(ray, cesiumViewer.scene);
        if (!Cesium.defined(cornerPos)){
            return [];
        }
        positions.push([Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cornerPos).latitude),
        Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cornerPos).longitude)]);
        
        return positions
    }
    //计算地图缩放等级
    function getZoomLevel(camera) {
        let h = camera.positionCartographic.height;
        if (h <= 100) { //0.01
            return 19;
        } else if (h <= 300) { //0.02
            return 18;
        } else if (h <= 660) { //0.05
            return 17;
        } else if (h <= 1300) { //0.1
            return 16;
        } else if (h <= 2600) { //0.2
            return 15;
        } else if (h <= 6400) { //0.5
            return 14;
        } else if (h <= 13200) { //1
            return 13;
        } else if (h <= 26000) { //2
            return 12;
        } else if (h <= 67985) { //5
            return 11;
        } else if (h <= 139780) { //10
            return 10;
        } else if (h <= 250600) { //20
            return 9;
        } else if (h <= 380000) { //30
            return 8;
        } else if (h <= 640000) { //50
            return 7;
        } else if (h <= 1280000) { //100
            return 6;
        } else if (h <= 2600000) { //200
            return 5;
        } else if (h <= 6100000) { //500
            return 4;
        } else if (h <= 11900000) { //1000
            return 3;
        } else {
            return 2;
        }
    }
    export default bindMinimap;
    
    

    4.总结

    Vue绑定数据很方便,通过Props就能把Cesium主窗口的数据绑定给miniMap子窗口,不需要写多余的代码。包括小地图的中心位置、鼠标坐标、视域范围都是这样同步过来的,且感受不到任何延迟。
    具体绑定过程:
    1.在Cesium组件声明小地图数据

    image.png

    2.在小地图组件中声明Props

    image.png

    3.在Cesium窗口中向Minimap绑定Props和自己的data

    image.png

    4.在miniMap组件把数据绑定到leaflet

    image.png

    经过一番折腾,在Cesium窗口中改变绑定的这些data,leaflet小地图就能实时变化了。

    本文转自 https://www.jianshu.com/p/e6f3d171a2d1,如有侵权,请联系删除。

  • 相关阅读:
    Java硬件同步机制Swap指令模拟+记录型信号量模拟
    算法(第四版)练习 1.1.26 ~ 1.1.31
    C++ 电路布线/最短路径问题
    线性代数笔记
    算法导论(第三版)练习 2.2-1 ~ 2.2-4
    条款45: 弄清C++在幕后为你所写、所调用的函数
    条款42: 明智地使用私有继承
    条款41: 区分继承和模板
    【python】字符遍历
    【python】range的用法
  • 原文地址:https://www.cnblogs.com/hustshu/p/15627486.html
Copyright © 2020-2023  润新知