Cesium鹰眼<利用Cesium本身你的二维地图和三维地图构成>

首先加载Cesium,3D地图和2D哥伦布视图

  • 效果图放前面,是不是你想要的效果。免得浪费宝贵的几分钟
  • 3D地图加载
<template>
  <div id="CesiumViewer">
    <slot></slot>
  </div>
</template>
<script lang="ts" setup>
import * as Cesium from "cesium";
import CesiumNavigation from "cesium-navigation-es6";
import { ref } from "vue";
import { onMounted, onBeforeUnmount } from "vue";
const options = defineProps({
  options: Object,
  navigatorOptions: {
    type: Object,
    default() {
      return {
        show: true,
      };
    },
  },
});

let latLonBounds = { "east": 180.0, "north": 90.0, "south": -90.0, "west": -180.0 };
let rectangle = new Cesium.Rectangle(Cesium.Math.toRadians(latLonBounds.west), Cesium.Math.toRadians(latLonBounds.south),
  Cesium.Math.toRadians(latLonBounds.east), Cesium.Math.toRadians(latLonBounds.north));
const MapConfig = {
  // ION: "",
  global: {
    enableLighting: false, //光照开关
    depthTestAgainstTerrain: true, //高度检测
    highDynamicRange: false, //是否使用高动态范围渲染。物体需要阳光照射出阴影开启
    maximumScreenSpaceError: 4 / 3 //用于驱动细节层次细化的最大屏幕空间误差。较高的值将提供更好的性能,但会降低视觉质量 默认2
  },
  MAPOPTIONS: {
    imageryProvider: 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}',
        minimumLevel: 0,
        maximumLevel: 18
    }), //设置影像图列表
    requestVertexNormals: true,
    shouldAnimate: true,
    geocoder: false, //右上角查询按钮
    shadows: false,
    terrainProviderViewModels: [], //设置地形图列表
    animation: false, //动画小窗口
    baseLayerPicker: false, //图层选择器
    fullscreenButton: false, //全屏
    vrButton: false, //vr按钮
    homeButton: false, //home按钮
    infoBox: false,
    sceneModePicker: false, //2D,2.5D,3D切换
    selectionIndicator: false,
    timeline: false, //时间轴
    navigationHelpButton: false, //帮助按钮
    terrainShadows: Cesium.ShadowMode.DISABLED
  },
};
// Cesium.Ion.defaultAccessToken = MapConfig.ION;
window.Cesium = Cesium;
const $emit = defineEmits(["onViewerLoaded"]);

