CutFill.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /* 引入Cesium */
  2. // import * as Cesium from 'Cesium';
  3. /**
  4. * 方量分析
  5. * 过获取绘制的范围,获取面的坐标,对该面的坐标进行插值,将面等分为一个个的小三角面,求该三角面内定点的平均高度作为该三角面对应的高度。然后 体积 = 三角形面积 x 高度,然后对体积进行累加,即获取了体积。
  6. * 填方体积为:当当前点的地形高度低于基准面高度时,用三角面的面积乘以基准面高度减去当前点的地形高度,然后体积求和。
  7. * 挖方体积为:当当前点的地形高度高于基准面高度时,用三角面的面积乘以当前点的地形高度减去基准面高度,然后体积求和。
  8. */
  9. class CutFill {
  10. /**
  11. * 默认初始化
  12. * @param {Object} viewer 三维场景
  13. */
  14. constructor(viewer) {
  15. if (!viewer) throw new Cesium.DeveloperError('no viewer object!');
  16. this._viewer = viewer;
  17. this.delEntitys = [];
  18. this.maxHeigh = -1000000; //用来记录整块区域的最小高程
  19. }
  20. /**
  21. * @ignore
  22. * @param {Object} options
  23. */
  24. _VolumeAnalysis(options) {
  25. let _self = this;
  26. let cutArea = 0,
  27. cutVolume = 0,
  28. fillArea = 0,
  29. fillVolume = 0;
  30. const indices = options.geom.indices; //获取顶点索引数据
  31. const positions = options.geom.attributes.position.values;
  32. for (let index = 0; index < indices.length; index += 3) {
  33. const pos0 = _self._returnPosition(positions, indices[index]);
  34. const pos1 = _self._returnPosition(positions, indices[index + 1]);
  35. const pos2 = _self._returnPosition(positions, indices[index + 2]);
  36. let _entities = _self._viewer.entities.add({
  37. name: "三角面",
  38. polygon: {
  39. hierarchy: [pos0.heightPos, pos1.heightPos, pos2.heightPos],
  40. perPositionHeight: true,
  41. material: Cesium.Color.fromRandom(),
  42. extrudedHeight: options.height,
  43. outline: true,
  44. outlineColor: Cesium.Color.BLACK,
  45. },
  46. });
  47. _self.delEntitys.push(_entities);
  48. //水平状态下三角形面积
  49. const area = _self._computeArea4Triangle(
  50. pos0.noHeightPos,
  51. pos1.noHeightPos,
  52. pos2.noHeightPos
  53. );
  54. //计算三个点的均高
  55. //计算三角体的平均高度
  56. const height = (pos0.height + pos1.height + pos2.height) / 3;
  57. //判断是 填方还是挖方
  58. //如果三角体低于基准面,则需要填方
  59. if (height < options.height) {
  60. // 需要填方的部分
  61. fillArea += area;
  62. const volume = area * (options.height - height);
  63. fillVolume += volume;
  64. } else {
  65. // 需要挖方的部分
  66. cutArea += area;
  67. const volume = area * (height - options.height);
  68. cutVolume += volume;
  69. }
  70. }
  71. let allArea = cutArea + fillArea;
  72. allArea = allArea.toFixed(2);
  73. cutArea = cutArea.toFixed(2);
  74. cutVolume = cutVolume.toFixed(2);
  75. fillArea = fillArea.toFixed(2);
  76. fillVolume = fillVolume.toFixed(2);
  77. let result = {
  78. allArea,
  79. cutArea,
  80. cutVolume,
  81. fillArea,
  82. fillVolume,
  83. };
  84. return result;
  85. }
  86. /**
  87. * 海伦公式求取三角形面积
  88. * @ignore
  89. * @param {*} pos1
  90. * @param {*} pos2
  91. * @param {*} pos3
  92. * @returns 三角形面积㎡
  93. */
  94. _computeArea4Triangle(pos1, pos2, pos3) {
  95. let a = Cesium.Cartesian3.distance(pos1, pos2);
  96. let b = Cesium.Cartesian3.distance(pos2, pos3);
  97. let c = Cesium.Cartesian3.distance(pos3, pos1);
  98. let S = (a + b + c) / 2;
  99. return Math.sqrt(S * (S - a) * (S - b) * (S - c));
  100. }
  101. /**
  102. * @ignore
  103. * @param {Object} positions
  104. * @param {Object} index
  105. */
  106. _returnPosition(positions, index) {
  107. let cartesian = new Cesium.Cartesian3(
  108. positions[index * 3],
  109. positions[index * 3 + 1],
  110. positions[index * 3 + 2]
  111. );
  112. let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
  113. let height = this._viewer.scene.sampleHeightSupported ?
  114. this._viewer.scene.sampleHeight(cartographic) :
  115. this._viewer.scene.globe.getHeight(cartographic);
  116. if (height > this.maxHeigh) {
  117. this.maxHeigh = height;
  118. }
  119. return {
  120. heightPos: Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, height),
  121. noHeightPos: Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0),
  122. height: height,
  123. };
  124. }
  125. }
  126. /**
  127. * 通用对外公开函数
  128. */
  129. Object.assign(CutFill.prototype, /** @lends CutFill.prototype */ {
  130. /**
  131. * 方量分析
  132. * @param {Object} points
  133. * @param {Object} options
  134. */
  135. createPolygonGeo(points, options) {
  136. //异步函数
  137. return new Promise((resolve, reject) => {
  138. let _self = this;
  139. options = options || {};
  140. options.precision = Cesium.defaultValue(options.precision, 256);
  141. options.height = Cesium.defaultValue(options.height, 10);
  142. //移除
  143. _self.remove();
  144. /* 转换坐标 */
  145. let positions = [];
  146. for (let i = 0; i < points.length; i++) {
  147. if (points[i] instanceof Cesium.Cartesian3) {
  148. positions.push(points[i]);
  149. } else {
  150. positions.push(Cesium.Cartesian3.fromDegrees(points[i][0], points[i][1], points[i][1] || 0));
  151. }
  152. }
  153. //计算网格粒度-精度
  154. let granularity = Math.PI / Math.pow(2, 11);
  155. granularity = granularity / options.precision;
  156. let polygonGeometry = new Cesium.PolygonGeometry.fromPositions({
  157. positions: positions,
  158. vertexFormat: Cesium.PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
  159. granularity: granularity,
  160. });
  161. options.geom = new Cesium.PolygonGeometry.createGeometry(polygonGeometry);
  162. let result = _self._VolumeAnalysis(options);
  163. //设置球体背景色
  164. //设置地图basecolor为透明色
  165. _self._viewer.scene.globe.baseColor = new Cesium.Color(1, 1, 1, 0.9);
  166. _self._viewer.scene.screenSpaceCameraController.enableCollisionDetection = false; //相机与地形的碰撞检测,允许相机进入地下
  167. _self._viewer.scene.globe.translucency.enabled = true; //地球将被渲染为半透明的球体。
  168. resolve(result);
  169. });
  170. },
  171. /**
  172. * 移除
  173. */
  174. remove() {
  175. /* 删除符合条件的所有实体 */
  176. for (var i = 0; i < this.delEntitys.length; i++) {
  177. this._viewer.entities.remove(this.delEntitys[i]);
  178. }
  179. this._viewer.scene.screenSpaceCameraController.enableCollisionDetection = true; //相机与地形的碰撞检测,允许相机进入地下
  180. this._viewer.scene.globe.translucency.enabled = false; //地球将被渲染为半透明的球体。
  181. },
  182. });
  183. export default CutFill;