在本教程中,您将学习如何创建 Cesium 应用程序,用您自己的 3D 模型替换真实城市中的建筑物。您可以使用它来可视化拟建建筑的影响,及如何改变天际线?从特定楼层或房间看到的景色会是什么样子?
我们将介绍如何:
-
在网络上设置并部署您的 Cesium 应用程序。
-
添加全球 3D 建筑物、地形和图像的基础图层。
-
隐藏单个建筑物并用您自己的 3D 模型替换它们。
开始之前
我们将从 Cesium ion(一个用于流式传输和托管 3D 内容的开放平台)获取全球卫星图像、3D 建筑物和地形。
如果您还没有免费的Cesium ion帐户,请注册一个。
登录后:
-
转到您的 访问令牌 选项卡。
-
请注意默认令牌旁边的复制按钮。我们将在下一步中使用此令牌。
1 设置你的Cesium应用程序
我们将使用 CesiumJS(一个开源 JavaScript 引擎)创建我们的应用程序。我们将使用 Glitch(一个在线 IDE)来托管我们的应用程序。
1 使用我们组合的基本模板创建一个新的 Glitch 项目。
2单击 左侧面板中的index.html以查看应用程序的代码。
3替换 为您的令牌页面your_token_here
中的访问令牌 。
4单击 顶部的 “显示”并选择“代码旁边”来运行应用程序。
到目前为止,index.html中的代码 做了三件事:
-
导入 CesiumJS 库。JavaScript 和 CSS 文件在这两行中加载:
<script src="https://cesium.com/downloads/cesiumjs/releases/1.115/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.115/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
-
为场景添加一个 HTML 容器:
<div id="cesiumContainer"></div>
。 -
用 ; 初始化查看器
const viewer = new Cesium.Viewer('cesiumContainer')
。
现在,您的浏览器中运行了一个基本的 CesiumJS 应用程序,其中包含来自 Cesium ion 的全球卫星图像。
配置自动刷新
每次代码更改时,Glitch 都会自动刷新页面。您可以通过单击左上角的项目名称并取消选中此框来切换此选项:
使用应用程序窗口顶部的刷新按钮重新运行应用程序:
2添加Cesium OSM建筑物和Cesium世界地形
Cesium OSM Buildings是一个全球基础层,拥有来自 OpenStreetMap 数据的超过 3.5 亿座建筑物。它被用作 3D Tiles,这是由 Cesium 创建的开放标准,可以将 3D 内容流式传输到任何兼容的客户端。
让我们添加这些图层,然后将摄像机移动到我们虚构的新建筑将位于的城市 - 美国科罗拉多州丹佛市。
1将index.html中的 JavaScript 代码替换 为以下代码,保留之前的访问令牌行。
2单击并拖动以移动相机。按住 CTRL 键的同时拖动可倾斜。
3单击任何建筑物以查看其元数据。
// Keep your Cesium.Ion.defaultAccessToken = 'your_token_here' line above.
// STEP 2 CODE
// Initialize the viewer with Cesium World Terrain.
const viewer = new Cesium.Viewer('cesiumContainer', {
terrain: Cesium.Terrain.fromWorldTerrain(),
});
// Fly the camera to Denver, Colorado at the given longitude, latitude, and height.
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(-104.9965, 39.74248, 4000)
});
// Add Cesium OSM Buildings.
const buildingsTileset = await Cesium.createOsmBuildingsAsync();
viewer.scene.primitives.add(buildingsTileset);
此时,完整的index.html将如下所示(访问令牌除外)。在后续步骤中,您将在标记内的现有代码下方添加新代码。
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.115/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.115/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<link href="style.css" rel="stylesheet">
</head>
<body>
<div id="cesiumContainer"></div>
<script type="module">
// Your access token can be found at: https://ion.cesium.com/tokens.
// Replace `your_access_token` with your Cesium ion access token.
Cesium.Ion.defaultAccessToken = 'your_access_token';
// Keep your Cesium.Ion.defaultAccessToken = 'your_token_here' line above.
// STEP 2 CODE
// Initialize the viewer with Cesium World Terrain.
const viewer = new Cesium.Viewer('cesiumContainer', {
terrain: Cesium.Terrain.fromWorldTerrain(),
});
// Fly the camera to Denver, Colorado at the given longitude, latitude, and height.
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(-104.9965, 39.74248, 4000)
});
// Add Cesium OSM Buildings.
const buildingsTileset = await Cesium.createOsmBuildingsAsync();
viewer.scene.primitives.add(buildingsTileset);
</script>
</body>
</html>
Cesium OSM Buildings 被固定在全球高分辨率 3D 地形层Cesium World Terrain上。这使得它非常适合需要精确建筑高度的应用,例如洪水分析工具。
3确定新建筑面积
在添加新建筑物之前,让我们添加一个 GeoJSON 文件来标记它的占地面积。这将向我们展示哪些现有建筑物需要拆除。
1下载 GeoJSON 文件。
2将 GeoJSON 文件拖放到 Cesium ion 仪表板中。
3按 UPLOAD。
4上传后,记下预览窗口下的3D对象 ID。
1在index.html中添加以下代码。
-
替换
your_asset_id
为您的3D对象ID。ID 是一个数字,因此不需要引号。
// STEP 3 CODE
async function addBuildingGeoJSON() {
// Load the GeoJSON file from Cesium ion.
const geoJSONURL = await Cesium.IonResource.fromAssetId(your_asset_id);
// Create the geometry from the GeoJSON, and clamp it to the ground.
const geoJSON = await Cesium.GeoJsonDataSource.load(geoJSONURL, { clampToGround: true });
// Add it to the scene.
const dataSource = await viewer.dataSources.add(geoJSON);
// By default, polygons in CesiumJS will be draped over all 3D content in the scene.
// Modify the polygons so that this draping only applies to the terrain, not 3D buildings.
for (const entity of dataSource.entities.values) {
entity.polygon.classificationType = Cesium.ClassificationType.TERRAIN;
}
// Move the camera so that the polygon is in view.
viewer.flyTo(dataSource);
}
addBuildingGeoJSON();
您现在会在地面上看到建筑物的足迹。使用鼠标滚轮放大或右键单击并拖动以仔细查看。
5隐藏现场现有的3D建筑物
现在我们已经确定了新建筑的去向,我们可以看到当前有哪些建筑。我们将使用 3D Tiles 样式语言 来隐藏它们。
在上面的足迹中,我们可以看到我们新拟建建筑的场地上有六栋建筑——一栋大型建筑和五栋小得多的建筑。
1添加以下代码。它隐藏了所有较小的 3D 建筑物。
// STEP 4 CODE
// Hide individual buildings in this area using 3D Tiles Styling language.
buildingsTileset.style = new Cesium.Cesium3DTileStyle({
// Create a style rule to control each building's "show" property.
show: {
conditions : [
// Any building that has this elementId will have `show = false`.
['${elementId} === 332469316', false],
['${elementId} === 332469317', false],
['${elementId} === 235368665', false],
['${elementId} === 530288180', false],
['${elementId} === 530288179', false],
// If a building does not have one of these elementIds, set `show = true`.
[true, true]
]
},
// Set the default color style for this particular 3D Tileset.
// For any building that has a `cesium#color` property, use that color, otherwise make it white.
color: "Boolean(${feature['cesium#color']}) ? color(${feature['cesium#color']}) : color('#ffffff')"
});
2扩展此代码以隐藏剩余的 3D 建筑。
-
单击建筑物即可找到其
elementId
。 -
添加另一行,例如:
['${elementId} === large_building_elementId', false]
,.
6上传并定位新建筑
让我们上传建议的建筑模型。
1下载此 glTF 模型。
2将其拖放到 Cesium ion 仪表板中。
3选择 3D 模型(平铺为 3D 平铺) 并按 UPLOAD。
4平铺完成后,单击 资源预览窗口顶部的“调整平铺集位置”按钮。
5在搜索框中 输入建筑物的地址 1250 Cherokee Street ,然后单击NEXT。
6使用查看器上的控件,直观地定位并旋转建筑物,使其与下方的卫星图像对齐。您的最终设置应约为:
-
经度:-104.9909
-
纬度: 39.73579
-
身高:1577
-
标题:-8
7按SAVE。
7将新建筑添加到场景中
现在让我们将新建筑添加到场景中。
1在资源预览窗口下获取我们刚刚地理定位的建筑模型的3D对象 ID。
2在index.html 中添加以下代码。
-
替换
your_asset_id
为您的3D对象 ID。
// STEP 6 CODE
// Add the 3D Tileset you created from your Cesium ion account.
const newBuildingTileset = await Cesium.Cesium3DTileset.fromIonAssetId(your_asset_id);
viewer.scene.primitives.add(newBuildingTileset);
// Move the camera to the new building.
viewer.flyTo(newBuildingTileset);
8添加一个按钮来切换新建筑
1在 index.html中,将按钮添加到 <body>
标记内的上方 <script>
。
<button id="toggle-building">Toggle new building</button>
2style
在标签内 添加以下 CSS 标签head
。
<style type="text/css">
#toggle-building { z-index: 1; position: fixed; top: 5px; left: 5px; }
</style>
3在index.html中添加以下JavaScript
// STEP 7 CODE
// Toggle the tileset's show property when the button is clicked.
document.querySelector('#toggle-building').onclick = function() {
newBuildingTileset.show = !newBuildingTileset.show;
};
9考虑建筑物对周围环境的影响
现在您可以比较有和没有这座新建筑的场景!丹佛的全景山景非常珍贵。这座建筑如何影响其他地点(例如科罗拉多州议会大厦)的景观?
对科罗拉多州议会大厦景观的影响。
要重现此效果,请搜索State Capitol Building, Denver, CO, USA并调整相机。
我们甚至可以探索国会大厦入口处的景色将如何变化。
从更接近地面的角度对视野的影响。
完整教程源码
这是此应用程序的完整源代码,带有 your_token_here
和 your_asset_id
占位符
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.115/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.115/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<link href="style.css" rel="stylesheet">
<style type="text/css">
#toggle-building { z-index: 1; position: fixed; top: 5px; left: 5px; }
</style>
</head>
<body type="module">
<div id="cesiumContainer"></div>
<button id="toggle-building">Toggle new building</button>
<script>
// Your access token can be found at: https://ion.cesium.com/tokens.
// Replace `your_access_token` with your Cesium ion access token.
Cesium.Ion.defaultAccessToken = 'your_access_token';
// Keep your Cesium.Ion.defaultAccessToken = 'your_token_here' line from above.
// STEP 2 CODE
// Initialize the viewer with Cesium World Terrain.
const viewer = new Cesium.Viewer('cesiumContainer', {
terrain: Cesium.Terrain.fromWorldTerrain(),
});
// Fly the camera to Denver, Colorado at the given longitude, latitude, and height.
/* viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(-104.9965, 39.74248, 4000)
}); */
// Add Cesium OSM Buildings.
const buildingsTileset = await Cesium.createOsmBuildingsAsync();
viewer.scene.primitives.add(buildingsTileset);
// STEP 3 CODE
async function addBuildingGeoJSON() {
// Load the GeoJSON file from Cesium ion.
const geoJSONURL = await Cesium.IonResource.fromAssetId(your_asset_id);
// Create the geometry from the GeoJSON, and clamp it to the ground.
const geoJSON = await Cesium.GeoJsonDataSource.load(geoJSONURL, { clampToGround: true });
// Add it to the scene.
const dataSource = await viewer.dataSources.add(geoJSON);
// By default, polygons in CesiumJS will be draped over all 3D content in the scene.
// Modify the polygons so that this draping only applies to the terrain, not 3D buildings.
for (const entity of dataSource.entities.values) {
entity.polygon.classificationType = Cesium.ClassificationType.TERRAIN;
}
// Move the camera so that the polygon is in view.
// viewer.flyTo(dataSource);
}
addBuildingGeoJSON();
// STEP 4 CODE
// Hide individual buildings in this area using 3D Tiles Styling language.
buildingsTileset.style = new Cesium.Cesium3DTileStyle({
// Create a style rule to control each building's "show" property.
show: {
conditions : [
// Any building that has this elementId will have `show = false`.
['${elementId} === 532245203', false],
['${elementId} === 332469316', false],
['${elementId} === 332469317', false],
['${elementId} === 235368665', false],
['${elementId} === 530288180', false],
['${elementId} === 530288179', false],
// If a building does not have one of these elementIds, set `show = true`.
[true, true]
]
},
// Set the default color style for this particular 3D Tileset.
// For any building that has a `cesium#color` property, use that color, otherwise make it white.
color: "Boolean(${feature['cesium#color']}) ? color(${feature['cesium#color']}) : color('#ffffff')"
});
// STEP 6 CODE
// Add the 3D Tileset you created from your Cesium ion account.
const newBuildingTileset = await Cesium.Cesium3DTileset.fromIonAssetId(your_asset_id);
viewer.scene.primitives.add(newBuildingTileset);
// Move the camera to the new building.
viewer.flyTo(newBuildingTileset);
// STEP 7 CODE
// Toggle the tileset's show property when the button is clicked.
document.querySelector('#toggle-building').onclick = function() {
newBuildingTileset.show = !newBuildingTileset.show;
};
</script>
</body>
</html>