CesiumJS学习第四章 替换指定3D建筑模型

核心目标

创建 Cesium 应用,用自定义 3D 模型替换真实城市中的现有建筑,可视化拟建建筑的影响(如天际线变化、特定楼层视野等)。

我们将从 Cesium ion 获取全球卫星影像、3D 建筑物和地形数据。Cesium ion 是一个用于流式传输和托管 3D 内容的开放平台。

如果还没有 Cesium ion 账号,请注册一个免费账号。https://ion.cesium.com/

登录后:

进入 "访问令牌"(Access Tokens)页面。

记下默认令牌旁的复制按钮,下一步会用到该令牌。

补充说明

Cesium ion 是流式传输和托管 3D 内容的开放平台,包含经过筛选的全球数据,可用于创建真实世界应用。

添加全球 3D 建筑物和地形

我们为场景添加几个全球图层。你的 Cesium ion 账号默认可访问以下资源:

Cesium 全球地形(Cesium World Terrain)------ 最高精度达 1 米的高分辨率地形。

Cesium 开放街道地图建筑物(Cesium OSM Buildings)------ 源自开放街道地图(OpenStreetMap)数据的 3.5 亿多座建筑物。

必应地图航空影像(Bing Maps Aerial imagery)------ 最高分辨率达 15 厘米的全球卫星影像。

你的应用已默认使用必应地图图层。

在vue3中新的页面顶部导入 CesiumJS 库,通过以下两行代码加载 JavaScript 和 CSS 文件:

javascript 复制代码
import {
 Cartesian3,
  createOsmBuildingsAsync,
  Ion,
  Terrain,
  Viewer,
  Cesium3DTileset,
  GeoJsonDataSource,
  ClassificationType,
  Cesium3DTileStyle,
  Color,
  JulianDate,
  SampledPositionProperty,
  TimeIntervalCollection,
  TimeInterval,
  PathGraphics,
  IonResource,
  VelocityOrientationProperty,
} from 'cesium'
import 'cesium/Build/Cesium/Widgets/widgets.css'

添加场景的 HTML 容器:<div id="cesiumContainer"></div>。

html 复制代码
<div id="cesiumContainer"></div>

初始化查看器:const viewer = new Cesium.Viewer('cesiumContainer');。

javascript 复制代码
let viewer: Viewer | null = null

onMounted(async () => {
  Ion.defaultAccessToken =
    'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1ODQ5MDM2MS0xY2JmLTRhYmUtODYyZC1iNDViMzk2ZDdkZGUiLCJpZCI6MzczNjc0LCJpYXQiOjE3NjcwODE3NjV9.LuYkwE7AwGf01zlwLT9GOo2h5oERVIayz5F0nXwoxAU'
  // 在 ID 为 cesiumContainer 的 HTML 元素中初始化 Cesium 视图器
  viewer = new Viewer('cesiumContainer', {
    terrain: Terrain.fromWorldTerrain(), // 加载全球地形 此处若不加载地形,会导致有些3D建筑 下面是悬空的
  })

 // 让相机飞向美国科罗拉多州丹佛市(指定经纬度和高度)
  viewer.camera.flyTo({
    destination: Cartesian3.fromDegrees(-104.9965, 39.74248, 4000),
  })

  // 添加全球 3D 建筑物图层(Cesium OSM Buildings)
  const buildingTileset = await createOsmBuildingsAsync()
  viewer.scene.primitives.add(buildingTileset)
})

此时你就可以得到一个从3D地球飞行到美国丹佛市效果。

点击并拖动鼠标浏览场景,按住 Ctrl 键拖动可倾斜相机。

标记拟建建筑区域

将以下代码保存为json格式文件。

javascript 复制代码
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              -104.9915474653244,
              39.7357743115534
            ],
            [
              -104.99040752649307,
              39.7357743115534
            ],
            [
              -104.99040752649307,
              39.736754049216195
            ],
            [
              -104.9915474653244,
              39.736754049216195
            ],
            [
              -104.9915474653244,
              39.7357743115534
            ]
          ]
        ]
      }
    }
  ]
}

