Cesium案例(八) Terrain
第一步正常建viewer,需要注意的是官网例子属性值比较老,最新版本的属性值有所差异,全copy官网会无法运行,提示函数未定义。
第一处差异
官网:
1 const viewer = new Cesium.Viewer("cesiumContainer", { 2 terrain: Cesium.Terrain.fromWorldTerrain({ 3 requestWaterMask: true, 4 requestVertexNormals: true, 5 }), 6 });
实际代码:
const viewer = new Cesium.Viewer("cesiumContainer", { terrainProvider: Cesium.createWorldTerrain({ requestWaterMask: true, requestVertexNormals: true, }), });
Cesium.createWorldTerrain //为 Cesium World Terrain 创建一个 CesiumTerrainProvider 实例
第二处差异
官方
{ text: "CesiumTerrainProvider - Cesium World Terrain", onselect: function () { viewer.scene.setTerrain( Cesium.Terrain.fromWorldTerrain({ requestWaterMask: true, requestVertexNormals: true, }) ); viewer.scene.globe.enableLighting = true; },
实际代码
{ text: "CesiumTerrainProvider - Cesium World Terrain", onselect: function () { viewer.terrainProvider = Cesium.createWorldTerrain({ requestWaterMask: true, requestVertexNormals: true, }); viewer.scene.globe.enableLighting = true; }, },
后续几个都是如此,直接替换
第三处代码差异
官网代码
{ text: "VRTheWorldTerrainProvider", onselect: function () { viewer.scene.setTerrain( new Cesium.Terrain( Cesium.VRTheWorldTerrainProvider.fromUrl( "http://www.vr-theworld.com/vr-theworld/tiles1.0.0/73/", { credit: "Terrain data courtesy VT MÄK", } ) ) ); },
实际代码
{ text: "VRTheWorldTerrainProvider", onselect: function () { viewer.terrainProvider = Cesium.createWorldTerrain( Cesium.VRTheWorldTerrainProvider({ url: "http://www.vr-theworld.com/vr-theworld/tiles1.0.0/73/", credit: "Terrain data courtesy VT MÄK", }) ); }, },
完整代码
Cesium.Ion.defaultAccessToken = token; const viewer = new Cesium.Viewer("cesiumContainer", { terrainProvider: Cesium.createWorldTerrain({ //为 Cesium World Terrain 创建一个 CesiumTerrainProvider 实例 requestWaterMask: true, //指示客户端是否应从服务器请求每个瓷砖水面具(如果可用)。 requestVertexNormals: true, //指示客户端是否应从服务器请求额外的照明信息(如果可用)。 }), }); viewer.scene.globe.enableLighting = true; //启用使用场景光源照亮地球。 const ellipsoidProvider = new Cesium.EllipsoidTerrainProvider(); //一个非常简单的 TerrainProvider ,它通过对椭球面进行镶嵌来生成几何图形。 const customHeightmapWidth = 32; const customheightmapHeight = 32; const customHeightmapProvider = new Cesium.CustomHeightmapTerrainProvider( //从回调函数获取高度值的简单 TerrainProvider 。 //它可以用于程序生成的地形或作为一种加载自定义高度图数据的方式, //而无需创建 TerrainProvider 的子类。 //有一些限制,例如没有水面具、没有顶点法线和没有可用性, //所以一个成熟的 TerrainProvider 子类更适合这些更复杂的用例。 { width: customHeightmapWidth, height: customheightmapHeight, callback: function (x, y, level) { const width = customHeightmapWidth; const height = customheightmapHeight; const buffer = new Float32Array(width * height); for (let yy = 0; yy < height; yy++) { for (let xx = 0; xx < width; xx++) { const u = (x + xx / (width - 1)) / Math.pow(2, level); const v = (y + yy / (height - 1)) / Math.pow(2, level); const heightValue = 4000 * (Math.sin(8000 * v) * 0.5 + 0.5); const index = yy * width + xx; buffer[index] = heightValue; } } return buffer; }, } ); Sandcastle.addToolbarMenu( [ { text: "CesiumTerrainProvider - Cesium World Terrain", onselect: function () { viewer.terrainProvider = Cesium.createWorldTerrain({ requestWaterMask: true, requestVertexNormals: true, }); viewer.scene.globe.enableLighting = true; }, }, { text: "CesiumTerrainProvider - Cesium World Terrain - no effects", onselect: function () { viewer.terrainProvider = new Cesium.createWorldTerrain(); }, }, { text: "CesiumTerrainProvider - Cesium World Terrain w/ Lighting", onselect: function () { viewer.terrainProvider = Cesium.createWorldTerrain({ requestVertexNormals: true, }); viewer.scene.globe.enableLighting = true; }, }, { text: "CesiumTerrainProvider - Cesium World Terrain w/ Water", onselect: function () { viewer.terrainProvider = Cesium.createWorldTerrain({ requestWaterMask: true, }); }, }, { text: "EllipsoidTerrainProvider", onselect: function () { viewer.terrainProvider = ellipsoidProvider; }, }, { text: "CustomHeightmapTerrainProvider", onselect: function () { viewer.terrainProvider = customHeightmapProvider; }, }, { text: "VRTheWorldTerrainProvider", onselect: function () { viewer.terrainProvider = Cesium.createWorldTerrain( Cesium.VRTheWorldTerrainProvider({ url: "http://www.vr-theworld.com/vr-theworld/tiles1.0.0/73/", credit: "Terrain data courtesy VT MÄK", // 数据源的功劳,显示在画布上。 }) ); }, }, { text: "ArcGISTerrainProvider", onselect: function () { viewer.terrainProvider = Cesium.createWorldTerrain( Cesium.ArcGISTiledElevationTerrainProvider({ url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer", }) ); }, }, ], "terrainMenu" ); Sandcastle.addDefaultToolbarMenu( [ { text: "Mount Everest", onselect: function () { const target = new Cesium.Cartesian3( 300770.50872389384, 5634912.131394585, 2978152.2865545116 ); const offset = new Cesium.Cartesian3( 6344.974098678562, -793.3419798081741, 2499.9508860763162 ); viewer.camera.lookAt(target, offset); viewer.camera.lookAtTransform( Cesium.Matrix4.IDENTITY //transform Matrix4 定义参考系的变换矩阵。 ); //使用目标和变换矩阵设置相机位置和方向。 //偏移量可以是笛卡尔坐标或航向/间距/范围。 //如果偏移量是笛卡尔坐标,则它是从由变换矩阵定义的参考系中心的偏移量。 //如果偏移量是航向/俯仰/范围,则航向和俯仰角在由变换矩阵定义的参考系中定义。 //航向是从 y 轴到 x 轴增加的角度。 //俯仰是从 xy 平面的旋转。正俯仰角在平面下方。 //负俯仰角在平面上方。范围是到中心的距离。 //在 2D 中,必须有自上而下的视图。相机将放置在参考框架的中心上方。 //目标上方的高度将是偏移量的大小。航向将根据偏移量确定。 //如果无法根据偏移量确定航向,则航向将为北。 }, }, { text: "Half Dome", onselect: function () { const target = new Cesium.Cartesian3( -2489625.0836225147, -4393941.44443024, 3882535.9454173897 ); const offset = new Cesium.Cartesian3( -6857.40902037546, 412.3284835694358, 2147.5545426812023 ); viewer.camera.lookAt(target, offset); viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY); }, }, { text: "San Francisco Bay", onselect: function () { const target = new Cesium.Cartesian3( -2708814.85583248, -4254159.450845907, 3891403.9457429945 ); const offset = new Cesium.Cartesian3( 70642.66030209465, -31661.517948317807, 35505.179997143336 ); viewer.camera.lookAt(target, offset); viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY); }, }, ], "zoomButtons" ); let terrainSamplePositions; function sampleTerrainSuccess(terrainSamplePositions) { const ellipsoid = Cesium.Ellipsoid.WGS84; //初始化为 WGS84 标准的 Ellipsoid 实例。 viewer.scene.globe.depthTestAgainstTerrain = true; //如果广告牌、折线、标签等图元应针对地形表面进行深度测试,则为 true; //如果此类图元应始终绘制在地形顶部,除非它们位于地球的另一侧,则为 false。 //针对地形进行深度测试图元的缺点是, //轻微的数值噪声或地形细节级别的切换有时会使应该在表面上的图元在其下方消失。 viewer.entities.suspendEvents(); //防止引发 EntityCollection#collectionChanged 事件, //直到对 EntityCollection#resumeEvents 进行相应的调用, //此时将引发涵盖所有暂停操作的单个事件。 //这允许有效地添加和删除许多项目。 //只要有对 EntityCollection#resumeEvents 的相应调用,就可以安全地多次调用此函数。 viewer.entities.removeAll(); // 从集合中移除所有实体。 for (let i = 0; i < terrainSamplePositions.length; ++i) { const position = terrainSamplePositions[i]; viewer.entities.add({ name: position.height.toFixed(1), //toFixed 固定小数位 position: ellipsoid.cartographicToCartesian(position), //将提供的制图转换为笛卡尔表示。 billboard: { //描述位于包含 Entity 位置的二维图标。 verticalOrigin: Cesium.VerticalOrigin.BOTTOM, //原点相对于对象的垂直位置,例如 Billboard 或 Label 。例如, //将垂直原点设置为 TOP 或 BOTTOM 将在锚点位置上方或下方(在屏幕空间中)显示广告牌 scale: 0.7, // 一个数字属性,指定应用于图像大小的比例。 image: "../../images/facility.gif", //一个属性,指定用于广告牌的图像、URI 或画布。 }, label: { //描述位于包含 Entity 位置的二维标签 text: position.height.toFixed(1), // 指定文本的属性。支持显式换行符 ''。 font: "10pt monospace", horizontalOrigin: Cesium.HorizontalOrigin.CENTER, //原点相对于对象的水平位置,例如 Billboard 或 Label 。 //例如,将水平原点设置为 LEFT 或 RIGHT 将在锚点位置的左侧或右侧(在屏幕空间中)显示广告牌。 pixelOffset: new Cesium.Cartesian2(0, -14), // 指定像素偏移的 Cartesian2 属性 fillColor: Cesium.Color.BLACK, //填充 outlineColor: Cesium.Color.BLACK, //轮廓 showBackground: true, //一个布尔属性,指定标签背后背景的可见性。 backgroundColor: new Cesium.Color(0.9, 0.9, 0.9, 0.7), backgroundPadding: new Cesium.Cartesian2(4, 3), // 一个 Cartesian2 属性,以像素为单位指定水平和垂直背景填充。 }, }); } viewer.entities.resumeEvents(); //添加或删除项目时立即恢复引发 EntityCollection#collectionChanged 事件。 //在事件挂起期间所做的任何修改都将在调用此函数时作为单个事件触发。 //此函数是引用计数的,只要有对 EntityCollection#resumeEvents 的相应调用,就可以安全地多次调用该函数。 } function createGrid(rectangleHalfSize) { //矩形一半大小 const gridWidth = 41; const gridHeight = 41; const everestLatitude = Cesium.Math.toRadians(27.988257); //将度数转换为弧度 const everestLongitude = Cesium.Math.toRadians(86.925145); //everest 最高 const e = new Cesium.Rectangle( everestLongitude - rectangleHalfSize, everestLatitude - rectangleHalfSize, everestLongitude + rectangleHalfSize, everestLatitude + rectangleHalfSize ); const terrainSamplePositions = []; for (let y = 0; y < gridHeight; ++y) { for (let x = 0; x < gridWidth; ++x) { const longitude = Cesium.Math.lerp( e.west, e.east, x / (gridWidth - 1) ); //计算两个值的线性插值。 const latitude = Cesium.Math.lerp( e.south, e.north, y / (gridHeight - 1) ); const position = new Cesium.Cartographic(longitude, latitude); //由经度、纬度和高度定义的位置。 terrainSamplePositions.push(position); } } return terrainSamplePositions; } Sandcastle.addToggleButton( "Enable Lighting", viewer.scene.globe.enableLighting, function (checked) { viewer.scene.globe.enableLighting = checked; } ); Sandcastle.addToggleButton( "Enable fog", viewer.scene.fog.enabled, function (checked) { viewer.scene.fog.enabled = checked; } ); Sandcastle.addToolbarButton( "Sample Everest Terrain at Level 9", function () { const terrainSamplePositions = createGrid(0.005); Promise.resolve( Cesium.sampleTerrain( //通过向地形提供者请求切片、采样和插值,启动对 Cartographic 位置数组的地形高度查询。 //插值匹配用于在指定级别渲染地形的三角形。查询是异步发生的,所以这个函数返回一个在查询完成时解决的 Promise。 //每个点的高度都在原地修改。如果由于该位置的指定级别没有可用的地形数据而无法确定高度,或者发生其他错误,则将高度设置为未定义。 //作为典型的 Cartographic 类型,提供的高度是参考椭球体上方的高度(例如 Ellipsoid.WGS84 ),而不是高于平均海平面的高度。 //换句话说,如果在海洋中采样,它不一定是 0.0。此函数需要地形细节级别作为输入, //如果您需要尽可能精确地获取地形高度(即最大细节级别),请使用 sampleTerrainMostDetailed 。 viewer.terrainProvider, 9, terrainSamplePositions //terrainProvider TerrainProvider 从中查询高度的地形提供者。 //level Number 用于查询地形高度的地形细节层次。 //positions Array.< Cartographic > 使用地形高度更新的位置。 ) ).then(sampleTerrainSuccess); const target = new Cesium.Cartesian3( 300770.50872389384, 5634912.131394585, 2978152.2865545116 ); const offset = new Cesium.Cartesian3( 6344.974098678562, -793.3419798081741, 2499.9508860763162 ); viewer.camera.lookAt(target, offset); viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY); }, "sampleButtons" ); Sandcastle.addToolbarButton( "Sample Most Detailed Everest Terrain", function () { if (!Cesium.defined(viewer.terrainProvider.availability)) { window.alert( "sampleTerrainMostDetailed is not supported for the selected terrain provider" ); return; } const terrainSamplePositions = createGrid(0.0005); Promise.resolve( Cesium.sampleTerrainMostDetailed( viewer.terrainProvider, terrainSamplePositions ) //在地形数据集的最大可用切片级别启动 sampleTerrain() 请求。 ).then(sampleTerrainSuccess); const target = new Cesium.Cartesian3( 300770.50872389384, 5634912.131394585, 2978152.2865545116 ); const offset = new Cesium.Cartesian3( 6344.974098678562, -793.3419798081741, 2499.9508860763162 ); viewer.camera.lookAt(target, offset); viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY); }, "sampleButtons" );