153.在 Vue 3 中使用 OpenLayers + Cesium 实现 2D/3D 地图切换效果

🎬 效果演示截图


✨ 前言

在实际项目开发中,我们经常需要提供「二维地图 + 三维地形」的可视化效果切换,例如:

  • 智慧农业展示耕地分布 + 三维地形起伏;

  • 智慧城市展示建筑物点位 + 三维城市;

  • 数字孪生场景中,2D/3D 切换已是标配功能。

本文将手把手教你如何使用 Vue 3 + OpenLayers + Cesium 结合 ol-cesium 实现一键切换 2D/3D 地图视图。


📦 技术选型说明

工具库 说明
Vue 3 + Composition API 构建 UI 和交互
OpenLayers (ol@10.x) 作为主地图引擎,承载2D地图
Cesium (cesium@1.131) 提供3D地形图、3D建筑、卫星视角等能力
ol-cesium (v2.17) 连接 OpenLayers 和 Cesium 的桥梁
Vite(推荐) 高速构建工具,适配 Cesium 配置更方便

🔧 环境配置

1️⃣ 安装依赖

javascript 复制代码
pnpm add ol cesium ol-cesium

或:

javascript 复制代码
npm install ol cesium ol-cesium

2️⃣ 配置 Cesium 静态资源

推荐方式:使用 vite-plugin-cesium 自动处理 Cesium 资源路径

javascript 复制代码
pnpm add vite-plugin-cesium -D

vite.config.ts 中配置插件:

javascript 复制代码
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import cesium from 'vite-plugin-cesium'

export default defineConfig({
  plugins: [vue(), cesium()]
})

✅ 这样可以让 CESIUM_BASE_URL 自动指向 /cesium/ 路径,无需手动复制 Cesium 静态文件。


💻 核心代码实现

以下是完整的 MapSwitcher.vue 示例组件,支持 2D/3D 地图切换:

🧩 组件结构代码

javascript 复制代码
<!--
 * @Author: 彭麒
 * @Date: 2025/7/15
 * @Email: 1062470959@qq.com
 * @Description: 此源码版权归吉檀迦俐所有,可供学习和借鉴或商用。
 -->
<template>
  <div class="full-screen">
    <div ref="mapContainer" class="map-container"></div>
    <button class="toggle-btn" @click="toggle3D">
      切换到 {{ is3D ? '2D' : '3D' }}
    </button>
  </div>
</template>

<script setup lang="ts">
import {onBeforeUnmount, onMounted, ref} from 'vue'
import 'ol/ol.css'
import {Map, View} from 'ol'
import TileLayer from 'ol/layer/Tile'
import OSM from 'ol/source/OSM'
import {fromLonLat} from 'ol/proj'

// 定义全局变量类型
declare global {
  interface Window {
    CESIUM_BASE_URL: string;
    Cesium: any; // 添加全局Cesium类型
  }
}

// 设置Cesium资源路径 - 使用相对路径
window.CESIUM_BASE_URL = './node_modules/cesium/Build/Cesium';

// 导入Cesium并赋值给全局对象
import * as Cesium from 'cesium'
window.Cesium = Cesium; // 关键步骤:将Cesium设为全局对象

import 'cesium/Build/Cesium/Widgets/widgets.css'
// @ts-ignore
import OLCesium from 'ol-cesium'

const mapContainer = ref<HTMLDivElement | null>(null)
const is3D = ref(false)
let map: Map
let olCesiumObj: any

onMounted(() => {
  // 初始化2D地图
  map = new Map({
    target: mapContainer.value!,
    layers: [new TileLayer({ source: new OSM() })],
    view: new View({
      center: fromLonLat([104.0668, 30.5728]),
      zoom: 5
    })
  })

  try {
    // 确保Cesium已加载
    if (!window.Cesium) {
      console.error('Cesium未正确加载为全局对象')
      return
    }

    // 初始化3D视图
    olCesiumObj = new OLCesium({
      map: map
    })

    // 初始设置为2D模式
    olCesiumObj.setEnabled(false)

    // 设置地形
    initTerrain()
  } catch (error) {
    console.error('初始化Cesium失败:', error)
  }
})

function initTerrain() {
  try {
    if (window.Cesium && window.Cesium.createWorldTerrainAsync) {
      window.Cesium.createWorldTerrainAsync({
        requestWaterMask: true,
        requestVertexNormals: true
      }).then(terrainProvider => {
        if (olCesiumObj && olCesiumObj.getCesiumScene) {
          olCesiumObj.getCesiumScene().terrainProvider = terrainProvider;
        }
      }).catch(error => {
        console.error('加载地形失败:', error);
      });
    }
  } catch (error) {
    console.error('设置地形失败:', error);
  }
}

onBeforeUnmount(() => {
  if (olCesiumObj) {
    olCesiumObj.setEnabled(false)
    olCesiumObj.destroy()
  }
  map?.setTarget(undefined)
})

function toggle3D() {
  is3D.value = !is3D.value
  olCesiumObj?.setEnabled(is3D.value)
}
</script>

<style scoped>
.full-screen {
  position: relative;
  width: 100%;
  height: 100vh;
}
.map-container {
  width: 100%;
  height: 100%;
}
.toggle-btn {
  position: absolute;
  top: 16px;
  left: 16px;
  z-index: 1000;
  padding: 8px 12px;
  background: white;
  border: 1px solid #ccc;
  cursor: pointer;
}
</style>

💡 项目拓展建议

  • ✅ 添加图层切换、绘图、定位等功能;

  • ✅ 使用 Cesium 加载 3D 模型(glTF、倾斜摄影);

  • ✅ 增加鼠标拾取、飞行动画;

  • ✅ 与 GIS 属性数据结合进行业务可视化。


❓常见问题汇总

问题 解决方式
OLCesium is not a constructor 确保导入的是 ol-cesium 并正确写法:import OLCesium from 'ol-cesium'
Cesium 未加载 必须设置 window.Cesium = Cesium,否则 ol-cesium 找不到全局对象
地形不生效 检查 createWorldTerrainAsync 调用是否报错、scene 是否为空
Cesium 路径报错 使用 vite-plugin-cesium 自动设置 CESIUM_BASE_URL,避免路径问题

📚 参考资料


✍️ 结语

如果你也在做地图相关项目,希望本文能帮你快速搭建一个可切换的 2D/3D 地图系统。该示例可直接嵌入业务页面,未来也可以拓展更多可视化能力。

相关推荐
LaoZhangAI几秒前
Kiro vs Cursor:2025年AI编程IDE深度对比
前端·后端
止观止3 分钟前
CSS3 粘性定位解析:position sticky
前端·css·css3
爱编程的喵13 分钟前
深入理解JavaScript单例模式:从Storage封装到Modal弹窗的实战应用
前端·javascript
lemon_sjdk30 分钟前
Java飞机大战小游戏(升级版)
java·前端·python
G等你下课32 分钟前
如何用 useReducer + useContext 构建全局状态管理
前端·react.js
欧阳天羲33 分钟前
AI 增强大前端数据加密与隐私保护:技术实现与合规遵
前端·人工智能·状态模式
慧一居士34 分钟前
Axios 和Express 区别对比
前端
I'mxx42 分钟前
【html常见页面布局】
前端·css·html
快起来别睡了1 小时前
Vuex 与 Pinia:Vue 状态管理详解,小白也能看懂
vue.js