开源三维地球GIS引擎Cesium常用功能的开发

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() {
 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     }

原文地址:https://www.cnblogs.com/thanks/p/8377578.html

时间: 2024-11-16 10:06:57

开源三维地球GIS引擎Cesium常用功能的开发的相关文章

[GitHub开源]基于HTML5实现的在线三维地球,带你畅游世界

WebGlobe 前端基于原生WebGL实现的三维地球,没有使用第三方框架,无需插件,所有支持WebGL的浏览器均可使用. 三年前这个项目其实就存在了,不过当时把所有代码写到了一个文件中,当时有人问能不能开源,当时觉得代码写的比较乱,就没开源. 最近把代码拆分成AMD格式的,结构清晰一些.以后会逐渐完善,增加更多功能,打算做成一个Web版本的三维在线地图网站. 如果对WebGL不熟悉,可以参考一下之前写的<WebGL自学教程专栏>. 下面是以前写的关于WebGlobe实现的一些理论基础: We

工作流框架Activiti常用功能初探

Activiti项目是一项新的基于Apache许可的开源BPM平台,从基础开始构建,旨在提供支持新的BPMN 2.0标准,包括支持对象管理组(OMG),面对新技术的机遇,诸如互操作性和云架构,提供技术实现. BPMN是由BPMI(The Business Process Management Initiative)开发了一套标准叫业务流程建模符号(BPMN - Business Process Modeling Notation)BPMN定义了一个业务流程图(Business Process D

jQuery实战的常用功能

最近看了<jQuery实战第二版>有所得,写下这篇随笔,这篇随笔主要介绍jQuery中的常用功能. jQuery是一个兼容多浏览器的javascript库,核心理念是write less,do more(写得更少,做得更多).jQuery在2006年1月由美国人John Resig在纽约的barcamp发布,吸引了来自世界各地的众多JavaScript高手加入,由Dave Methvin率领团队进行开发.如今,jQuery已经成为最流行的javascript库,在世界前10000个访问最多的网

Keil的使用方法 - 常用功能(一)

Ⅰ.概述 学习一门软件的开发,开发工具的掌握可以说尤为重要.由于Keil集成开发工具支持多种MCU平台的开发,是市面上比较常见的,也是功能比较强大一款IDE.所以,对于大多数人说,选择Keil几乎是单片机或者嵌入式开发人员的不二选择. 从今天开始的接下来一些时间总结关于Keil的使用方法.计划前一阶段总结常见功能,后面综合性的总结Keil的功能. 关注我的朋友应该知道,我前面已经总结过Keil下载与安装 和 Keil新建软件工程这两篇文章,今天接着上面的文章来总结Keil使用方法之常用功能. 使

JavaScript 常用功能总结

编吐血整理加上翻译,太辛苦了~求赞! 本文主要总结了JavaScript 常用功能总结,如一些常用的额JS 对象,基本数据结构,功能函数等,还有一些常用的设计模式. 目录: 众所周知,JavaScript是动态的面向对象的编程语言,能够实现以下效果: 1. 丰富Web 网页功能 2. 丰富Web界面 3. 实现本地或远程存储. 4. 实现分布式网络应用的前端组件,并在后台进行数据存储管理. 5. 使用JavaScript可以实现完整的分布式Web 应用. JavaScript 中的数据类型 Ja

CSS3常用功能的写法

随着浏览器的升级,CSS3已经可以投入实际应用了. 但是,不同的浏览器有不同的CSS3实现,兼容性是一个大问题.上周的YDN介绍了CSS3 Please网站,该网站总结了一些常用功能的写法. 以下就是这些写法的详细介绍.所有代码都经过了Firefox 3.6和IE 8.0的验证,原文的错误之处也已得到改正. 一.圆角(Rounded Corner) .box_round { -moz-border-radius: 30px; /* FF1+ */ -webkit-border-radius: 3

Android Studio 常用功能介绍

Android Studio 的基本用法 界面介绍 讲解1 这个界面,显示了我们使用 Android Studio时经常接触到的功能面板. Project 面板.用于浏览项目文件. Project 面板会显示当前的所有的 module . android application module 会显示一个手机图标(下图中的 app ):android library module 会显示一个书架图标(下图中的 android-lib):java library module 会显示一个咖啡图标(下

工程管理,用网页就够了!——Wish3D Earth在线三维地球强势上线

大型工程涉及到众多的施工队.管理单位和相关部门,相互之间需要传递的数据.文件的数量是惊人的,必须建立起有效的信息管理方法,使管理者及时把握工程的信息,全面准确地控制工程施工情况. 现代化的建筑工程管理信息量巨大,给主管人员和各层的工作人员带来了大量的工作和沉重的负担.依靠传统工作方式进行工程信息管理存在不少的问题: 1.规划没时间跑工地,造成返工工期延误 2.数据管理混乱不直观,决策效率低下 3.参与各方难以协调,增加沟通成本 Wish3D最新发布的Wish3D Earth,实现了三维地球的极致

旗正商业规则引擎和开源及其它规则引擎测试对比

规则引擎在基础软件,或者在很多系统中已经不是稀奇的玩意,最近这几年,国内不断兴起很多的规则引擎,至于什么是规则引擎,在这篇文章中,就不做介绍了,我想能看以下内容的,多少对规则引擎也都有所了解了. 国内在2003年的时候,出了第一款商业规则引擎-旗正商业规则引擎(VisualRules),为什么这么说呢,因为再此之前,国内所用的规则引擎,都是国外产品,或者开源产品,纯自主研发旗正是第一款,直至目前为止,纯自主研发的规则引擎少之又少.那么旗正商业规则引擎到底怎样?今天,给大家介绍一下,顺便,我们拿出