PlanesMonitor.html 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. <!--********************************************************************
  2. * Copyright© 2000 - 2021 SuperMap Software Co.Ltd. All rights reserved.
  3. *********************************************************************-->
  4. <!DOCTYPE html>
  5. <html>
  6. <head>
  7. <meta charset="UTF-8">
  8. <title data-i18n="resources.title_planesMonitor"></title>
  9. <script type="text/javascript"
  10. include="jquery,bootstrap,moment,bootstrap-datetimepicker,bootstrap-select,geohash,randomcolor,widgets.alert"
  11. src="../js/include-web.js"></script>
  12. <style>
  13. .bootstrap-select {
  14. width: 168px !important;
  15. }
  16. .form-control.input-sm {
  17. width: 166px;
  18. }
  19. .form-group label {
  20. width: 100px;
  21. }
  22. .tips {
  23. z-index: 1000;
  24. }
  25. </style>
  26. </head>
  27. <body style=" margin: 0;overflow: hidden;background: #fff;width: 100%;height:100%;position: absolute;top: 0;">
  28. <div id="map" style="margin:0 auto;width: 100%;height: 100%;"></div>
  29. <script type="text/javascript" include="mapv,elasticsearch" src="../../dist/leaflet/include-leaflet.js"></script>
  30. <script type="text/javascript">
  31. var map;
  32. var timeControl,
  33. liveESService,//实时点查询的ESService
  34. trackESService,//历史轨迹查询的ESService
  35. liveRenderer,//实时点层
  36. trackLineLayers,//历史轨迹层
  37. trackLineMap,//轨迹线数据
  38. liveDataSet,//渲染用实时点数据集
  39. planeStyles,
  40. planeLabels,//航班信息提示框集合
  41. dataUrl = "https://iclient.supermap.io/es";
  42. var flightIdens = [
  43. 'CCA4189', 'CCA4187', 'CBJ5531', 'CSC8747', 'TBA9879',
  44. 'CCA4113', 'CES2251', 'CCA4521', 'CSC8927', 'CSC8811',
  45. 'CSC8857', 'UEA2711', 'UEA2205', 'CCA4439', 'CCA415',
  46. 'CSC8719', 'CSC8555', 'CCA4211', 'HDA825'
  47. ];
  48. init();
  49. function init() {
  50. initMap();
  51. liveESService = new SuperMap.ElasticSearch(dataUrl);
  52. trackESService = new SuperMap.ElasticSearch(dataUrl);
  53. planeStyles = initPlaneStyles();
  54. }
  55. //开始播放
  56. function start() {
  57. var options = getControlOptions();
  58. if (!timeControl) {
  59. timeControl = new SuperMap.TimeFlowControl(loadLiveData, options);
  60. } else {
  61. timeControl.updateOptions(options);
  62. }
  63. timeControl.start();
  64. }
  65. //暂停播放
  66. function pause() {
  67. if(!timeControl){
  68. return;
  69. }else{
  70. timeControl.pause();
  71. }
  72. }
  73. //停止播放
  74. function stop() {
  75. if(!timeControl){
  76. return;
  77. }else{
  78. timeControl.stop();
  79. clearAll();
  80. }
  81. }
  82. //时间控制器回调参数,即每次刷新时执行的操作,此处为向服务器请求数据并绘制。实时刷新执行。
  83. function loadLiveData(currentTime) {
  84. var datetime = moment(currentTime).format("YYYY-MM-DDTHH:mm:ss") + "Z";
  85. var options = getServiceParamOptions();
  86. var liveParameters = [];
  87. for (var i = 0; i < options.flightIds.length; i++) {
  88. var code = options.flightIds[i];
  89. if (code === 'ZUUU') {
  90. continue;
  91. }
  92. liveParameters.push({index: "flights", type: "flight_utc"});
  93. liveParameters.push({
  94. "query": {
  95. "bool": {
  96. "must": {"match": {"ident": code}},
  97. "filter": {"match": {"datetime": datetime}},
  98. }
  99. },
  100. "sort": {"time-ms": {"order": "ASC"}},
  101. "from": 0,
  102. "size": 20
  103. });
  104. }
  105. liveESService.msearch({body: liveParameters}, function (error, result) {
  106. if (error) {
  107. widgets.alert.showAlert(JSON.stringify(error), false);
  108. timeControl.stop();
  109. return;
  110. }
  111. result.responses && renderData.call(window, result.responses);
  112. });
  113. updateProgress(datetime);
  114. }
  115. //渲染实时点数据和轨迹图层
  116. function renderData(result) {
  117. if (timeControl && !timeControl.getRunning()) {
  118. return;
  119. }
  120. var data = createLiveRendererData(result);
  121. if (data.length < 1) {
  122. return;
  123. }
  124. updateDataSet(data);
  125. if (!liveRenderer) {
  126. liveRenderer = L.supermap.mapVLayer(liveDataSet, {
  127. zIndex: 800,
  128. globalCompositeOperation: "lighter",
  129. shadowColor: "white",
  130. shadowBlur: 2,
  131. draw: 'icon',
  132. methods: {
  133. click: function (evt) {
  134. renderLiveLabel.call(window, evt)
  135. }
  136. }
  137. }).addTo(map);
  138. } else {
  139. liveRenderer.update({data: liveDataSet});
  140. }
  141. renderTrackLineLayers();
  142. }
  143. //解析点查询结果数据为mapv数据
  144. function createLiveRendererData(results) {
  145. var data = [];
  146. results && results.map(function (result) {
  147. result.hits.hits.map(function (source) {
  148. var content = source._source;
  149. data.push({
  150. geometry: {
  151. type: 'Point',
  152. coordinates: [content.x, content.y],
  153. },
  154. deg: content.direction,
  155. icon: planeStyles[content.ident].img,
  156. ident: content.ident,
  157. content: content
  158. });
  159. trackLineMap = trackLineMap || {};
  160. if (!trackLineMap[content.ident]) {
  161. trackLineMap[content.ident] = [];
  162. }
  163. trackLineMap[content.ident].push([content.x, content.y]);
  164. });
  165. });
  166. return data;
  167. }
  168. //更新点数据集
  169. function updateDataSet(data) {
  170. if (!liveDataSet) {
  171. liveDataSet = new mapv.DataSet(data);
  172. return;
  173. }
  174. var dat = data.slice();
  175. var innerData = liveDataSet.get();
  176. for (var i = 0; i < dat.length; i++) {
  177. for (var j = 0; j < innerData.length; j++) {
  178. if (dat[i].ident === innerData[j].ident) {
  179. innerData[j] = dat[i];
  180. break;
  181. }
  182. }
  183. //如果是新的飞机Id则追加到飞机数组里
  184. if (j === innerData.length) {
  185. innerData.push(dat[i]);
  186. }
  187. }
  188. liveDataSet.set(innerData);
  189. }
  190. //渲染轨迹图层
  191. function renderTrackLineLayers() {
  192. if (!trackLineLayers) {
  193. trackLineLayers = {};
  194. }
  195. for (var id in trackLineMap) {
  196. renderTrackLayerData(id, trackLineMap[id]);
  197. }
  198. }
  199. //渲染飞机id所对应的轨迹
  200. function renderTrackLayerData(id, coordinates) {
  201. var trackLayer = trackLineLayers[id];
  202. var trackDataSet = new mapv.DataSet({
  203. geometry: {
  204. type: 'LineString',
  205. coordinates: coordinates
  206. }
  207. });
  208. if (!trackLayer) {
  209. var options = {
  210. zIndex: 1,
  211. strokeStyle: planeStyles[id].color,
  212. shadowColor: "white",
  213. shadowBlur: 1,
  214. lineCap: 'butt',
  215. lineJoin: "round",
  216. lineWidth: 3,
  217. draw: 'simple'
  218. };
  219. trackLayer = L.supermap.mapVLayer(trackDataSet, options).addTo(map);
  220. trackLineLayers[id] = trackLayer;
  221. } else {
  222. trackLayer.update({data: trackDataSet});
  223. }
  224. }
  225. //实时点的提示框
  226. function renderLiveLabel(data) {
  227. planeLabels = planeLabels || {};
  228. var content = data && data.content;
  229. if (!content) return;
  230. var latlng = L.latLng(content.y, content.x);
  231. var labelContent = resources.text_flight + ": " + content.ident + "<br>" +
  232. resources.text_startPoint + ": " + content.origin + "/" + content.originLabel + "<br>" +
  233. resources.text_endPoint + ": " + content.destination + "/" + content.destinationLabel + "<br>" +
  234. resources.text_time + ": " + content.datetime;
  235. if (planeLabels[content.ident]) {
  236. planeLabels[content.ident].remove();
  237. }
  238. planeLabels[content.ident] = L.popup({
  239. autoPan: false,
  240. maxWidth: 180,
  241. offset: L.point(0, -1)
  242. }).setContent(labelContent).setLatLng(latlng).addTo(map);
  243. }
  244. //初始化飞机的样式,颜色随机
  245. function initPlaneStyles() {
  246. var planes = {};
  247. var colors = randomColor({
  248. luminosity: 'bright',
  249. hue: 'random',
  250. alpha: 1,
  251. count: flightIdens.length
  252. });
  253. $.ajaxSetup({
  254. async: false
  255. });
  256. var count = flightIdens.length;
  257. $.get('../data/plane.svg', function (svg) {
  258. flightIdens.map(function (ident, key) {
  259. $(svg).children('svg').css("fill",colors[key]);
  260. var svgUrl= "data:image/svg+xml;utf8,"+$(svg).children('svg')[0].outerHTML;
  261. var img = new Image();
  262. img.src = svgUrl;
  263. planes[ident] = {color: colors[key], img: img};
  264. });
  265. });
  266. return planes;
  267. }
  268. //获取时间控件设置的参数
  269. function getControlOptions() {
  270. var startTime = $("#startTime").val();
  271. var endTime = $("#endTime").val();
  272. startTime = new Date(Date.parse(startTime.replace(/-/g, "/"))).getTime();
  273. endTime = new Date(Date.parse(endTime.replace(/-/g, "/"))).getTime();
  274. var speed = $("#speed").val();
  275. speed = (speed > 0) ? speed : 1000;
  276. speed = parseInt(speed);
  277. var frequency = $("#frequency").val();
  278. frequency = (frequency > 0) ? frequency : 1000;
  279. frequency = parseInt(frequency);
  280. return {
  281. startTime: startTime,
  282. endTime: endTime,
  283. speed: speed,
  284. frequency: frequency
  285. }
  286. }
  287. //获取ES服务查询参数
  288. function getServiceParamOptions() {
  289. var flightIds = $("#flightIds").val();
  290. return {
  291. flightIds: flightIds
  292. }
  293. }
  294. //更新当前时间界面
  295. function updateProgress(currentTime) {
  296. $("#progress").html(currentTime);
  297. }
  298. //默认设置参数
  299. function getDefaultControlOptions() {
  300. var startMs = 1498781767725;
  301. var endMs = 1498935332876;
  302. var start = moment(startMs).format("YYYY-MM-DDTHH:mm:ss") + "Z";
  303. var end = moment(endMs).format("YYYY-MM-DDTHH:mm:ss") + "Z";
  304. return {
  305. startTime: start,
  306. endTime: end,
  307. speed: 1000,
  308. frequency: 100,
  309. }
  310. }
  311. function initMap() {
  312. if (!map) {
  313. map = L.map('map', {
  314. crs: L.CRS.EPSG3857,
  315. center: [31.3677, 108.116],
  316. maxZoom: 20,
  317. minZoom: 3,
  318. zoom: 7
  319. });
  320. var attr = 'Data © <a href="https://www.elastic.co/products/elasticsearch" target="_blank">Elasticsearch</a> Map Data <span>© <a href="http://support.supermap.com.cn/product/iServer.aspx" target="_blank">SuperMap iServer</a></span>';
  321. var host = window.isLocal ? window.server : "https://iserver.supermap.io";
  322. var url = host + "/iserver/services/map-china400/rest/maps/ChinaDark";
  323. L.supermap.tiledMapLayer(url, {attribution: attr}).addTo(map);
  324. }
  325. initTimeControlView();
  326. }
  327. //初始化时间控制控件,仅UI
  328. function initTimeControlView() {
  329. var control = L.control({position: "topright"});
  330. control.onAdd = function () {
  331. var me = this;
  332. me._div = L.DomUtil.create('div', 'panel panel-primary controlPane');
  333. me._div.style.width = "300px";
  334. var titleDiv = $("<div class='panel-heading text-center' id='toggle' style='cursor: pointer'>" +
  335. "<span class='panel-title text-center'>" + resources.text_console + "</span>&nbsp;" +
  336. "<span class='glyphicon glyphicon-triangle-top' id='toggleIcon' ></span></div>").appendTo(me._div);
  337. var contentDiv = $("<div class='panel-body content center-block' style='font-size: 14px'></div>").appendTo(me._div);
  338. var optionsDiv = $("<div class='' id='options'></div>").appendTo(contentDiv);
  339. $("<div class='form-group'><label class='text-right' for='flightIds' >" +
  340. resources.text_flightNumber + "<span style='color:red'>*</span></label>" +
  341. "<select class='selectpicker ' id='flightIds' multiple " +
  342. "data-actions-box='true' " +
  343. "data-select-all-Text='" + resources.text_allSelect + "' " +
  344. "data-deselect-all-Text='" + resources.text_deSelectAll + "' " +
  345. "title='" + resources.text_select + "'>" +
  346. initSelectOpitons() +
  347. "</select></div><hr/>").appendTo(optionsDiv);
  348. function initSelectOpitons() {
  349. var str = '';
  350. flightIdens.map(function (ident, key) {
  351. str += "<option value=" + ident + " selected>" + ident + "</option> "
  352. });
  353. return str;
  354. }
  355. var defaultOption = getDefaultControlOptions();
  356. $("<div class='form-group form-inline'><label class='text-right' for='startTime' >" + resources.text_startTime + "<span style='color:red'>*</span></label>" +
  357. "<input id='startTime' type='text' class='form-control input-sm' placeholder='" + defaultOption.startTime +
  358. "' value='" + defaultOption.startTime + "'/></div></div>").appendTo(optionsDiv);
  359. $("<div class='form-group form-inline'><label class='text-right' for='endTime' >" + resources.text_finishTime + "<span style='color:red'>*</span></label>" +
  360. "<input id='endTime' type='text' class='form-control input-sm' placeholder='" + defaultOption.endTime +
  361. "' value='" + defaultOption.endTime + "'/></div></div>").appendTo(optionsDiv);
  362. $("<div class='form-group form-inline'><label class='text-right' for='speed' >" + resources.text_refreshStepSize + "(ms)</label>" +
  363. "<input id='speed' type='number' min='1' class='form-control input-sm' placeholder='" + defaultOption.speed +
  364. "' value='" + defaultOption.speed + "'/></div></div>").appendTo(optionsDiv);
  365. $("<div class='form-group form-inline'><label class='text-right' for='frequency' >" + resources.text_refreshFrequency + "(ms)</label>" +
  366. "<input id='frequency' type='number' min='1' class='form-control input-sm' placeholder='" + defaultOption.frequency +
  367. "' value='" + defaultOption.frequency + "'/></div></div>").appendTo(optionsDiv);
  368. var progressDiv = $("<div class='form-group'><div class='form-horizontal text-center'><div class='form-group'>" +
  369. "<label for='progress'>" + resources.text_currentTime + "</label><span class='form-control-static' id='progress'>" + resources.text_noStart + "</span>" +
  370. "</div></div></div>").appendTo(contentDiv);
  371. var controlDiv = $("<section><div class='form-inline text-center'>" +
  372. "<input id='start' type='button' class='btn btn-default text-center' value='" + resources.btn_start + "'/>&nbsp;" +
  373. "<input id='pause' type='button' class='btn btn-default text-center' value='" + resources.btn_pause + "'/>&nbsp;" +
  374. "<input id='stop' type='button' class='btn btn-default text-center' value='" + resources.btn_stop + "'/>" +
  375. "</div></section>").appendTo(contentDiv);
  376. me._div.addEventListener('mouseover', function () {
  377. me._map.dragging.disable();
  378. me._map.scrollWheelZoom.disable();
  379. me._map.doubleClickZoom.disable();
  380. });
  381. me._div.addEventListener('mouseout', function () {
  382. me._map.dragging.enable();
  383. me._map.scrollWheelZoom.enable();
  384. me._map.doubleClickZoom.enable();
  385. });
  386. return me._div;
  387. };
  388. control.addTo(map);
  389. var dateOptions = {
  390. format: "YYYY-MM-DD HH:mm:ss",
  391. stepping: 1,
  392. showClose: true,
  393. locale: 'zh-cn'
  394. };
  395. $("#startTime").datetimepicker(dateOptions);
  396. $("#endTime").datetimepicker(dateOptions);
  397. $('#flightIds').selectpicker();
  398. $("#start").on('click', function () {
  399. $("#options").slideUp("fast", function () {
  400. toggle(this);
  401. });
  402. start();
  403. });
  404. $("#pause").on('click', pause);
  405. $("#stop").on('click', stop);
  406. $("#toggle").on('click', function () {
  407. $("#options").slideToggle("fast", function () {
  408. toggle(this);
  409. });
  410. return false;
  411. });
  412. function toggle(ele) {
  413. if ($(ele).is(":visible")) {
  414. $("#toggleIcon").attr('class', "glyphicon glyphicon-triangle-top");
  415. } else {
  416. $("#toggleIcon").attr('class', "glyphicon glyphicon-triangle-bottom");
  417. }
  418. }
  419. }
  420. function clearAll() {
  421. if (timeControl) {
  422. timeControl.destroy();
  423. timeControl = null;
  424. }
  425. if (trackLineLayers) {
  426. for (var id in trackLineLayers) {
  427. map.removeLayer(trackLineLayers[id]);
  428. }
  429. trackLineLayers = null;
  430. }
  431. if (trackLineMap) {
  432. trackLineMap = null;
  433. }
  434. if (liveRenderer) {
  435. map.removeLayer(liveRenderer);
  436. liveRenderer = null;
  437. }
  438. if (liveDataSet) {
  439. liveDataSet = null;
  440. }
  441. if (planeLabels) {
  442. for (var item in planeLabels) {
  443. planeLabels[item].remove();
  444. }
  445. planeLabels = {};
  446. }
  447. }
  448. </script>
  449. </body>
  450. </html>