在本教程中,您将学习如何创建 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 CODEasync 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>