上传至 Cesium ion 仪表盘,记录预览窗口下方的资源 ID(asset ID)。

此时 我们的onMounted里面的代码是这样:

javascript 复制代码
onMounted(async () => {
  Ion.defaultAccessToken =
    'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1ODQ5MDM2MS0xY2JmLTRhYmUtODYyZC1iNDViMzk2ZDdkZGUiLCJpZCI6MzczNjc0LCJpYXQiOjE3NjcwODE3NjV9.LuYkwE7AwGf01zlwLT9GOo2h5oERVIayz5F0nXwoxAU'
  // 在 ID 为 cesiumContainer 的 HTML 元素中初始化 Cesium 视图器
  viewer = new Viewer('cesiumContainer', {
    terrain: Terrain.fromWorldTerrain(), // 加载全球地形 此处若不加载地形,会导致有些3D建筑 下面是悬空的
  })
  // 让相机飞向美国科罗拉多州丹佛市(指定经纬度和高度)
  viewer.camera.flyTo({
    destination: Cartesian3.fromDegrees(-104.9965, 39.74248, 4000),
  })
  // 添加全球 3D 建筑物图层(Cesium OSM Buildings)
  const buildingTileset = await createOsmBuildingsAsync()
  viewer.scene.primitives.add(buildingTileset)
  
  async function addBuildingGeoJSON() {
    if (!viewer) {
      return
    }
    // 从 Cesium ion 加载 GeoJSON 文件
    const geoJSONURL = await IonResource.fromAssetId(4318422)
    // 基于 GeoJSON 创建几何图形并贴合地面
    const geoJSON = await GeoJsonDataSource.load(geoJSONURL, { clampToGround: true })
    // 将图形添加到场景
    const dataSource = await viewer.dataSources.add(geoJSON)
    // 设置多边形仅贴合地形,不覆盖 3D 建筑物
    for (const entity of dataSource.entities.values) {
      entity.polygon.classificationType = ClassificationType.TERRAIN
    }
    // 移动相机至多边形可视范围
    viewer.flyTo(dataSource)
  }
  addBuildingGeoJSON()
})

效果:地面将显示建筑占地范围,可通过鼠标滚轮缩放或右键拖拽近距离查看。

最终我们得到一个黄思方块在地图上面。

隐藏区域内现有 3D 建筑

将如下代码加入到onMounted 页面挂在函数里面。

javascript 复制代码
// 用 3D Tiles 样式语言隐藏指定建筑
  buildingTileset.style = new Cesium3DTileStyle({
    show: {
      conditions: [
        // 隐藏指定 elementId 的建筑(示例为 5 座小型建筑)
        ['${elementId} === 332469316', false],
        ['${elementId} === 332469317', false],
        ['${elementId} === 235368665', false],
        ['${elementId} === 530288180', false],
        ['${elementId} === 530288179', false],
        // 其他建筑默认显示
        [true, true],
      ],
    },
    // 建筑颜色配置(优先使用自带颜色,无则为白色)
    color:
      "Boolean(${feature['cesium#color']}) ? color(${feature['cesium#color']}) : color('#ffffff')",
  })

我们将得到隐藏了2个小建筑的黄色区域。

上传并定位新建筑模型

1.下载3D建筑模型

2.上传至 Cesium ion 仪表盘

选择3D Model (tile as 3D Tiles) 点击 Upload上传.

得到我们上传的id

点击资源预览窗口顶部的 "调整瓦片集位置",搜索地址 "1250 Cherokee Street"。

点击next,进入下一步编辑,可以看到 我们3D建筑已经被加载到地图里。

通过视图控件调整建筑的经纬度、高度和朝向(参考值:经度 -104.9909、纬度 39.73579、高度 1577、朝向 -8),点击 "保存"。

我们将得到这样一个建筑物。

我们将 onMounted 方法里面的代码变成这下面的:加载指定的3D建筑。