let beActive = ref<Boolean>(false);
let viewer: Cesium.Viewer | null;
let beforeDestroy = new Cesium.Event();
const destroy = (): void => {
  beforeDestroy.raiseEvent();
  console.warn("cesium destroy!!");
  viewer?.destroy();
  viewer = null;
};
onMounted(() => {
  start()
});
const start = (flag: boolean = false) => {
  viewer = new Cesium.Viewer("CesiumViewer", {
    ...MapConfig.MAPOPTIONS,
    ...options.options,
  });
  (viewer.cesiumWidget.creditContainer as HTMLElement).style.display = "none"; //去除版权信息
  viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(
    Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
  ); //移除双击选中
  let terrainProvider;
  if (options.options?.terrainProvider) {
    terrainProvider = options.options.terrainProvider;
    viewer.terrainProvider = terrainProvider;
  } else {
    // terrainProvider = new Cesium.CesiumTerrainProvider({
    //   url: "/public/Terran"
    // }); 
  }
  //图像显示效果设置
  viewer.scene.highDynamicRange = MapConfig.global.highDynamicRange
  viewer.scene.globe.maximumScreenSpaceError = MapConfig.global.maximumScreenSpaceError
  viewer.scene.globe.enableLighting = MapConfig.global.enableLighting;
  viewer.scene.globe.depthTestAgainstTerrain = MapConfig.global.depthTestAgainstTerrain;
  // 获取要改变的的图层
  // let layer = viewer.scene.imageryLayers.get(1);
  //调整伽马值
  // layer.gamma = 0.66; 
  //改变当前地图的组织结构
  // layer.magnificationFilter = Cesium.TextureMagnificationFilter.NEAREST;


  (viewer as any).frameUpdate = new Cesium.Event();
  let lasTime: null | number;
  viewer.scene.preUpdate.addEventListener((time) => {
    let dateNow = Date.now();
    let deltaTime = lasTime != null ? dateNow - lasTime : 0;
    lasTime = dateNow;
    (viewer as any).frameUpdate.raiseEvent(deltaTime);
  });
  const { navigatorOptions } = options;
  if (navigatorOptions?.show === true) {
    //指南针比例尺
    let CesiumNavigationOptions = {
      // 用于在使用重置导航重置地图视图时设置默认视图控制。接受的值是Cesium.Cartographic 和Cesium.Rectangle.
      defaultResetView:
        navigatorOptions.defaultResetView ||
        Cesium.Cartographic.fromDegrees(115, 30, 2000000),
      // 用于启用或禁用罗盘。true是启用罗盘,false是禁用罗盘。默认值为true。如果将选项设置为false,则罗盘将不会添加到地图中。
      enableCompass: true,
      // 用于启用或禁用缩放控件。true是启用,false是禁用。默认值为true。如果将选项设置为false,则缩放控件 将不会添加到地图中。
      enableZoomControls: true,
      // 用于启用或禁用距离图例。true是启用,false是禁用。默认值为true。如果将选项设置为false,距离图例将不会添加到地图中。
      enableDistanceLegend: true,
      // 用于启用或禁用指南针外环。true是启用,false是禁用。默认值为true。如果将选项设置为false,则该环将可见但无效。
      enableCompassOuterRing: true,
    };
    new CesiumNavigation(viewer, CesiumNavigationOptions);
  }

  window.Viewer = viewer;
  beActive.value = true;
  console.warn("cesium 启动!!");
  if (!flag) $emit("onViewerLoaded", viewer);
}
onBeforeUnmount(() => {
  destroy();
});
defineExpose({ start })
</script>
<style lang="less" scoped>
#CesiumViewer {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}
</style>
  • 2D地图加载
<template>
    <div class="eye" id="eagleEye"></div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted } from 'vue'
