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>

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

相关推荐
爱上好庆祝6 小时前
学习js的第五天
前端·css·学习·html·css3·js
前端老石人7 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
jingqingdai39 小时前
别用正则格式化 HTML!我用 DOM 遍历实现零风险本地格式化,老项目重构效率直接拉满
前端·重构·html
a11177611 小时前
“像风之翼“无人机巡检平台仪表盘
前端·javascript·开源·html·无人机
a11177611 小时前
QQ 宠物(怀旧 开源)前端electron项目
前端·开源·html
ZC跨境爬虫11 小时前
跟着 MDN 学 HTML day_8:(高级文本语义标签+适配核心功底)
前端·css·笔记·ui·html
Dxy123931021611 小时前
HTML中的伪类详解:从基础到高级应用的全面指南
前端·html
Dxy123931021611 小时前
HTML中如何设置元素样式:从基础到进阶的完整指南
前端·html
bzmK1DTbd11 小时前
OpenGL与Java:JOGL库的3D图形渲染实战
java·3d·图形渲染
DFT计算杂谈1 天前
VASP官方教程 TRIQS DFT+DMFT计算教程
运维·css·自动化·html·css3