Cesium是一个非常优秀的三维地球GIS引擎(开源且免费)。能够加载各种符合标准的地图图层,瓦片图、矢量图等都支持。支持3DMax等建模软件生成的obj文件,支持通用的GIS计算;支持DEM高程图。测试中的3D-Tiles分支还支持倾斜摄影生成的城市三维建筑群。国内许多三维GIS产品都基于Cesium进行封装(包括一些大厂)。因为工作关系,我对Cesium的一些基本GIS功能进行了研究,特此记录下来。
如上图,这是一个给市政GIS\BIM管理平台做的原型,GIS部分使用Cesium,BIM部分使用第三方商业引擎。GIS控制宏观、BIM支持微观(现在还没有什么好的引擎能做到GIS\BIM的无缝切换)。
常用功能介绍:
- 卫星\矢量地图切换
我这里使用的是天地图提供的服务,卫星地图和矢量地图分别调用不同的接口,卫星地图显示效果如上图,矢量地图显示如下图:
- 道路及基本标注
点“道路及基本标注”后,将路名等显示并加载在原先的图层上
- 加标记点
首先在地图上点击需要加点的位置,然后在弹出框内选取颜色,设置提示文字和显示内容,点击保存;可以添加多个标记。
- 绘制线段
连续点击地图两次就可以绘制线段(可绘制折线)
- 绘制圆形
支持绘制多个圆形,每个圆形随机颜色,能够显示园的半径、面积等
- 绘制多边形
连续点击地图上的点,再右键闭合,就可以绘制多边形,能够计算多边形每一边的边长、总面积等
- 保存视角、跳转视角
保存当前的视角;输入经纬度,跳转到指定位置
- 隐藏、加载模型
可以动态加载、隐藏三维模型(为了便于演示,所有的模型均放大了几百倍);地图上的绘制功能对所有模型都有效,包含在范围内的模型会自动高亮并显示;点选模型能自动居中并提示是否跳转到BIM模型显示(BIM模型也是基于三维WebGL的)
- 搜索
可以根据输入的关键词进行搜索,使用百度或者高德的API,或者使用天地图的API,搜索后进行定位,只是百度、高德、天地图等用的是不同的坐标系,转换非常麻烦。
- 清除绘制
清除所有绘制的部分
以上功能只要再完善下,封装下就可以成为一个很不错的三维GIS项目基础平台了,附我们公司做的GIS+BIM的产品截图,使用了3D-Tiles:)
附:示例程序的js部分代码
1 var bimEngine; var msgControl; var toolbar; var fileControl; var spaceControl; var domainControl; var propertyControl; var searchControl; var markControl; 2 var storeyControl; var roamingControl; var bimevent; 3 4 var viewer = new Cesium.Viewer("cesiumContainer", { 5 animation: false, //是否显示动画控件 6 baseLayerPicker: false, //是否显示图层选择控件 7 geocoder: true, //是否显示地名查找控件 8 timeline: false, //是否显示时间线控件 9 sceneModePicker: true, //是否显示投影方式控件 10 navigationHelpButton: false, //是否显示帮助信息控件 11 infoBox: true, //是否显示点击要素之后显示的信息 12 imageryProvider: new Cesium.WebMapTileServiceImageryProvider({ 13 url: "http://t0.tianditu.com/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles", 14 layer: "tdtBasicLayer", 15 style: "default", 16 format: "image/jpeg", 17 tileMatrixSetID: "GoogleMapsCompatible", 18 show: false 19 }) 20 }); 21 var scene = viewer.scene; 22 var pinBuilder = new Cesium.PinBuilder(); 23 24 var vecLayer = null, roadLayer = null, electricLayers = null; 25 26 var getEnumPropertyNames = function(obj) { 27 var props = []; 28 for (prop in obj) { 29 props.push(prop + ': ' + obj[prop]); 30 } 31 return props; 32 } 33 34 var models = new Array(); 35 models[0] = { id: 'house1', name: 'house1', url: '../SampleData/house/house1.gltf', lon: 121.41, lat: 31.22, height: 0, pid: '4e027d42-f033-4bab-87f1-e34c8860b90e' }; 36 models[1] = { id: 'house2', name: 'house2', url: '../SampleData/house/house2.gltf', lon: 121.42, lat: 31.21, height: 0, pid: '918dcfaa-4568-4468-ba03-e379deaa99b7' }; 37 models[2] = { id: 'house3', name: 'house3', url: '../SampleData/house/house3.gltf', lon: 121.43, lat: 31.20, height: 0, pid: '2071736b-0054-4041-ad34-34f2e7a975e5' }; 38 models[3] = { id: 'house4', name: 'house4', url: '../SampleData/house/house4.gltf', lon: 121.44, lat: 31.22, height: 0, pid: '4e027d42-f033-4bab-87f1-e34c8860b90e' }; 39 models[4] = { id: 'house5', name: 'house5', url: '../SampleData/house/house5.gltf', lon: 121.41, lat: 31.21, height: 0, pid: '918dcfaa-4568-4468-ba03-e379deaa99b7' }; 40 models[5] = { id: 'house6', name: 'house6', url: '../SampleData/house/house6.gltf', lon: 121.42, lat: 31.20, height: 0, pid: '2071736b-0054-4041-ad34-34f2e7a975e5' }; 41 models[6] = { id: 'house7', name: 'house7', url: '../SampleData/house/house7.gltf', lon: 121.43, lat: 31.22, height: 0, pid: '4e027d42-f033-4bab-87f1-e34c8860b90e' }; 42 models[7] = { id: 'house8', name: 'house8', url: '../SampleData/house/house8.gltf', lon: 121.44, lat: 31.21, height: 0, pid: '918dcfaa-4568-4468-ba03-e379deaa99b7' }; 43 models[8] = { id: 'house9', name: 'house9', url: '../SampleData/house/house9.gltf', lon: 121.45, lat: 31.20, height: 0, pid: '2071736b-0054-4041-ad34-34f2e7a975e5' }; 44 models[9] = { id: 'house10', name: 'house10', url: '../SampleData/house/house10.gltf', lon: 121.46, lat: 31.21, height: 0, pid: '918dcfaa-4568-4468-ba03-e379deaa99b7' }; 45 models[10] = { id: 'house11', name: 'house11', url: '../SampleData/house/house11.gltf', lon: 121.40, lat: 31.20, height: 0, pid: '4e027d42-f033-4bab-87f1-e34c8860b90e' }; 46 models[11] = { id: 'villa', name: 'villa', url: '../SampleData/house3/house3.gltf', lon: 121.45, lat: 31.22, height: 0, pid: '2071736b-0054-4041-ad34-34f2e7a975e5' }; 47 48 var loadedModels = []; 49 50 var shapes = new Array(); 51 shapes[0] = { layer: '测试层', author: 'liu', date: '2017-06-18', ploy: [ 52 { name: 'A区', type: 'ploy', points: [] } 53 ]}; 54 55 var tempPoints = []; 56 var tempEntities = []; 57 var tempPinEntities = []; 58 var tempPinLon, tempPinLat; 59 60 var handler = null; 61 62 $(function() { 63 64 /**初始化**/ 65 $("input[name='optionsRadios']").click(function() { 66 if ($("input[name='optionsRadios']:eq(1)").prop("checked")) { 67 //viewer.imageryLayers.addImageryProvider(vecLayer); 68 vecLayer = viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({ 69 url: "http://t0.tianditu.com/vec_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=vec&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles", 70 layer: "tdtVecBasicLayer", 71 style: "default", 72 format: "image/jpeg", 73 tileMatrixSetID: "GoogleMapsCompatible", 74 show: false 75 })); 76 } else if ($("input[name='optionsRadios']:eq(0)").prop("checked")) { 77 if (viewer.imageryLayers.contains(vecLayer)) { 78 viewer.imageryLayers.remove(vecLayer); 79 } 80 } 81 }); 82 //标记层 83 $("#cbxPinLayer").change(function() { 84 if ($("#cbxPinLayer").prop("checked")) { 85 for (var i = 0; i < tempPinEntities.length; i++) { 86 viewer.entities.add(tempPinEntities[i]); 87 } 88 89 } else { 90 for (var i = 0; i < tempPinEntities.length; i ++) { 91 viewer.entities.remove(tempPinEntities[i]); 92 } 93 } 94 }); 95 $("#pinColor").change(function() {