SightLine.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /* 引入Cesium */
  2. // import * as Cesium from 'Cesium';
  3. import CreateRemindertip from "../common/ReminderTip.js";
  4. import CoordTransform from "../common/CoordTransform.js";
  5. /**
  6. * 视线分析 通视分析 透视分析
  7. */
  8. class SightLine {
  9. /**
  10. * 默认初始化
  11. * @param {Object} viewer 三维场景
  12. */
  13. constructor(viewer) {
  14. if (!viewer) throw new Cesium.DeveloperError('no viewer object!');
  15. this._viewer = viewer;
  16. this._resultObject = {
  17. viewPoint: undefined, //通视分析起点
  18. targetPoints: [], //通视分析目标点集合
  19. targetPoint: undefined, //当前目标点
  20. objectExclude: [], //射线排除集合
  21. entities: [], //创建的Entity对象
  22. };
  23. }
  24. /**
  25. * 空间两点间距离
  26. * @ignore 忽略注释,注释不生成Doc
  27. * @param {Object} point1
  28. * @param {Object} point2
  29. */
  30. _distance(point1, point2) {
  31. let point1cartographic = Cesium.Cartographic.fromCartesian(point1);
  32. let point2cartographic = Cesium.Cartographic.fromCartesian(point2);
  33. /**根据经纬度计算出距离**/
  34. let geodesic = new Cesium.EllipsoidGeodesic();
  35. geodesic.setEndPoints(point1cartographic, point2cartographic);
  36. let s = geodesic.surfaceDistance;
  37. //返回两点之间的距离
  38. s = Math.sqrt(
  39. Math.pow(s, 2) +
  40. Math.pow(point2cartographic.height - point1cartographic.height, 2)
  41. );
  42. return s;
  43. }
  44. /**
  45. * 检测程序运行环境
  46. * @return {SightLine.RuntimeEnvironment}
  47. */
  48. _checkAppOrWeb() {
  49. if (window.navigator.userAgent.match(
  50. /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
  51. )) {
  52. return SightLine.RuntimeEnvironment.App;
  53. } else {
  54. return SightLine.RuntimeEnvironment.Web;
  55. }
  56. }
  57. /**
  58. * 是否是运行于App
  59. * @ignore
  60. */
  61. _isRuntimeApp() {
  62. if (this._checkAppOrWeb() === SightLine.RuntimeEnvironment.App) {
  63. return true;
  64. }
  65. return false;
  66. }
  67. /**
  68. * 是否是运行于App
  69. * @ignore
  70. */
  71. _isRuntimeWeb() {
  72. if (this._checkAppOrWeb() === SightLine.RuntimeEnvironment.Web) {
  73. return true;
  74. }
  75. return false;
  76. }
  77. /**
  78. * @ignore
  79. * 创建操作的主容器
  80. */
  81. _createOperationMainDom() {
  82. //创建画布
  83. let buttonDiv = document.createElement('div');
  84. buttonDiv.id = "drawButtonDiv";
  85. buttonDiv.style.width = '80px';
  86. buttonDiv.style.backgroundColor = 'rgba(5, 45, 155, 0.7)';
  87. buttonDiv.style.borderRadius = '5px';
  88. buttonDiv.style.display = 'flex';
  89. buttonDiv.style.flexDirection = 'column';
  90. buttonDiv.style.padding = '8px';
  91. buttonDiv.style.justifyContent = 'center';
  92. buttonDiv.style.position = 'absolute';
  93. buttonDiv.style.bottom = '150px';
  94. buttonDiv.style.right = '10px';
  95. // let btnUndo = document.createElement('button');
  96. // btnUndo.id = "btnDrawBackout";
  97. // btnUndo.style.height = '30px';
  98. // btnUndo.style.marginBottom = '8px';
  99. // btnUndo.style.backgroundColor = 'rgba(52, 137, 255, 1.0)';
  100. // btnUndo.style.color = 'rgb(255, 255, 255)';
  101. // btnUndo.style.border = '0px solid red';
  102. // btnUndo.style.borderRadius = '5px';
  103. // btnUndo.innerHTML = '回退';
  104. // btnUndo.style.fontSize = '13px';
  105. // btnUndo.style.cursor = 'pointer';
  106. // buttonDiv.appendChild(btnUndo);
  107. let btnCompletion = document.createElement('button');
  108. btnCompletion.id = "btnDrawComplete";
  109. btnCompletion.style.height = '30px';
  110. btnCompletion.style.backgroundColor = 'rgba(88, 185, 45, 1.0)';
  111. btnCompletion.style.color = 'rgb(255, 255, 255)';
  112. btnCompletion.style.border = '0px solid red';
  113. btnCompletion.style.borderRadius = '5px';
  114. btnCompletion.innerHTML = '完成';
  115. btnCompletion.style.fontSize = '13px';
  116. btnCompletion.style.cursor = 'pointer';
  117. buttonDiv.appendChild(btnCompletion);
  118. /* 加入到页面 */
  119. document.body.appendChild(buttonDiv);
  120. }
  121. /**
  122. * 创建顶部弹出提示消息 1秒后自动消失
  123. * @ignore
  124. * @param {String} message 消息内容
  125. */
  126. _showTooltipMessage(message) {
  127. let msgMainDom = document.getElementById('messageMainDom');
  128. if (msgMainDom !== null && msgMainDom !== undefined) {
  129. document.body.removeChild(msgMainDom);
  130. }
  131. msgMainDom = document.createElement('div');
  132. msgMainDom.style.width = '30%';
  133. msgMainDom.style.backgroundColor = 'rgba(237, 248, 230, 1.0)';
  134. msgMainDom.style.height = '45px';
  135. msgMainDom.style.border = 'solid 2px rgb(219, 241, 208)';
  136. msgMainDom.style.borderRadius = '8px';
  137. msgMainDom.style.display = 'flex';
  138. msgMainDom.style.alignItems = 'center';
  139. msgMainDom.style.paddingLeft = '10px';
  140. msgMainDom.style.color = 'rgb(91, 188, 48)';
  141. msgMainDom.style.fontSize = '14px';
  142. msgMainDom.style.fontWeight = '600';
  143. msgMainDom.style.position = 'absolute';
  144. msgMainDom.style.left = '35%';
  145. msgMainDom.style.transition = 'transform 1s';
  146. msgMainDom.style.transform = 'translateY(-90px)';
  147. msgMainDom.style.top = '0px';
  148. msgMainDom.style.zIndex = 1000;
  149. document.body.appendChild(msgMainDom);
  150. let strHtml = '';
  151. strHtml += "<div style='"
  152. strHtml += "background-color: rgb(88, 185, 45);";
  153. strHtml += "color: rgb(255, 255, 255);";
  154. strHtml += "height: 24px;";
  155. strHtml += "width: 24px;";
  156. strHtml += "border-radius: 20px;";
  157. strHtml += "display: flex;";
  158. strHtml += "justify-content: center;";
  159. strHtml += "align-items: center;";
  160. strHtml += "font-size: 14px;";
  161. strHtml += "margin-right: 18px;";
  162. strHtml += "'>&#10003</div>";
  163. strHtml += "<div>" + message + "</div>";
  164. msgMainDom.innerHTML = strHtml;
  165. msgMainDom.addEventListener('transitionend', function() {
  166. setTimeout(function() {
  167. document.body.removeChild(msgMainDom);
  168. }, 1000);
  169. }, false);
  170. setTimeout(function() {
  171. msgMainDom.style.transform = 'translateY(50px)';
  172. }, 100)
  173. }
  174. }
  175. /**
  176. * 通用对外公开函数
  177. */
  178. Object.assign(SightLine.prototype, /** @lends SightLine.prototype */ {
  179. /**
  180. * 开启通视分析
  181. */
  182. startSightLine() {
  183. let _self = this;
  184. _self.clearAll();
  185. let toolTip = "左键单击创建视角起点";
  186. if (this._isRuntimeApp()) {
  187. toolTip = "单击创建视角起点";
  188. _self._showTooltipMessage(toolTip);
  189. }
  190. _self.handler = new Cesium.ScreenSpaceEventHandler(_self._viewer.canvas);
  191. _self.handler.setInputAction((event) => {
  192. let loc = CoordTransform._transfromFromScreenPoint(_self._viewer, event.position);
  193. toolTip = "左键创建视角终点,右键结束通视分析";
  194. if (this._isRuntimeApp()) {
  195. toolTip = "再次单击创建视角终点";
  196. _self._showTooltipMessage(toolTip);
  197. }
  198. if (!Cesium.defined(loc.sLocation)) return;
  199. let cartesian = loc.sLocation;
  200. if (!_self._resultObject.viewPoint) {
  201. _self._resultObject.viewPoint = cartesian;
  202. let pointEntity = _self._viewer.entities.add({
  203. position: cartesian,
  204. point: {
  205. color: Cesium.Color.YELLOW,
  206. pixelSize: 5,
  207. },
  208. //文字标签
  209. label: {
  210. text: "观察位置",
  211. font: '12px sans-serif',
  212. fillColor: new Cesium.Color(255 / 255, 255 / 255, 255 / 255, 1.0),
  213. outlineColor: new Cesium.Color(0, 154 / 255, 94 / 255, 1.0),
  214. style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  215. outlineWidth: 1.0,
  216. verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
  217. pixelOffset: new Cesium.Cartesian2(0, -28),
  218. showBackground: true,
  219. backgroundColor: new Cesium.Color(0, 0, 0, 0.6),
  220. disableDepthTestDistance: Number.POSITIVE_INFINITY,
  221. },
  222. });
  223. _self._resultObject.objectExclude.push(pointEntity);
  224. _self._resultObject.entities.push(pointEntity);
  225. } else {
  226. _self._resultObject.targetPoint = cartesian;
  227. let pointEntity = _self._viewer.entities.add({
  228. position: cartesian,
  229. point: {
  230. color: Cesium.Color.YELLOW,
  231. pixelSize: 5,
  232. },
  233. //文字标签
  234. label: {
  235. text: "目标位置",
  236. font: '12px sans-serif',
  237. fillColor: new Cesium.Color(255 / 255, 255 / 255, 255 / 255, 1.0),
  238. outlineColor: new Cesium.Color(0, 154 / 255, 94 / 255, 1.0),
  239. style: Cesium.LabelStyle.FILL_AND_OUTLINE,
  240. outlineWidth: 1.0,
  241. verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
  242. pixelOffset: new Cesium.Cartesian2(0, -28),
  243. showBackground: true,
  244. backgroundColor: new Cesium.Color(0, 0, 0, 0.6),
  245. disableDepthTestDistance: Number.POSITIVE_INFINITY,
  246. },
  247. });
  248. _self._resultObject.objectExclude.push(pointEntity);
  249. _self._resultObject.entities.push(pointEntity);
  250. let direction = Cesium.Cartesian3.normalize(
  251. Cesium.Cartesian3.subtract(
  252. _self._resultObject.targetPoint,
  253. _self._resultObject.viewPoint,
  254. new Cesium.Cartesian3()
  255. ),
  256. new Cesium.Cartesian3()
  257. );
  258. let ray = new Cesium.Ray(_self._resultObject.viewPoint, direction);
  259. let result = _self._viewer.scene.pickFromRay(ray, _self._resultObject.objectExclude); // 计算交互点,返回第一个
  260. if (result) {
  261. let dis0 = _self._distance(
  262. _self._resultObject.viewPoint,
  263. _self._resultObject.targetPoint
  264. );
  265. let dis1 = _self._distance(
  266. _self._resultObject.viewPoint,
  267. result.position
  268. );
  269. let dis2 = _self._distance(
  270. result.position,
  271. _self._resultObject.targetPoint
  272. );
  273. console.log(dis0, dis1, dis2);
  274. if (dis0 > dis1) {
  275. let _poly0 = _self._viewer.entities.add({
  276. polyline: {
  277. positions: [_self._resultObject.viewPoint, result.position],
  278. material: Cesium.Color.GREEN,
  279. width: 3,
  280. },
  281. });
  282. _self._resultObject.entities.push(_poly0);
  283. let _poly1 = _self._viewer.entities.add({
  284. polyline: {
  285. positions: [result.position, _self._resultObject.targetPoint],
  286. material: Cesium.Color.RED,
  287. width: 3,
  288. },
  289. });
  290. _self._resultObject.entities.push(_poly1);
  291. _self._resultObject.targetPoints.push({
  292. targetPoint: cartesian,
  293. visual: false, //如果dis2足够小,其实他是可视的
  294. distance: [dis0, dis1, dis2], //[初始点和终点,初始点和交点,交点和终点]
  295. });
  296. } else {
  297. let _poly2 = _self._viewer.entities.add({
  298. polyline: {
  299. positions: [
  300. _self._resultObject.viewPoint,
  301. _self._resultObject.targetPoint,
  302. ],
  303. material: Cesium.Color.GREEN,
  304. width: 3,
  305. },
  306. });
  307. _self._resultObject.entities.push(_poly2);
  308. _self._resultObject.targetPoints.push({
  309. targetPoint: cartesian,
  310. visual: true, //如果dis2足够小,其实他是可视的
  311. distance: [dis0, dis1, dis2], //[初始点和终点,初始点和交点,交点和终点]
  312. });
  313. }
  314. }
  315. }
  316. if (_self._resultObject.objectExclude.length === 2) {
  317. if (this._isRuntimeApp()) {
  318. //创建按钮
  319. _self._createOperationMainDom();
  320. //完成绘制
  321. document.getElementById("btnDrawComplete").onclick = () => {
  322. CreateRemindertip(toolTip, event.endPosition, false);
  323. _self.handler.destroy();
  324. let buttonDiv = document.getElementById("drawButtonDiv");
  325. if (buttonDiv) {
  326. //从页面移除
  327. document.body.removeChild(buttonDiv);
  328. }
  329. }
  330. }
  331. }
  332. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  333. _self.handler.setInputAction(function(move) {
  334. if (_self._isRuntimeApp()) return;
  335. CreateRemindertip(toolTip, move.endPosition, true);
  336. }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  337. _self.handler.setInputAction((event) => {
  338. CreateRemindertip(toolTip, event.endPosition, false);
  339. _self.handler.destroy();
  340. }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  341. },
  342. /**
  343. * 清除视域分析
  344. */
  345. clearAll() {
  346. this._resultObject.entities.forEach((element) => {
  347. this._viewer.entities.remove(element);
  348. });
  349. this._resultObject = {
  350. viewPoint: undefined, //通视分析起点
  351. targetPoints: [], //通视分析目标点集合
  352. targetPoint: undefined, //当前目标点
  353. objectExclude: [], //射线排除集合
  354. entities: [], //创建的Entity对象
  355. };
  356. if (this.handler) {
  357. this.handler.destroy();
  358. }
  359. let buttonDiv = document.getElementById("drawButtonDiv");
  360. if (buttonDiv) {
  361. //从页面移除
  362. document.body.removeChild(buttonDiv);
  363. }
  364. }
  365. });
  366. /**
  367. * 运行环境类型
  368. */
  369. SightLine.RuntimeEnvironment = Object.freeze(({
  370. App: 'app',
  371. Web: 'web'
  372. }))
  373. export default SightLine;