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. }

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

  1. 驰骋开源的asp.net工作流程引擎java工作流 2015 正文 驰骋工作流引擎ccflow6的功能列表

    关键词: 驰骋工作流引擎   ccflow的功能列表   工作流功能列表  表单引擎功能列表 我们工作流引擎ccflow6重构之后对功能做了一些调整,要想快速了解ccbpm的功能,可以以下面列表为准 ...

  2. 无插件,跨平台,基于WebGL的三维地球来了!!!

    用户通过浏览器即可递交数据到同一个地理信息系统中,操作简单,跨平台 ,无插件,可扩展,高效共享 ,完美匹配超大数据量发布! 近年来,随着计算机图形学.虚拟现实.卫星遥感.航空摄影.激光雷达等技术的迅猛 ...

  3. 开源项目OEIP 游戏引擎与音视频多媒体(UE4/Unity3D)

    现开源一个项目 OEIP 项目实现的功能Demo展示 这个项目演示了在UE4中,接入摄像机通过OEIP直接输出到UE4纹理上,并直接把UE4里的RenderTarget当做输入源通过OEIP里GPU管 ...

  4. C#两行代码实现三维地球

    一.             为什么要用三维地球? 三维地球是地理信息技术的一个重要发展方向,相比较二维地图技术,三维地球最大的特点是更直观更形象地表达地理信息和空间上的方位.我们可以在三维气象模拟. ...

  5. 前端3D引擎-Cesium自定义动态材质

    本文代码基于Vue-cli4和使用WebGL的地图引擎Cesium,主要内容为三维场景下不同对象的动态材质构建. 参考了很多文章,链接附在文末. 为不同的几何对象添加动态材质 不知道这一小节的名称概况 ...

  6. JavaScript 常用功能总结

    小编吐血整理加上翻译,太辛苦了~求赞! 本文主要总结了JavaScript 常用功能总结,如一些常用的JS 对象,基本数据结构,功能函数等,还有一些常用的设计模式. 目录: 众所周知,JavaScri ...

  7. CSS3常用功能的写法

    随着浏览器的升级,CSS3已经可以投入实际应用了. 但是,不同的浏览器有不同的CSS3实现,兼容性是一个大问题.上周的YDN介绍了CSS3 Please网站,该网站总结了一些常用功能的写法. 以下就是 ...

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

    Ⅰ.概述 学习一门软件的开发,开发工具的掌握可以说尤为重要.由于Keil集成开发工具支持多种MCU平台的开发,是市面上比较常见的,也是功能比较强大一款IDE.所以,对于大多数人说,选择Keil几乎是单 ...

  9. CSS3常用功能的写法 转

    CSS3常用功能的写法 作者: 阮一峰   随着浏览器的升级,CSS3已经可以投入实际应用了. 但是,不同的浏览器有不同的CSS3实现,兼容性是一个大问题.上周的YDN介绍了CSS3 Please网站 ...

随机推荐

  1. Python的__getattr__和__getattribute__

    __getattr____getattr__在当前主流的Python版本中都可用,重载__getattr__方法对类及其实例未定义的属性有效.也就属性是说,如果访问的属性存在,就不会调用__getat ...

  2. 公众号的Markdown排版利器:Md2All

    微信公众号:颜家大少如有问题或建议,请公众号留言 本文所用排版工具:Md2All,http://md.aclickall.com 公众号排版工具 公众号的排版编辑工具还真的不少,但功能大同小异,大部分 ...

  3. 七行代码开始flask

    前言: 对于现有的企业接口服务实现方式来说,Java比较适用于大型的并发式的业务场景:而对一些低IO的且功能简单的数据接口来说,Python似乎更合适.近几年流行的Flask可以说是专为接口式开发而生 ...

  4. [SharePoint]解决用户权限被无缘无故自动删除的问题

    前几天在维护公司内网的时候接到了一个case, 说是某个用户的权限无缘无故的就会被SharePoint自动去掉. 刚开始我还不愿意相信这个用户的说法,认为可能是权限赋的方法不对,有可能是被其他人误删了 ...

  5. 北斗卫星同步时钟(NTP网络时钟服务器)成功投运世界级工程港珠澳大桥

    北斗卫星同步时钟(NTP网络时钟服务器)成功投运世界级工程港珠澳大桥 北斗卫星同步时钟(NTP网络时钟服务器)成功投运世界级工程港珠澳大桥 本文由北京华人开创科技公司提供 原址在 http://www ...

  6. Django-mtv开发模式

    从著名的MVC模式开始说起 所谓的MVC就是把Web应用分为模型(M)控制器(C)和视图(V)三层,他们之间以一种插件式的.松耦合的房还是 连接在一起,模型负责业务对象与数据库的映射(ORM),视图负 ...

  7. Java中的集合框架(中)

    Map和HashMap Map接口 1.Map提供了一种映射关系,其中的元素是以键值对(key-value)的形式存储的,能够实现根据key快速查找value 2.Map中的键值对以Entry类型的对 ...

  8. django中数据库操作——in操作符

    django一对多关系中,为了实现按照一端查询多端,之前都是用extra操作符,经过查看官方文档还有in这个操作符,用这个实现更简单.直接上代码: 根据查询条件模糊查询所有符合条件的机架列表,然后根据 ...

  9. Friday for Oldboy

    计算机的硬件介绍 1.  CPU的工作流程:取指令->解码->执行 .  程序状态字寄存器(Program Status Word,PSW)中有一个二进制位控制这两种模式. 内核态:当cp ...

  10. Spring Boot项目构建

    环境准备 IDEA+JDK 1.8+Maven+mysql+SSM 1.使用Spring Boot框架可以大大加速Web应用的开发过程,首先在Maven项目依赖中引入spring-boot-start ...