开源三维地球GIS引擎Cesium常用功能的开发
Cesium是一个非常优秀的三维地球GIS引擎(开源且免费)。能够加载各种符合标准的地图图层,瓦片图、矢量图等都支持。支持3DMax等建模软件生成的obj文件,支持通用的GIS计算;支持DEM高程图。测试中的3D-Tiles分支还支持倾斜摄影生成的城市三维建筑群。国内许多三维GIS产品都基于Cesium进行封装(包括一些大厂)。因为工作关系,我对Cesium的一些基本GIS功能进行了研究,特此记录下来。
(源码: https://github.com/thanksqq/CesiumExample 请注意现在最新的cesium版本API可能有些不同)
如上图,这是一个给市政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() { 96 $(this).css("background-color", $(this).val()); 97 }); 98 99 $("#cbxRoad").click(function() { 100 if ($(this).prop("checked")) { 101 roadLayer = viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({ 102 url: "http://t0.tianditu.com/cia_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles", 103 layer: "tdtImgAnnoLayer", 104 style: "default", 105 format: "image/jpeg", 106 tileMatrixSetID: "GoogleMapsCompatible", 107 show: false 108 })); 109 } else { 110 viewer.imageryLayers.remove(roadLayer); 111 } 112 }); 113 $("#cbxTestArc").click(function() { 114 if ($(this).prop("checked")) { 115 electricLayers = viewer.imageryLayers.addImageryProvider(new Cesium.ArcGisMapServerImageryProvider({ 116 url: 'https://nationalmap.gov.au/proxy/http://services.ga.gov.au/site_3/rest/services/Electricity_Infrastructure/MapServer' 117 })); 118 viewer.camera.flyTo({ 119 destination: Cesium.Rectangle.fromDegrees(114.591, -45.837, 148.970, -5.730) 120 }); 121 } else { 122 viewer.imageryLayers.remove(electricLayers); 123 } 124 }); 125 $("#opts .btn").click(function () { 126 window.setTimeout(function() { 127 if ($("input[name='opt']:eq(0)").prop("checked")) { 128 clearEffects(); 129 setTips(""); 130 } 131 else if ($("input[name='opt']:eq(1)").prop("checked")) { 132 clearEffects(); 133 SetMode("addPin"); 134 setTips("首先在地图上点击需要加点的位置,然后在弹出框内选取颜色,设置提示文字和显示内容,点击保存"); 135 } 136 else if ($("input[name='opt']:eq(2)").prop("checked")) { 137 tempPoints = []; 138 for (var i = 0; i < tempEntities.length; i++) { 139 viewer.entities.remove(tempEntities[i]); 140 } 141 for (var i = 0; i < loadedModels.length; i++) { 142 if (loadedModels[i].color == Cesium.Color.SPRINGGREEN) { 143 loadedModels[i].color = {red:1,green:1, blue:1, alpha:1}; 144 } 145 } 146 clearEffects(); 147 setTips("绘制的图形被清除,点选页面标记可以删除标记"); 148 SetMode("erase"); 149 } 150 else if ($("input[name='opt']:eq(3)").prop("checked")) { 151 clearEffects(); 152 SetMode("drawLine"); 153 setTips("在地图上分别点击,即可绘制多个线段,点右键结束绘制"); 154 } 155 else if ($("input[name='opt']:eq(4)").prop("checked")) { 156 clearEffects(); 157 SetMode("drawCircle"); 158 setTips("第一次点击绘制圆心,第二次点击根据和圆心的位置绘制半径"); 159 } 160 else if ($("input[name='opt']:eq(5)").prop("checked")) { 161 clearEffects(); 162 SetMode("drawSquare"); 163 setTips("第一、二次点击绘制长方形的一个边,再次点击根据点和边的距离绘制方形"); 164 } 165 else if ($("input[name='opt']:eq(6)").prop("checked")) { 166 clearEffects(); 167 SetMode("drawPloy"); 168 setTips("如果需要绘制多边形,在地图上使用左键逐个点选地点,右击闭合多边形"); 169 } 170 else if ($("input[name='opt']:eq(7)").prop("checked")) { 171 clearEffects(); 172 SetMode("pickBuilding"); 173 setTips("点选建筑查看详细的三维模型"); 174 } 175 },100); 176 }); 177 178 var homeView = { 179 destination: new Cesium.Cartesian3(-2852877.756667368, 4655857.919027944, 3288673.682311567), 180 orientation: { 181 direction: new Cesium.Cartesian3(0.5437275903005284, -0.8386290220423197, -0.03258329225728158), 182 up: new Cesium.Cartesian3(0.05520718287689969, -0.00299987805272847, 0.9984704140286108) 183 }, 184 complete: function() { LoadModel(); }, 185 }; 186 187 setTimeout( 188 function() { 189 // scene.primitives.removeAll(); 190 //reset(); 191 viewer.camera.flyTo(homeView); 192 193 //viewer.zoomTo(wyoming); 194 }, 3000); 195 196 //var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( 197 //Cesium.Cartesian3.fromDegrees(121.49, 31.22, 0.0)); 198 //var model = scene.primitives.add(Cesium.Model.fromGltf({ 199 // url: '../SampleData/house/house1.gltf', 200 // modelMatrix: modelMatrix, 201 // scale: 20.0, 202 // name: 'SampleHouse', 203 // color: getColor('Red', 1) 204 //})); 205 206 $("#poly-show").click(function () { 207 LoadModel(); 208 }); 209 210 $("#poly-hide").click(function () { 211 HideModel(); 212 }); 213 214 //alert(getEnumPropertyNames(model).join('\r')); 215 216 }); 217 218 function LoadModel() { 219 for (var i = 0; i < models.length; i++) { 220 var hasLoaded = false; 221 for (var j = 0; j < loadedModels.length; j ++) { 222 if (models[i].id == loadedModels[j].id) { 223 hasLoaded = true; 224 } 225 } 226 if (!hasLoaded) { 227 var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame( 228 Cesium.Cartesian3.fromDegrees(models[i].lon, models[i].lat, models[i].height)); 229 230 var model = scene.primitives.add( 231 Cesium.Model.fromGltf({ 232 url: models[i].url, 233 modelMatrix: modelMatrix, 234 scale: 20.0, 235 name: models[i].name, 236 id: models[i].id 237 })); 238 loadedModels.push(model); 239 } 240 } 241 //var cartesian = viewer.camera.pickEllipsoid(loadedModels[0].modelMatrix, scene.globe.ellipsoid); 242 //alert(getEnumPropertyNames(cartesian).join('\r')); 243 244 } 245 246 function HideModel() { 247 for (var i = 0; i < loadedModels.length; i++) { 248 scene.primitives.remove(loadedModels[i]); 249 } 250 loadedModels = []; 251 } 252 253 function setTips(message, close) { 254 if ("" == message) { 255 $("#message").fadeOut(); 256 } else { 257 if (close != undefined && close == true) { 258 $("#message").html(message).fadeOut(); 259 } else { 260 $("#message").html(message).fadeIn(); 261 } 262 } 263 } 264 265 function clearEffects() { 266 if (handler != null) { 267 handler.destroy(); 268 } 269 } 270 271 //设置各种操作模式 272 function SetMode(mode) { 273 if (mode == "drawPloy") 274 { 275 tempPoints = []; 276 handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); 277 handler.setInputAction(function (click) { 278 var cartesian = viewer.camera.pickEllipsoid(click.position, scene.globe.ellipsoid); 279 if (cartesian) { 280 var cartographic = Cesium.Cartographic.fromCartesian(cartesian); 281 var longitudeString = Cesium.Math.toDegrees(cartographic.longitude); 282 var latitudeString = Cesium.Math.toDegrees(cartographic.latitude); 283 tempPoints.push({ lon: longitudeString, lat: latitudeString }); 284 var tempLength = tempPoints.length; 285 drawPoint(tempPoints[tempPoints.length-1]); 286 if (tempLength > 1) { 287 drawLine(tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1], true); 288 } 289 } 290 }, Cesium.ScreenSpaceEventType.LEFT_CLICK); 291 292 handler.setInputAction(function (click) { 293 var cartesian = viewer.camera.pickEllipsoid(click.position, scene.globe.ellipsoid); 294 if (cartesian) { 295 var tempLength = tempPoints.length; 296 if (tempLength < 3) { 297 alert('请选择3个以上的点再执行闭合操作命令'); 298 } else { 299 drawLine(tempPoints[0], tempPoints[tempPoints.length - 1], true); 300 drawPoly(tempPoints); 301 highLightAssetsInArea(tempPoints); 302 alert('多边形面积' + SphericalPolygonAreaMeters(tempPoints) + '平方米'); 303 tempPoints = []; 304 } 305 306 } 307 }, Cesium.ScreenSpaceEventType.RIGHT_CLICK); 308 } 309 else if (mode == "pickBuilding") 310 { 311 handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); 312 handler.setInputAction(function(click) { 313 var pick = scene.pick(click.position); 314 if (Cesium.defined(pick) && Cesium.defined(pick.node) && Cesium.defined(pick.mesh)) { 315 for (var i = 0; i < models.length; i ++) { 316 if (models[i].id == pick.node._model.id) { 317 var modelName = models[i].name; 318 var modelId = models[i].id; 319 var modelBimId = models[i].pid; 320 highLigthModel(modelId); 321 viewer.camera.flyTo({ 322 destination: Cesium.Cartesian3.fromDegrees(models[i].lon, parseFloat(models[i].lat) - 0.01, 2000.0), 323 orientation: { 324 direction: new Cesium.Cartesian3(0.5437275903005284, -0.8386290220423197, -0.03258329225728158), 325 up: new Cesium.Cartesian3(0.05520718287689969, -0.00299987805272847, 0.9984704140286108) 326 }, 327 complete: function() { 328 if (confirm("你选择的是" + modelName + ",是否查看详细模型?")) { 329 LoadBim(modelBimId); 330 } 331 unHighLightModel(modelId); 332 }, 333 }); 334 } 335 } 336 } 337 }, Cesium.ScreenSpaceEventType.LEFT_CLICK); 338 } 339 else if ("addPin" == mode) 340 { 341 handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); 342 handler.setInputAction(function (click) { 343 var cartesian = viewer.camera.pickEllipsoid(click.position, scene.globe.ellipsoid); 344 if (cartesian) { 345 var cartographic = Cesium.Cartographic.fromCartesian(cartesian); 346 tempPinLon = Cesium.Math.toDegrees(cartographic.longitude); 347 tempPinLat = Cesium.Math.toDegrees(cartographic.latitude); 348 $('#addPinModal').modal('show'); 349 } 350 }, Cesium.ScreenSpaceEventType.LEFT_CLICK); 351 } 352 else if ("erase" == mode) 353 { 354 handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); 355 handler.setInputAction(function (click) { 356 var pick = scene.pick(click.position); 357 if (Cesium.defined(pick) && Cesium.defined(pick.id) && Cesium.defined(pick.id._id)) { 358 for (var i = 0; i < models.length; i++) { 359 if ( pick.id != undefined && tempPinEntities[i].id == pick.id._id) { 360 removePoint(tempPinEntities[i]); 361 tempPinEntities.splice(i, 1); 362 } 363 } 364 } 365 }, Cesium.ScreenSpaceEventType.LEFT_CLICK); 366 } 367 else if ("drawLine" == mode) 368 { 369 tempPoints = []; 370 handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); 371 handler.setInputAction(function (click) { 372 var cartesian = viewer.camera.pickEllipsoid(click.position, scene.globe.ellipsoid); 373 if (cartesian) { 374 var cartographic = Cesium.Cartographic.fromCartesian(cartesian); 375 var longitudeString = Cesium.Math.toDegrees(cartographic.longitude); 376 var latitudeString = Cesium.Math.toDegrees(cartographic.latitude); 377 tempPoints.push({ lon: longitudeString, lat: latitudeString }); 378 var tempLength = tempPoints.length; 379 drawPoint(tempPoints[tempPoints.length - 1]); 380 if (tempLength > 1) { 381 drawLine(tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1], true); 382 } 383 } 384 }, Cesium.ScreenSpaceEventType.LEFT_CLICK); 385 handler.setInputAction(function (click) { 386 tempPoints = []; 387 }, Cesium.ScreenSpaceEventType.RIGHT_CLICK); 388 } 389 else if ("drawCircle" == mode) 390 { 391 tempPoints = []; 392 handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); 393 handler.setInputAction(function (click) { 394 var cartesian = viewer.camera.pickEllipsoid(click.position, scene.globe.ellipsoid); 395 if (cartesian) { 396 var cartographic = Cesium.Cartographic.fromCartesian(cartesian); 397 var longitudeString = Cesium.Math.toDegrees(cartographic.longitude); 398 var latitudeString = Cesium.Math.toDegrees(cartographic.latitude); 399 tempPoints.push({ lon: longitudeString, lat: latitudeString }); 400 var tempLength = tempPoints.length; 401 if (tempLength == 1) { 402 drawPoint(tempPoints[0]); 403 } 404 else if (tempLength == 2) { 405 drawPoint(tempPoints[1]); 406 drawLine(tempPoints[0], tempPoints[1], true); 407 //算两点间距离 408 var distance = getFlatternDistance(tempPoints[0].lat, tempPoints[0].lon, tempPoints[1].lat, tempPoints[1].lon); 409 410 var entity = 411 viewer. 412 entities.add({ 413 position: Cesium.Cartesian3.fromDegrees(tempPoints[0].lon, tempPoints[0].lat), 414 ellipse: { 415 semiMinorAxis: distance, 416 semiMajorAxis: distance, 417 height: 0, 418 material: Cesium.Color.fromRandom({ alpha: 0.8 }) 419 } 420 }); 421 tempEntities.push(entity); 422 423 //高亮圈内模型 424 for (var i = 0; i < loadedModels.length; i++) { 425 for (var j = 0; j < models.length; j++) { 426 if (loadedModels[i].id == models[j].id && getFlatternDistance(models[j].lat, models[j].lon, tempPoints[0].lat, tempPoints[0].lon) <= distance) { 427 loadedModels[i].color = Cesium.Color.SPRINGGREEN; 428 } 429 } 430 } 431 432 //面积 433 setTimeout(function () { alert("面积是 " + Math.PI * distance * distance + "平方米") },500); 434 435 tempPoints = []; 436 } 437 } 438 }, Cesium.ScreenSpaceEventType.LEFT_CLICK); 439 } 440 else if ("drawSquare" == mode) { 441 tempPoints = []; 442 handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); 443 handler.setInputAction(function (click) { 444 var cartesian = viewer.camera.pickEllipsoid(click.position, scene.globe.ellipsoid); 445 if (cartesian) { 446 var cartographic = Cesium.Cartographic.fromCartesian(cartesian); 447 var longitudeString = Cesium.Math.toDegrees(cartographic.longitude); 448 var latitudeString = Cesium.Math.toDegrees(cartographic.latitude); 449 tempPoints.push({ lon: longitudeString, lat: latitudeString }); 450 var tempLength = tempPoints.length; 451 if (tempLength == 1) { 452 drawPoint(tempPoints[0]); 453 } 454 else if (tempLength == 2) { 455 //算两点间距离 456 var distance = getFlatternDistance(tempPoints[0].lat, tempPoints[0].lon, tempPoints[1].lat, tempPoints[1].lon); 457 458 var entity = 459 viewer. 460 entities.add({ 461 position: Cesium.Cartesian3.fromDegrees(tempPoints[0].lon, tempPoints[0].lat), 462 ellipse: { 463 semiMinorAxis: distance, 464 semiMajorAxis: distance, 465 height: 0, 466 material: Cesium.Color.fromRandom({ alpha: 0.8 }) 467 } 468 }); 469 tempEntities.push(entity); 470 471 //高亮圈内模型 472 for (var i = 0; i < loadedModels.length; i++) { 473 for (var j = 0; j < models.length; j++) { 474 if (loadedModels[i].id == models[j].id && getFlatternDistance(models[j].lat, models[j].lon, tempPoints[0].lat, tempPoints[0].lon) <= distance) { 475 loadedModels[i].color = Cesium.Color.SPRINGGREEN; 476 } 477 } 478 } 479 480 tempPoints = []; 481 } 482 } 483 }, Cesium.ScreenSpaceEventType.LEFT_CLICK); 484 } 485 } 486 487 function drawPoint(point) { 488 var entity = 489 viewer.entities.add({ 490 position: Cesium.Cartesian3.fromDegrees(point.lon, point.lat), 491 point: { 492 pixelSize: 10, 493 color: Cesium.Color.CHARTREUSE 494 } 495 }); 496 tempEntities.push(entity); 497 } 498 499 function removePoint(entity) { 500 viewer.entities.remove(entity); 501 } 502 503 function drawLine(point1, point2, showDistance) { 504 var entity = 505 viewer.entities.add({ 506 polyline: { 507 positions: [Cesium.Cartesian3.fromDegrees(point1.lon, point1.lat), Cesium.Cartesian3.fromDegrees(point2.lon, point2.lat)], 508 width: 10.0, 509 material: new Cesium.PolylineGlowMaterialProperty({ 510 color: Cesium.Color.CHARTREUSE.withAlpha(.5) 511 }) 512 } 513 }); 514 tempEntities.push(entity); 515 if (showDistance) { 516 var w = Math.abs(point1.lon - point2.lon); 517 var h = Math.abs(point1.lat - point2.lat); 518 var offsetV = w >= h ? 0.0005 : 0; 519 var offsetH = w < h ? 0.001 : 0; 520 var distance = getFlatternDistance(point1.lat, point1.lon, point2.lat, point2.lon); 521 entity = 522 viewer.entities.add({ 523 position: Cesium.Cartesian3.fromDegrees(((point1.lon + point2.lon) / 2) + offsetH, 524 ((point1.lat + point2.lat) / 2) + offsetV), 525 label: { 526 text: distance.toFixed(1) + 'm', 527 font: '22px Helvetica', 528 fillColor: Cesium.Color.WHITE 529 } 530 }); 531 tempEntities.push(entity); 532 } 533 } 534 535 function drawPoly(points) { 536 var pArray = []; 537 for (var i = 0; i < points.length; i ++) { 538 pArray.push(points[i].lon); 539 pArray.push(points[i].lat); 540 } 541 var entity = 542 viewer.entities.add({ 543 polygon: { 544 hierarchy: new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArray(pArray)), 545 material: Cesium.Color.CHARTREUSE.withAlpha(.5) 546 } 547 }); 548 tempEntities.push(entity); 549 } 550 551 function getColor(colorName, alpha) { 552 var color = Cesium.Color[colorName.toUpperCase()]; 553 return Cesium.Color.fromAlpha(color, parseFloat(alpha)); 554 } 555 556 //判断点是否在多边形内 557 function PointInPoly(point, polyPoints) { 558 for (var c = false, i = -1, l = polyPoints.length, j = l - 1; ++i < l; j = i) 559 ((polyPoints[i].lat <= point.lat && point.lat < polyPoints[j].lat) || (polyPoints[j].lat <= point.lat && point.lat < polyPoints[i].lat)) 560 && (point.lon < (polyPoints[j].lon - polyPoints[i].lon) * (point.lat - polyPoints[i].lat) / (polyPoints[j].lat - polyPoints[i].lat) + polyPoints[i].lon) 561 && (c = !c); 562 return c; 563 } 564 565 //选区内模型高亮 566 function highLightAssetsInArea(points) { 567 568 for (var i = 0; i < loadedModels.length; i++) { 569 for (var j = 0; j < models.length; j ++) { 570 if (loadedModels[i].id == models[j].id && PointInPoly(models[j], points)) { 571 loadedModels[i].color = Cesium.Color.SPRINGGREEN; 572 } 573 } 574 } 575 } 576 577 //高亮模型 578 function highLigthModel(modelId) { 579 for (var i = 0; i < loadedModels.length; i ++) { 580 if (loadedModels[i].id == modelId) { 581 loadedModels[i].color = Cesium.Color.SPRINGGREEN; 582 } 583 } 584 } 585 586 //取消高亮模型 587 function unHighLightModel(modelId) { 588 for (var i = 0; i < loadedModels.length; i++) { 589 if (loadedModels[i].id == modelId) { 590 loadedModels[i].color = { 591 red: 1, 592 green: 1, 593 blue: 1, 594 alpha: 1 595 }; 596 } 597 } 598 } 599 600 //定位 601 function goLocation() { 602 $('#flyToModal').modal('hide'); 603 $('#flyToModal').on('hidden.bs.modal', function(e) { 604 viewer.camera.flyTo({ 605 destination: Cesium.Cartesian3.fromDegrees($("#jumpLon").val(), $("#jumpLat").val(), 1000.0) 606 }); 607 }); 608 } 609 610 //加点 611 function addPin() { 612 $('#addPinModal').modal('hide'); 613 var pin = viewer.entities.add({ 614 name: $("#pinContent").val(), 615 position: Cesium.Cartesian3.fromDegrees(tempPinLon, tempPinLat), 616 billboard: { 617 image: $("#pinLabel").val() == "" ? pinBuilder.fromColor(Cesium.Color[$("#pinColor").val().toUpperCase()], 48).toDataURL() : 618 pinBuilder.fromText($("#pinLabel").val(), Cesium.Color[$("#pinColor").val().toUpperCase()], 64).toDataURL(), 619 verticalOrigin: Cesium.VerticalOrigin.BOTTOM 620 } 621 }); 622 tempPinEntities.push(pin); 623 $("#pinLabel").val(""); 624 $("#pinContent").val(""); 625 } 626 627 //计算两点间距离 628 function getFlatternDistance(lat1, lng1, lat2, lng2) { 629 var EARTH_RADIUS = 6378137.0; //单位M 630 var PI = Math.PI; 631 632 function getRad(d) { 633 return d * PI / 180.0; 634 } 635 var f = getRad((lat1 + lat2) / 2); 636 var g = getRad((lat1 - lat2) / 2); 637 var l = getRad((lng1 - lng2) / 2); 638 639 var sg = Math.sin(g); 640 var sl = Math.sin(l); 641 var sf = Math.sin(f); 642 643 var s, c, w, r, d, h1, h2; 644 var a = EARTH_RADIUS; 645 var fl = 1 / 298.257; 646 647 sg = sg * sg; 648 sl = sl * sl; 649 sf = sf * sf; 650 651 s = sg * (1 - sl) + (1 - sf) * sl; 652 c = (1 - sg) * (1 - sl) + sf * sl; 653 654 w = Math.atan(Math.sqrt(s / c)); 655 r = Math.sqrt(s * c) / w; 656 d = 2 * w * a; 657 h1 = (3 * r - 1) / 2 / c; 658 h2 = (3 * r + 1) / 2 / s; 659 660 return d * (1 + fl * (h1 * sf * (1 - sg) - h2 * (1 - sf) * sg)); 661 } 662 663 //计算多边形面积 664 var earthRadiusMeters = 6371000.0; 665 var metersPerDegree = 2.0 * Math.PI * earthRadiusMeters / 360.0; 666 var radiansPerDegree = Math.PI / 180.0; 667 var degreesPerRadian = 180.0 / Math.PI; 668 var pointArr; 669 function SphericalPolygonAreaMeters(points) { 670 var totalAngle = 0; 671 for (var i = 0; i < points.length; i++) { 672 var j = (i + 1) % points.length; 673 var k = (i + 2) % points.length; 674 totalAngle += Angle(points[i], points[j], points[k]); 675 } 676 var planarTotalAngle = (points.length - 2) * 180.0; 677 var sphericalExcess = totalAngle - planarTotalAngle; 678 if (sphericalExcess > 420.0) { 679 totalAngle = points.length * 360.0 - totalAngle; 680 sphericalExcess = totalAngle - planarTotalAngle; 681 } else if (sphericalExcess > 300.0 && sphericalExcess < 420.0) { 682 sphericalExcess = Math.abs(360.0 - sphericalExcess); 683 } 684 return sphericalExcess * radiansPerDegree * earthRadiusMeters * earthRadiusMeters; 685 } 686 687 /*角度*/ 688 function Angle(p1, p2, p3) { 689 var bearing21 = Bearing(p2, p1); 690 var bearing23 = Bearing(p2, p3); 691 var angle = bearing21 - bearing23; 692 if (angle < 0) { 693 angle += 360; 694 } 695 return angle; 696 } 697 /*方向*/ 698 function Bearing(from, to) { 699 var lat1 = from.lat * radiansPerDegree; 700 var lon1 = from.lon * radiansPerDegree; 701 var lat2 = to.lat * radiansPerDegree; 702 var lon2 = to.lon * radiansPerDegree; 703 var angle = -Math.atan2(Math.sin(lon1 - lon2) * Math.cos(lat2), Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2)); 704 if (angle < 0) { 705 angle += Math.PI * 2.0; 706 } 707 angle = angle * degreesPerRadian; 708 return angle; 709 } 710 711 function LoadBim(projId) { 712 //加载模型 713 $('#myTabs li:eq(1) a').tab('show'); 714 715 }