import * as Cesium from "cesium";
import { getCurrentExtent, getZoomLevel, getCenterPosition, zoomToAltitude } from '@/components/Cesium/utils/tool'
onMounted(() => {
    initMap()
})
const props = defineProps({
    viewer: {
        type: Cesium.Viewer
    }
})
let eyeViewer: Cesium.Viewer | null
const initMap = () => {
    eyeViewer = new Cesium.Viewer("eagleEye", {
        animation: false,
        baseLayerPicker: false,
        fullscreenButton: false,
        imageryProvider: 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}',
            minimumLevel: 0,
            maximumLevel: 18
        }),
        geocoder: false,
        homeButton: false,
        infoBox: false,
        sceneModePicker: false,
        selectionIndicator: false,
        timeline: false,
        navigationHelpButton: false,
        scene3DOnly: false,
        navigationInstructionsInitiallyVisible: false,
        showRenderLoopErrors: false,
        sceneMode: Cesium.SceneMode.SCENE2D,
    });
    (eyeViewer.cesiumWidget.creditContainer as HTMLElement).style.display = "none"; //去除版权信息
    //限制相机高度范围
    eyeViewer.scene.screenSpaceCameraController.minimumZoomDistance = 500.0
    addListenerEvent()
    syncViewer()
}
let rectangleArr: number[] = []
let entity: Cesium.Entity | undefined
const drawRegionalScope = () => { //绘制区域范围轮廓 
    const extent = getCurrentExtent((props.viewer as Cesium.Viewer)) //相机当前视野范围
    const { xmin, ymax, xmax, ymin } = extent
    rectangleArr = [xmin, ymin,xmin,ymax, xmax, ymax, xmax, ymin, xmin, ymin]
    if (!entity) { 
        entity = eyeViewer?.entities.add({
            polyline: {
                positions: new Cesium.CallbackProperty(() => {
                    return Cesium.Cartesian3.fromDegreesArray(rectangleArr);
                }, false),
                show: true,
                material: Cesium.Color.RED,
                width: 3,
                clampToGround: true //是否贴地
            }
        });
    }
}
let isMapTrigger: boolean = true
let isEyeMapTrigger: boolean = false
const addListenerEvent = () => {
    const viewer: Cesium.Viewer = (props.viewer as Cesium.Viewer);
    const scene = viewer.scene;
    const sceneEye = eyeViewer?.scene;
    const handlerL = new Cesium.ScreenSpaceEventHandler(scene.canvas);
    const handlerR = new Cesium.ScreenSpaceEventHandler(sceneEye?.canvas);
    handlerL.setInputAction(() => {
        isMapTrigger = true;
        isEyeMapTrigger = false;
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    handlerR.setInputAction(() => {
        isMapTrigger = false;
        isEyeMapTrigger = true;
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    viewer.scene.camera.moveEnd.addEventListener(syncViewer); //主地图更新后触发
    eyeViewer?.scene.camera.moveEnd.addEventListener(syncViewerR);//小地图更新后触发
}
const syncViewer = () => {
    if (isMapTrigger) {
        const level = getZoomLevel((props.viewer as Cesium.Viewer).camera) //获取当前层级
        const { lon, lat } = getCenterPosition((props.viewer as Cesium.Viewer)) //获取中心点
        eyeViewer?.camera.setView({
            destination: Cesium.Cartesian3.fromDegrees(lon, lat, zoomToAltitude(level - 1))
        });
        drawRegionalScope()
    }
};
const syncViewerR = () => {
    if (isEyeMapTrigger && eyeViewer) {
        const level = getZoomLevel(eyeViewer.camera)
        const { lon, lat } = getCenterPosition(eyeViewer);
        (props.viewer as Cesium.Viewer).camera.setView({
            destination: Cesium.Cartesian3.fromDegrees(lon, lat, zoomToAltitude(level + 1)),
            orientation: {
                heading: (props.viewer as Cesium.Viewer).camera.heading,
                pitch: (props.viewer as Cesium.Viewer).camera.pitch,
                roll: (props.viewer as Cesium.Viewer).camera.roll,
            }
        });
        drawRegionalScope()
    }
};
onUnmounted(() => {
    (props.viewer as Cesium.Viewer).scene.camera.moveEnd.removeEventListener(syncViewer);
    eyeViewer?.scene.camera.moveEnd.removeEventListener(syncViewerR);
    eyeViewer?.entities.removeAll()
    eyeViewer = null
    entity = undefined
})
</script>
<style lang="less" scoped>
.eye {
    position: absolute;
    right: 25px;
    bottom: 30px;
    border-radius: 50%;
    width: 200px;
    height: 200px;
    overflow: hidden;
    border: 2px solid orange;
    box-shadow: 2px 2px 3px #2b2b2b;
    z-index: 10;
}
</style>

tip:重点就是在两个地图容器更新后同步到另一个地图中,因为鹰眼地图容器小很多,所以鹰眼地图的层级总是比地图容器大一级。

  • 文中用到的函数
/*
@ 相机当前视野范围
*/
export function getCurrentExtent(viewer: Cesium.Viewer) { 
    //范围对象
    let extent = {
        xmin: 0,
        ymax: 0,
        xmax: 0,
        ymin: 0,
        height: 0,
        xminRadian: 0,
        ymaxRadian: 0,
        xmaxRadian: 0,
        yminRadian: 0,
    };
    // 得到当前三维场景
    const scene = viewer.scene;
    // 得到当前三维场景的椭球体
    const ellipsoid = scene.globe.ellipsoid;
    const canvas = scene.canvas;
    // canvas左上角
    const car3_lt = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(0, 0), ellipsoid);
    // canvas右下角
    const car3_rb = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(canvas.width, canvas.height), ellipsoid);
    // 当canvas左上角和右下角全部在椭球体上
    if (car3_lt && car3_rb) {
        const carto_lt = ellipsoid.cartesianToCartographic(car3_lt);
        const carto_rb = ellipsoid.cartesianToCartographic(car3_rb);
        extent.xmin = Cesium.Math.toDegrees(carto_lt.longitude);
        extent.ymax = Cesium.Math.toDegrees(carto_lt.latitude);
        extent.xmax = Cesium.Math.toDegrees(carto_rb.longitude);
        extent.ymin = Cesium.Math.toDegrees(carto_rb.latitude);
        extent.xminRadian = Cesium.Math.toDegrees(carto_lt.longitude);
        extent.ymaxRadian = Cesium.Math.toDegrees(carto_lt.latitude);
        extent.xmaxRadian = Cesium.Math.toDegrees(carto_rb.longitude);
        extent.yminRadian = Cesium.Math.toDegrees(carto_rb.latitude);
    }
    // 当canvas左上角不在但右下角在椭球体上
    else if (!car3_lt && car3_rb) {
        let car3_lt2 = null;
        let yIndex = 0;
        do {
            // 这里每次10像素递加,一是10像素相差不大,二是为了提高程序运行效率
            yIndex <= canvas.height ? yIndex += 10 : canvas.height;
            car3_lt2 = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(0,
                yIndex), ellipsoid);
        } while (!car3_lt2);
        let carto_lt2 = ellipsoid.cartesianToCartographic(car3_lt2);
        let carto_rb2 = ellipsoid.cartesianToCartographic(car3_rb);
        extent.xmin = Cesium.Math.toDegrees(carto_lt2.longitude);
        extent.ymax = Cesium.Math.toDegrees(carto_lt2.latitude);
        extent.xmax = Cesium.Math.toDegrees(carto_rb2.longitude);
        extent.ymin = Cesium.Math.toDegrees(carto_rb2.latitude);
        extent.xminRadian = Cesium.Math.toDegrees(carto_lt2.longitude);
        extent.ymaxRadian = Cesium.Math.toDegrees(carto_lt2.latitude);
        extent.xmaxRadian = Cesium.Math.toDegrees(carto_rb2.longitude);
        extent.yminRadian = Cesium.Math.toDegrees(carto_rb2.latitude);
    } // 获取高度
    extent.height = Math.ceil(viewer.camera.positionCartographic.height);
    return extent;
}

export function getCenterPosition(viewer: Cesium.Viewer) {
    let centerResult = viewer.camera.pickEllipsoid(
        new Cesium.Cartesian2(
            viewer.canvas.clientWidth / 2,
            viewer.canvas.clientHeight / 2,
        ),
    )
    let curLongitude = 0
    let curLatitude = 0
    let height = 0
    if(centerResult) {
        let curPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(centerResult);
        curLongitude = (curPosition.longitude * 180) / Math.PI;
        curLatitude = (curPosition.latitude * 180) / Math.PI;
        height = Math.ceil(viewer.camera.positionCartographic.height);
    }
    return {
        lon: curLongitude,
        lat: curLatitude,
        height
    }
}
export function getZoomLevel(camera: Cesium.Camera) {
    let height = camera.positionCartographic.height;
    const A = 40487.57;
    const B = 0.00007096758;
    const C = 91610.74;
    const D = -40467.74;
    return Math.round(D+(A-D)/(1+Math.pow(height/C, B)));
}

你可能感兴趣的:(Cesium,Vue3,TS,typescript,vue,前端)