javascript 复制代码
onMounted(async () => {
  Ion.defaultAccessToken =
    'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1ODQ5MDM2MS0xY2JmLTRhYmUtODYyZC1iNDViMzk2ZDdkZGUiLCJpZCI6MzczNjc0LCJpYXQiOjE3NjcwODE3NjV9.LuYkwE7AwGf01zlwLT9GOo2h5oERVIayz5F0nXwoxAU'
  // 在 ID 为 cesiumContainer 的 HTML 元素中初始化 Cesium 视图器
  viewer = new Viewer('cesiumContainer', {
    terrain: Terrain.fromWorldTerrain(), // 加载全球地形 此处若不加载地形,会导致有些3D建筑 下面是悬空的
  })

  // 从 Cesium ion 加载新建筑的 3D Tiles 集
  const newBuildingTileset = await Cesium3DTileset.fromIonAssetId(4318430)
  viewer.scene.primitives.add(newBuildingTileset)
  // 移动相机至新建筑
  viewer.flyTo(newBuildingTileset)
})

项目运行 将得到下面的建筑

最后 我们可以在加一个 按钮来切换这个3d建筑的隐藏和展示

最最最终的代码如下:

javascript 复制代码
<script setup lang="ts">
import { onMounted } from 'vue'
import {
  Cartesian3,
  createOsmBuildingsAsync,
  Ion,
  Terrain,
  Viewer,
  Cesium3DTileset,
  GeoJsonDataSource,
  ClassificationType,
  Cesium3DTileStyle,
  Color,
  JulianDate,
  SampledPositionProperty,
  TimeIntervalCollection,
  TimeInterval,
  PathGraphics,
  IonResource,
  VelocityOrientationProperty,
} from 'cesium'
import 'cesium/Build/Cesium/Widgets/widgets.css'

let viewer: Viewer | null = null
// 从 Cesium ion 加载新建筑的 3D Tiles 集
let newBuildingTileset: Cesium3DTileset
onMounted(async () => {
  Ion.defaultAccessToken =
    'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1ODQ5MDM2MS0xY2JmLTRhYmUtODYyZC1iNDViMzk2ZDdkZGUiLCJpZCI6MzczNjc0LCJpYXQiOjE3NjcwODE3NjV9.LuYkwE7AwGf01zlwLT9GOo2h5oERVIayz5F0nXwoxAU'
  // 在 ID 为 cesiumContainer 的 HTML 元素中初始化 Cesium 视图器
  viewer = new Viewer('cesiumContainer', {
    terrain: Terrain.fromWorldTerrain(), // 加载全球地形 此处若不加载地形,会导致有些3D建筑 下面是悬空的
  })
  newBuildingTileset = await Cesium3DTileset.fromIonAssetId(4318430)
  viewer.scene.primitives.add(newBuildingTileset)
  // 移动相机至新建筑
  viewer.flyTo(newBuildingTileset)
})
const handleToggle = () => {
  newBuildingTileset.show = !newBuildingTileset.show
}
</script>

<template>
  <main>
    <div class="box">
      <div id="cesiumContainer"></div>
      <div class="btns">
        <el-button @click="handleToggle">切换3D建筑显示</el-button>
      </div>
    </div>
  </main>
</template>
<style scoped>
.box {
  position: relative;
}
.btns {
  width: 100%;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
}
</style>

写作不易,大家点个赞在走哦~~~

相关推荐
松涛和鸣26 分钟前
45、无依赖信息查询系统(C语言+SQLite3+HTML)
c语言·开发语言·数据库·单片机·sqlite·html
PieroPc1 小时前
用FastAPI 后端 和 HTML/CSS/JavaScript 前端写一个博客系统 例
前端·html·fastapi
名字越长技术越强1 小时前
html\css\js(一)
javascript·css·html
hunter14501 小时前
2026.1.4 html简单制作
java·前端·笔记·html
海天鹰2 小时前
电机齿轮拉马
3d
鹧鸪云光伏2 小时前
3D光伏支架设计,让项目落地更直观
3d·光伏·光储
fie88892 小时前
基于MATLAB的3D心形图与玫瑰花图案实现
数学建模·matlab·3d
前端小脑虎12 小时前
2026版最新 HTML零基础小白完整版学习指南(通俗易懂+条理清晰+企业主流技术)
html
释怀不想释怀12 小时前
vue布局,动态路由
前端·html