核心目标
创建 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个小建筑的黄色区域。

上传并定位新建筑模型
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>

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