Cesium加载三维路线

1. 概述

将路线加载到三维地图中,能直观显示道路的坡度变化,协同DEM和遥感影像,能极大丰富道路的可视化效果

本文此处基于Cesium,加载地形数据,叠加遥感影像,再叠加路网数据,形成三维地图,效果如下:

image-20220506023737750

2. 代码实现

2.1 CDN引入

笔者这里使用 CDN 引入 Cesium,另外后续加载路网数据需要使用Ajax,这里引入jQuery

<!-- Include the CesiumJS JavaScript and CSS files --> <script src=https://cesium.com/downloads/cesiumjs/releases/1.92/Build/Cesium/Cesium.js></script> <link href=https://cesium.com/downloads/cesiumjs/releases/1.92/Build/Cesium/Widgets/widgets.css rel=stylesheet> <script src=http://libs.baidu.com/jquery/2.0.0/jquery.min.js></script> 

2.2 加载地形

笔者这里使用Cesium的地形数据

// Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID. var viewer = new Cesium.Viewer('cesiumContainer', {     terrainProvider: Cesium.createWorldTerrain() }); 

2.3 加载遥感影像

笔者这里使用Bing的遥感影像

var bingStyle = [     Cesium.BingMapsStyle.AERIAL_WITH_LABELS,     Cesium.BingMapsStyle.COLLINS_BART,     Cesium.BingMapsStyle.CANVAS_GRAY,     Cesium.BingMapsStyle.CANVAS_LIGHT,     Cesium.BingMapsStyle.CANVAS_DARK,     Cesium.BingMapsStyle.ORDNANCE_SURVEY,     Cesium.BingMapsStyle.ROAD,     Cesium.BingMapsStyle.AERIAL, ]; var bingMapProvider = new Cesium.BingMapsImageryProvider({     url: https://dev.virtualearth.net,     key: AmXdbd8UeUJtaRSn7yVwyXgQlBBUqliLbHpgn2c76DfuHwAXfRrgS5qwfHU6Rhm8,     mapStyle: bingStyle[7], }); viewer.imageryLayers.addImageryProvider(bingMapProvider); 

2.4 设置视点

笔者这里将视点即观察点设置为长沙岳麓山附近

// Fly the camera to Changsha at the given longitude, latitude, and height. viewer.camera.flyTo({     destination: Cesium.Cartesian3.fromDegrees(112.9448, 28.1708, 1200),     orientation: {         heading: Cesium.Math.toRadians(0.0),         pitch: Cesium.Math.toRadians(-15.0),     } }); 

2.5 小结测试

到目前为止,整体代码如下:

<!DOCTYPE html> <html lang=zh-cn>  <head>     <meta charset=utf-8>     <!-- Include the CesiumJS JavaScript and CSS files -->     <script src=https://cesium.com/downloads/cesiumjs/releases/1.92/Build/Cesium/Cesium.js></script>     <link href=https://cesium.com/downloads/cesiumjs/releases/1.92/Build/Cesium/Widgets/widgets.css rel=stylesheet>     <script src=http://libs.baidu.com/jquery/2.0.0/jquery.min.js></script> </head>  <body>     <div id=cesiumContainer></div>     <script>         // Your access token can be found at: https://cesium.com/ion/tokens.         // Replace `your_access_token` with your Cesium ion access token.          // Cesium.Ion.defaultAccessToken = 'your_access_token';          // Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID.         var viewer = new Cesium.Viewer('cesiumContainer', {             terrainProvider: Cesium.createWorldTerrain()         });          var bingStyle = [             Cesium.BingMapsStyle.AERIAL_WITH_LABELS,             Cesium.BingMapsStyle.COLLINS_BART,             Cesium.BingMapsStyle.CANVAS_GRAY,             Cesium.BingMapsStyle.CANVAS_LIGHT,             Cesium.BingMapsStyle.CANVAS_DARK,             Cesium.BingMapsStyle.ORDNANCE_SURVEY,             Cesium.BingMapsStyle.ROAD,             Cesium.BingMapsStyle.AERIAL,         ];         var bingMapProvider = new Cesium.BingMapsImageryProvider({             url: https://dev.virtualearth.net,             key: AmXdbd8UeUJtaRSn7yVwyXgQlBBUqliLbHpgn2c76DfuHwAXfRrgS5qwfHU6Rhm8,             mapStyle: bingStyle[7],         });         viewer.imageryLayers.addImageryProvider(bingMapProvider);          // Fly the camera to Changsha at the given longitude, latitude, and height.         viewer.camera.flyTo({             destination: Cesium.Cartesian3.fromDegrees(112.9448, 28.1708, 1200),             orientation: {                 heading: Cesium.Math.toRadians(0.0),                 pitch: Cesium.Math.toRadians(-15.0),             }         });      </script>     </div> </body>  </html> 

我们测试其效果,笔者这里使用的是VS Code的Live Server插件打开:

image-20220506024724036

2.6 路网加载

路网(Ployline)的加载方式主要有两种:

  • 使用Viewer.entities
  • 使用Cesium.GroundPolylinePrimitive

此处笔者的示例数据如下:

112.91725386767241,28.18088394284931,47.28909519744873 112.91765398304703,28.18080062181755,52.27461015478515 112.94271541045252,28.18907071666184,34.729803589843755 112.94263996370492,28.18929672498712,44.796474171875005 112.94263996370492,28.18929672498712,44.796474171875005 112.94271541045252,28.18907071666184,34.729803589843755 112.94264628370074,28.190083096717974,36.39323925 112.94259227750655,28.18977721403619,24.70917075 112.94259227750655,28.18977721403619,24.70917075 112.94264628370074,28.190083096717974,36.39323925 112.94264628370074,28.190083096717974,36.39323925 112.94255004568583,28.19039785102342,46.969459281249996 112.94255004568583,28.19039785102342,46.969459281249996 112.94264628370074,28.190083096717974,36.39323925 112.94263996370492,28.18929672498712,44.796474171875005 112.942639007202,28.18939392688125,41.37971635546875 112.942639007202,28.18939392688125,41.37971635546875 112.94263996370492,28.18929672498712,44.796474171875005 112.942639007202,28.18939392688125,41.37971635546875 112.94259227750655,28.18977721403619,24.70917075 112.94259227750655,28.18977721403619,24.70917075 112.942639007202,28.18939392688125,41.37971635546875 112.94255707716033,28.19747999678687,78.00138119911344 112.94251096347146,28.197641540468794,62.308529711560176 112.94251096347146,28.197641540468794,62.308529711560176 112.94251086130572,28.19765191789022,62.308529711560176 112.94251086130572,28.19765191789022,62.308529711560176 112.94216077246415,28.19774676732831,88.14892901957208 112.94251096347146,28.197641540468794,62.308529711560176 112.94255707716033,28.19747999678687,78.00138119911344 112.94251086130572,28.19765191789022,62.308529711560176 112.94251096347146,28.197641540468794,62.308529711560176  

数据文件的名字为trans_final_map_with_dem.csv,笔者使用Ajax加载并解析

前者Viewer.entities的代码如下:

$.ajax({     url: 'trans_final_map_with_dem.csv',     dataType: 'text', }).done(successFunction);  var groundPolylineGeometryInstances = []; function successFunction(data) {     var allRows = data.split(/\r?\n|\r/);     for (let i = 0; i < allRows.length - 1; i = i + 2) {         var rowCell1 = allRows[i].split(',');         var rowCell2 = allRows[i + 1].split(',');          viewer.entities.add({             polyline: {                 positions: Cesium.Cartesian3.fromDegreesArrayHeights([                     rowCell1[0], rowCell1[1],rowCell1[2],                     rowCell2[0], rowCell2[1],rowCell2[2]                 ]),                 width: 4.0,                 material: Cesium.Color.ORANGE,                 clampToGround: true             }         })     } } 

后者Cesium.GroundPolylinePrimitive的代码如下:

$.ajax({     url: 'trans_final_map_with_dem.csv',     dataType: 'text', }).done(successFunction);  var groundPolylineGeometryInstances = []; function successFunction(data) {     var allRows = data.split(/\r?\n|\r/);     for (let i = 0; i < allRows.length - 1; i = i + 2) {         var rowCell1 = allRows[i].split(',');         var rowCell2 = allRows[i + 1].split(',');          groundPolylineGeometryInstances.push(new Cesium.GeometryInstance({             geometry: new Cesium.GroundPolylineGeometry({                 positions: Cesium.Cartesian3.fromDegreesArrayHeights([                     rowCell1[0], rowCell1[1], rowCell1[2],                     rowCell2[0], rowCell2[1], rowCell2[2]                 ]),                 width: 4.0,             }),             attributes: {                 color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.ORANGE.withAlpha(1.0))             }         }));     }     var groundPolylinePrimitive = new Cesium.GroundPolylinePrimitive({         geometryInstances: groundPolylineGeometryInstances,         show: true,         appearance: new Cesium.PolylineColorAppearance()     });     viewer.scene.groundPrimitives.add(groundPolylinePrimitive) } 

注意:

  • 笔者这里的数据是每两个点构成一个Polyline,每一行最后是换行符'\n'
  • 数据量大时尽可能使用Cesium.GroundPolylinePrimitive

2.7 最终测试

整体代码如下:

<!DOCTYPE html> <html lang=zh-cn>  <head>     <meta charset=utf-8>     <!-- Include the CesiumJS JavaScript and CSS files -->     <script src=https://cesium.com/downloads/cesiumjs/releases/1.92/Build/Cesium/Cesium.js></script>     <link href=https://cesium.com/downloads/cesiumjs/releases/1.92/Build/Cesium/Widgets/widgets.css rel=stylesheet>     <script src=http://libs.baidu.com/jquery/2.0.0/jquery.min.js></script> </head>  <body>     <div id=cesiumContainer></div>     <script>         // Your access token can be found at: https://cesium.com/ion/tokens.         // Replace `your_access_token` with your Cesium ion access token.          // Cesium.Ion.defaultAccessToken = 'your_access_token';          // Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID.         var viewer = new Cesium.Viewer('cesiumContainer', {             terrainProvider: Cesium.createWorldTerrain()         });          var bingStyle = [             Cesium.BingMapsStyle.AERIAL_WITH_LABELS,             Cesium.BingMapsStyle.COLLINS_BART,             Cesium.BingMapsStyle.CANVAS_GRAY,             Cesium.BingMapsStyle.CANVAS_LIGHT,             Cesium.BingMapsStyle.CANVAS_DARK,             Cesium.BingMapsStyle.ORDNANCE_SURVEY,             Cesium.BingMapsStyle.ROAD,             Cesium.BingMapsStyle.AERIAL,         ];         var bingMapProvider = new Cesium.BingMapsImageryProvider({             url: https://dev.virtualearth.net,             key: AmXdbd8UeUJtaRSn7yVwyXgQlBBUqliLbHpgn2c76DfuHwAXfRrgS5qwfHU6Rhm8,             mapStyle: bingStyle[7],         });         viewer.imageryLayers.addImageryProvider(bingMapProvider);          $.ajax({             url: 'trans_final_map_with_dem.csv',             dataType: 'text',         }).done(successFunction);          // const polylines = new Cesium.PolylineCollection();         var groundPolylineGeometryInstances = [];         function successFunction(data) {             var allRows = data.split(/\r?\n|\r/);             for (let i = 0; i < allRows.length - 1; i = i + 2) {                 var rowCell1 = allRows[i].split(',');                 var rowCell2 = allRows[i + 1].split(',');                  //     viewer.entities.add({                 //         polyline: {                 //             positions: Cesium.Cartesian3.fromDegreesArrayHeights([                 //                 rowCell1[0], rowCell1[1],rowCell1[2],                 //                 rowCell2[0], rowCell2[1],rowCell2[2]                 //             ]),                 //             width: 4.0,                 //             material: Cesium.Color.ORANGE,                 //             clampToGround: true                 //         }                 //     })                 // }                 groundPolylineGeometryInstances.push(new Cesium.GeometryInstance({                     geometry: new Cesium.GroundPolylineGeometry({                         positions: Cesium.Cartesian3.fromDegreesArrayHeights([                             rowCell1[0], rowCell1[1], rowCell1[2],                             rowCell2[0], rowCell2[1], rowCell2[2]                         ]),                         width: 4.0,                     }),                     attributes: {                         color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.ORANGE.withAlpha(1.0))                     }                 }));                 // }             }             var groundPolylinePrimitive = new Cesium.GroundPolylinePrimitive({                 geometryInstances: groundPolylineGeometryInstances,                 show: true,                 appearance: new Cesium.PolylineColorAppearance()             });             viewer.scene.groundPrimitives.add(groundPolylinePrimitive)         }          // Fly the camera to Changsha at the given longitude, latitude, and height.         viewer.camera.flyTo({             destination: Cesium.Cartesian3.fromDegrees(112.9448, 28.1708, 1200),             orientation: {                 heading: Cesium.Math.toRadians(0.0),                 pitch: Cesium.Math.toRadians(-15.0),             }         });      </script>     </div> </body>  </html> 

最后效果如下:

image-20220506023737750

参考资料

[1]Sandcastle

[2]Polylines on Terrain

[3]cesiumjs/ref-doc/Viewer

[4]遥感影像和DEM数据获取处理、GeoServer切片发布并使用Cesium加载