ESHeatMap.html 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  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_ESHeatMap"></title>
  9. <script type="text/javascript" include="jquery,bootstrap,moment,bootstrap-datetimepicker,bootstrap-select,geohash"
  10. src="../js/include-web.js"></script>
  11. <style>
  12. .bootstrap-select {
  13. width: 168px !important;
  14. }
  15. .form-group label {
  16. width: 100px;
  17. padding-right: 5px;
  18. }
  19. .tips {
  20. z-index: 1000;
  21. }
  22. </style>
  23. </head>
  24. <body style=" margin: 0;overflow: hidden;background: #fff;width: 100%;height:100%;position: absolute;top: 0;">
  25. <div id="map" style="margin:0 auto;width: 100%;height: 100%;"></div>
  26. <script type="text/javascript" include="mapv,elasticsearch" src="../../dist/leaflet/include-leaflet.js"></script>
  27. <script type="text/javascript">
  28. var map,
  29. stopRender,
  30. timeControl,
  31. liveESService,
  32. liveRenderer,
  33. liveDataSet,
  34. geoFenceLayer,
  35. dataUrl = "https://iclient.supermap.io/es",
  36. info = L.control({position: 'bottomleft'}),
  37. layerOptions = getGridOptions();
  38. init();
  39. function init() {
  40. initMap();
  41. info.onAdd = function () {
  42. var popup = L.DomUtil.create('div');
  43. popup.innerHTML = "<div class='btn-group' role='group' aria-label='...'>" +
  44. "<button value='grid' type='button' class='btn btn-default'>"+resources.btn_grid+"</button>" +
  45. "<button value='heatmap' type='button' class='btn btn-default'>"+resources.title_heatMap+"</button></div>"
  46. handleMapEvent(popup, map);
  47. return popup;
  48. };
  49. info.addTo(map);
  50. liveESService = new SuperMap.ElasticSearch(dataUrl);
  51. }
  52. function handleMapEvent(div, map) {
  53. if (!div || !map) {
  54. return;
  55. }
  56. div.addEventListener('mouseover', function () {
  57. map.dragging.disable();
  58. map.scrollWheelZoom.disable();
  59. map.doubleClickZoom.disable();
  60. });
  61. div.addEventListener('mouseout', function () {
  62. map.dragging.enable();
  63. map.scrollWheelZoom.enable();
  64. map.doubleClickZoom.enable();
  65. });
  66. }
  67. //开始播放
  68. function start() {
  69. var options = getControlOptions();
  70. if (!timeControl) {
  71. timeControl = new SuperMap.TimeFlowControl(loadLiveData, options);
  72. } else {
  73. timeControl.updateOptions(options);
  74. }
  75. timeControl.start();
  76. stopRender = false;
  77. }
  78. //暂停播放
  79. function pause() {
  80. timeControl && timeControl.pause();
  81. }
  82. //停止播放
  83. function stop() {
  84. stopRender = true;
  85. timeControl && timeControl.stop();
  86. clearAll();
  87. }
  88. //时间控制器回调参数,即每次刷新时执行的操作,此处为向服务器请求数据并绘制。实时刷新执行。
  89. function loadLiveData(currentTime) {
  90. getESAggregations(map.getZoom(), map.getBounds(), currentTime, currentTime + getControlOptions().speed);
  91. updateProgress(moment(currentTime).format("YYYY-MM-DD HH:mm:ss"));
  92. }
  93. function wrapLatLngBounds(extent) {
  94. var left = extent.getNorthWest().lng < -180 ? -180 : extent.getNorthWest().lng;
  95. var bottom = extent.getSouthEast().lat < -90 ? -90 : extent.getSouthEast().lat;
  96. var right = extent.getSouthEast().lng > 180 ? 180 : extent.getSouthEast().lng;
  97. var top = extent.getNorthWest().lat > 90 ? 90 : extent.getNorthWest().lat;
  98. return L.latLngBounds(L.latLng(bottom, left), L.latLng(top, right));
  99. }
  100. function getESAggregations(level, extent, startT, endT) {
  101. if (startT > endT) {
  102. return;
  103. }
  104. var coords = wrapLatLngBounds(extent), prec;
  105. if (level <= 2) {
  106. prec = 2;
  107. } else if (level > 2 && level <= 5) {
  108. prec = 4;
  109. } else if (level > 5) {
  110. prec = 8;
  111. } else if (level > 8 && level <= 11) {
  112. prec = 9;
  113. } else if (level > 11 && level <= 13) {
  114. prec = 10;
  115. } else if (level > 13 && level <= 15) {
  116. prec = 11;
  117. } else if (level > 15) {
  118. prec = 12;
  119. }
  120. liveESService.search({
  121. index: "flights",
  122. type: "flight_utc",
  123. body: {
  124. query: {
  125. constant_score: {
  126. filter: {
  127. range: {
  128. 'time-ms': {
  129. from: startT,
  130. to: endT
  131. }
  132. }
  133. }
  134. }
  135. },
  136. aggregations: {
  137. zoomedInView: {
  138. filter: {
  139. geo_bounding_box: {
  140. 'pin.location': {
  141. top_left: {
  142. lat: coords.getNorthWest().lat,
  143. lon: coords.getNorthWest().lng
  144. },
  145. bottom_right: {
  146. lat: coords.getSouthEast().lat,
  147. lon: coords.getSouthEast().lng
  148. }
  149. }
  150. }
  151. },
  152. aggregations: {
  153. geohash: {
  154. geohash_grid: {
  155. field: "pin.location",
  156. precision: prec
  157. }
  158. }
  159. }
  160. }
  161. }
  162. }
  163. }).then(function (response) {
  164. if (response.error) {
  165. console.log(error);
  166. console.log(error.body);
  167. return;
  168. }
  169. !stopRender && renderLive(response.aggregations.zoomedInView.geohash.buckets);
  170. });
  171. }
  172. //渲染实时点数据
  173. function renderLive(result) {
  174. if (timeControl && !timeControl.getRunning()) {
  175. return;
  176. }
  177. result = result || {};
  178. var data = createLiveRendererData(result);
  179. if (data.length < 1) {
  180. return;
  181. }
  182. updateDataSet(data);
  183. if (!liveRenderer) {
  184. liveRenderer = L.supermap.mapVLayer(liveDataSet, layerOptions, {noWrap: true}).addTo(map);
  185. } else {
  186. liveRenderer.update({data: liveDataSet, options: layerOptions});
  187. }
  188. }
  189. var buttons = $('.btn-group').children();
  190. buttons.map(function (key) {
  191. var value = buttons[key].value;
  192. if (value === 'grid') {
  193. $(buttons[key]).on('click', function () {
  194. layerOptions = getGridOptions();
  195. if (liveDataSet) {
  196. liveRenderer.update({data: liveDataSet, options: layerOptions});
  197. }
  198. });
  199. return;
  200. }
  201. if (value === 'heatmap') {
  202. $(buttons[key]).on('click', function () {
  203. layerOptions = getHeatMapOptions();
  204. if (liveDataSet) {
  205. liveRenderer.update({data: liveDataSet, options: layerOptions});
  206. }
  207. });
  208. }
  209. });
  210. function getGridOptions() {
  211. return {
  212. fillStyle: 'rgba(55, 50, 250, 0.8)',
  213. shadowColor: 'rgba(255, 250, 50, 1)',
  214. shadowBlur: 10,
  215. size: 40,
  216. globalAlpha: 0.5,
  217. label: {
  218. show: true,
  219. fillStyle: 'white',
  220. shadowColor: 'yellow',
  221. font: '15px Arial',
  222. shadowBlur: 10
  223. },
  224. gradient: {
  225. 0: "rgba(49, 54, 149, 0)",
  226. 0.2: "rgba(69,117,180, 0.7)",
  227. 0.3: "rgba(116,173,209, 0.7)",
  228. 0.4: "rgba(171,217,233, 0.7)",
  229. 0.5: "rgba(224,243,248, 0.7)",
  230. 0.6: "rgba(254,224,144,0.7)",
  231. 0.7: "rgba(253,174,97,0.7)",
  232. 0.8: "rgba(244,109,67,0.8)",
  233. 0.9: "rgba(215,48,39,0.8)",
  234. 0.95: "rgba(165, 0, 38,0.8)"
  235. },
  236. draw: 'grid'
  237. }
  238. }
  239. function getHeatMapOptions() {
  240. return {
  241. size: 20,
  242. gradient: {
  243. 0: "rgba(49, 54, 149, 0)",
  244. 0.2: "rgba(69,117,180, 0.7)",
  245. 0.3: "rgba(116,173,209, 0.7)",
  246. 0.4: "rgba(171,217,233, 0.7)",
  247. 0.5: "rgba(224,243,248, 0.7)",
  248. 0.6: "rgba(254,224,144,0.7)",
  249. 0.7: "rgba(253,174,97,0.7)",
  250. 0.8: "rgba(244,109,67,0.8)",
  251. 0.9: "rgba(215,48,39,0.8)",
  252. 0.95: "rgba(165, 0, 38,0.8)"
  253. },
  254. draw: 'heatmap'
  255. }
  256. }
  257. //解析点查询结果数据为mapv数据
  258. function createLiveRendererData(results) {
  259. var data = [];
  260. results.map(function (feature) {
  261. var coords = decodeGeoHash(feature.key);
  262. data.push({
  263. geometry: {
  264. type: 'Point',
  265. coordinates: [coords.longitude[2], coords.latitude[2]]
  266. },
  267. count: feature.doc_count
  268. });
  269. });
  270. return data;
  271. }
  272. //更新点数据集
  273. function updateDataSet(data) {
  274. if (!liveDataSet) {
  275. liveDataSet = new mapv.DataSet(data);
  276. return;
  277. }
  278. var innerData = liveDataSet.get();
  279. var dataLen = data.length;
  280. for (var i = 0; i < innerData.length; i++) {
  281. if (i < dataLen && data[i].ident === innerData[i].ident) {
  282. innerData[i] = data[i];
  283. }
  284. }
  285. liveDataSet.set(innerData);
  286. }
  287. //获取时间控件设置的参数
  288. function getControlOptions() {
  289. var startTime = $("#startTime").val();
  290. var endTime = $("#endTime").val();
  291. startTime = new Date(Date.parse(startTime.replace(/-/g, "/"))).getTime();
  292. endTime = new Date(Date.parse(endTime.replace(/-/g, "/"))).getTime();
  293. var speed = $("#speed").val();
  294. speed = (speed > 0) ? speed : 1000;
  295. speed = parseInt(speed);
  296. var frequency = $("#frequency").val();
  297. frequency = (frequency > 0) ? frequency : 1000;
  298. frequency = parseInt(frequency);
  299. return {
  300. startTime: startTime,
  301. endTime: endTime,
  302. speed: speed,
  303. frequency: frequency
  304. }
  305. }
  306. //更新当前时间界面
  307. function updateProgress(currentTime) {
  308. $("#progress").html(currentTime);
  309. }
  310. //默认设置参数
  311. function getDefaultControlOptions() {
  312. var startMs = 1498772645774;
  313. var endMs = 1498935332879;
  314. var start = moment(startMs).format("YYYY-MM-DD HH:mm:ss");
  315. var end = moment(endMs).format("YYYY-MM-DD HH:mm:ss");
  316. return {
  317. startTime: start,
  318. endTime: end,
  319. speed: 900000,
  320. frequency: 1000
  321. }
  322. }
  323. function initMap() {
  324. if (!map) {
  325. map = L.map('map', {
  326. center: [40.745654, -90.931577],
  327. maxZoom: 15,
  328. minZoom: 1,
  329. zoom: 4,
  330. crs: L.CRS.EPSG3857
  331. });
  332. 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>';
  333. var host = window.isLocal ? window.server : "https://iserver.supermap.io";
  334. var url = host + "/iserver/services/map-china400/rest/maps/ChinaDark";
  335. L.supermap.tiledMapLayer(url, {attribution: attr}).addTo(map);
  336. }
  337. initTimeControlView();
  338. }
  339. //初始化时间控制控件,仅UI
  340. function initTimeControlView() {
  341. var control = L.control({position: "topright"});
  342. control.onAdd = function () {
  343. var me = this;
  344. me._div = L.DomUtil.create('div', 'panel panel-primary controlPane');
  345. me._div.style.width = "300px";
  346. var titleDiv = $("<div class='panel-heading text-center' id='toggle' style='cursor: pointer'>" +
  347. "<span class='panel-title text-center'>"+resources.text_console+"</span>&nbsp;" +
  348. "<span class='glyphicon glyphicon-triangle-top' id='toggleIcon' ></span></div>").appendTo(me._div);
  349. var contentDiv = $("<div class='panel-body content center-block' style='font-size: 14px'></div>").appendTo(me._div);
  350. var optionsDiv = $("<div class='' id='options'></div>").appendTo(contentDiv);
  351. var defaultOption = getDefaultControlOptions();
  352. $("<div class='form-group form-inline'><label class='text-right' for='startTime' >"+resources.text_startTime+": "+"</label>" +
  353. "<input id='startTime' type='text' class='form-control input-sm' placeholder='" + defaultOption.startTime +
  354. "' value='" + defaultOption.startTime + "'/></div></div>").appendTo(optionsDiv);
  355. $("<div class='form-group form-inline'><label class='text-right' for='endTime' >"+resources.text_finishTime+": "+"</label>" +
  356. "<input id='endTime' type='text' class='form-control input-sm' placeholder='" + defaultOption.endTime +
  357. "' value='" + defaultOption.endTime + "'/></div></div>").appendTo(optionsDiv);
  358. $("<div class='form-group form-inline'><label class='text-right' for='speed' >"+resources.text_refreshStepSize+": "+"</label>" +
  359. "<input id='speed' type='number' min='1' class='form-control input-sm' placeholder='" + defaultOption.speed +
  360. "' value='" + defaultOption.speed + "'/></div></div>").appendTo(optionsDiv);
  361. $("<div class='form-group form-inline'><label class='text-right' for='frequency' >"+resources.text_refreshFrequency+": "+"</label>" +
  362. "<input id='frequency' type='number' min='1' class='form-control input-sm' placeholder='" + defaultOption.frequency +
  363. "' value='" + defaultOption.frequency + "'/></div></div>").appendTo(optionsDiv);
  364. var progressDiv = $("<div class='form-group'><div class='form-horizontal text-center'><div class='form-group'>" +
  365. "<label for='progress'>"+resources.text_currentTime+"</label><span class='form-control-static' id='progress'>"+resources.text_noStart+"</span>" +
  366. "</div></div></div>").appendTo(contentDiv);
  367. var controlDiv = $("<section><div class='form-inline text-center'>" +
  368. "<input id='start' type='button' class='btn btn-default text-center' value="+resources.btn_start+">&nbsp;" +
  369. "<input id='pause' type='button' class='btn btn-default text-center' value="+resources.btn_pause+">&nbsp;" +
  370. "<input id='stop' type='button' class='btn btn-default text-center' value="+resources.btn_stop+">" +
  371. "</div></section>").appendTo(contentDiv);
  372. me._div.addEventListener('mouseover', function () {
  373. me._map.dragging.disable();
  374. me._map.scrollWheelZoom.disable();
  375. me._map.doubleClickZoom.disable();
  376. });
  377. me._div.addEventListener('mouseout', function () {
  378. me._map.dragging.enable();
  379. me._map.scrollWheelZoom.enable();
  380. me._map.doubleClickZoom.enable();
  381. });
  382. return me._div;
  383. };
  384. control.addTo(map);
  385. var dateOptions = {
  386. format: "YYYY-MM-DD HH:mm:ss",
  387. stepping: 1,
  388. showClose: true,
  389. locale: 'zh-cn'
  390. };
  391. $("#startTime").datetimepicker(dateOptions);
  392. $("#endTime").datetimepicker(dateOptions);
  393. $("#start").on('click', function () {
  394. $("#options").slideUp("fast", function () {
  395. toggle(this);
  396. });
  397. start();
  398. });
  399. $("#pause").on('click', pause);
  400. $("#stop").on('click', stop);
  401. $("#toggle").on('click', function () {
  402. $("#options").slideToggle("fast", function () {
  403. toggle(this);
  404. });
  405. return false;
  406. });
  407. function toggle(ele) {
  408. if ($(ele).is(":visible")) {
  409. $("#toggleIcon").attr('class', "glyphicon glyphicon-triangle-top");
  410. } else {
  411. $("#toggleIcon").attr('class', "glyphicon glyphicon-triangle-bottom");
  412. }
  413. }
  414. }
  415. function clearAll() {
  416. if (timeControl) {
  417. timeControl.destroy();
  418. timeControl = null;
  419. }
  420. if (liveRenderer) {
  421. map.removeLayer(liveRenderer);
  422. liveRenderer = null;
  423. }
  424. if (liveDataSet) {
  425. liveDataSet = null;
  426. }
  427. if (geoFenceLayer) {
  428. geoFenceLayer.remove();
  429. geoFenceLayer = null;
  430. }
  431. }
  432. </script>
  433. </body>
  434. </html>