main.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. const selColor = "#FF5C26";
  2. let d = [{
  3. label:"Altitude,m",
  4. data:[],
  5. yaxis: 2
  6. },
  7. {
  8. label:"Speed,km/h",
  9. data:[]
  10. }];
  11. let options = {
  12. xaxis: {
  13. mode: "time",
  14. tickLength: 5,
  15. zoomRange: [60 * 1000, 1000 * 3600 * 24 * 365]
  16. },
  17. /*selection: {
  18. mode: "x",
  19. color: selColor
  20. },*/
  21. yaxes: [{
  22. min: 0,
  23. zoomRange: false,
  24. panRange: false
  25. }, {
  26. position: "right",
  27. alignTicksWithAxis: 1,
  28. min: 0,
  29. zoomRange: false,
  30. panRange: false
  31. }],
  32. zoom: {
  33. interactive: true
  34. },
  35. pan: {
  36. interactive: true,
  37. cursor: "move",
  38. frameRate: 60
  39. }
  40. };
  41. let optO = {
  42. legend : {
  43. show: false
  44. },
  45. series: {
  46. lines: {
  47. show: true,
  48. lineWidth: 1
  49. },
  50. shadowSize: 0
  51. },
  52. xaxis: {
  53. //ticks: [],
  54. mode: "time"
  55. },
  56. yaxis: {
  57. ticks: [],
  58. autoscaleMargin: 0.1
  59. },
  60. selection: {
  61. mode: "x",
  62. color: selColor,
  63. minSize: 0
  64. }
  65. };
  66. let gps = new GPS();
  67. let plot, overview;
  68. window.onload = function() {
  69. Cesium.Ion.defaultAccessToken = '<token>';
  70. var extent = {west: -0.2540382220862719, south: 0.6872565916005104, east: 0.6129855406042352, north: 0.9377043806513488};
  71. Cesium.Camera.DEFAULT_VIEW_RECTANGLE = extent;
  72. Cesium.Camera.DEFAULT_VIEW_FACTOR = 0;
  73. bingMapsProvider = new Cesium.BingMapsImageryProvider({
  74. url: 'https://dev.virtualearth.net',
  75. key: '<key>',
  76. mapStyle: Cesium.BingMapsStyle.AERIAL_WITH_LABELS
  77. });
  78. viewer = new Cesium.Viewer('map', {
  79. fullscreenElement: "map",
  80. terrainProvider : Cesium.createWorldTerrain({
  81. requestVertexNormals: true,
  82. requestWaterMask: true
  83. }),
  84. //shadows: true
  85. });
  86. viewer.scene.globe.enableLighting = true;
  87. viewer.resolutionScale = 1.2;
  88. viewer.scene.screenSpaceCameraController.enableTilt = !('ontouchstart' in window);
  89. viewer.baseLayerPicker.viewModel.selectedImagery = new Cesium.ProviderViewModel({
  90. name: 'Bing Maps Aerial with Labels',
  91. iconUrl: Cesium.buildModuleUrl('Widgets/Images/ImageryProviders/bingAerialLabels.png'),
  92. tooltip: 'Bing Maps aerial imagery with labels',
  93. creationFunction: function () {
  94. return bingMapsProvider
  95. }
  96. });
  97. var paths = [];
  98. fetch("trips")
  99. .then(function(response) {
  100. return response.json();
  101. })
  102. .then(function(jsonResponse) {
  103. gps.parse(jsonResponse);
  104. for(const t of gps.trips) {
  105. var desc = "Start: <b>" + timeFormat(t.startTime, "datetime") +
  106. "</b><br/>Finish: <b>" + timeFormat(t.endTime, "datetime") + "</b><br/>" +
  107. "Track time (full): "+ timeFormat(t.totalTime) + "<br>" +
  108. "Track time (mov.): "+ timeFormat(t.movementTime) + "<br>" +
  109. "Alt: &uarr;" + t.ascendHeight.toFixed(1) + "m &darr;" + t.descendHeight.toFixed(1) + "m"+
  110. "<br/>Distance: "+(t.distance/1000).toFixed(1)+"km<br/>" +
  111. "top speed: "+t.topSpeed.toFixed(1)+
  112. "km/h<br/>"+
  113. "average speed: "+t.avgSpeed.toFixed(1)+"km/h<br/>";
  114. const col = Cesium.Color.fromCssColorString(t.color)
  115. var path = viewer.entities.add({
  116. label : "Track "+t.id,
  117. id: "track_"+t.id,
  118. description: desc,
  119. polyline : {
  120. positions : t.path.map(p => Cesium.Cartesian3.fromDegrees(p.lng, p.lat, p.alt)),
  121. width : 5,
  122. material : new Cesium.PolylineOutlineMaterialProperty({
  123. color : col,
  124. }),
  125. //clampToGround: true,
  126. depthFailMaterial: new Cesium.PolylineOutlineMaterialProperty({
  127. color : Cesium.Color.fromAlpha(col, 0.6),
  128. }),
  129. shadows: Cesium.ShadowMode.ENABLED
  130. }
  131. });
  132. paths.push(path);
  133. }
  134. var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  135. handler.setInputAction(function (movement) {
  136. var pick = viewer.scene.pick(movement.position);
  137. if (Cesium.defined(pick) && (pick.id._id.match(/track_([0-9]+)/))) {
  138. var id = parseInt(RegExp.$1);
  139. var path = paths[id];
  140. show(id);
  141. }
  142. }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
  143. viewer.flyTo(paths[paths.length - 1], { offset: new Cesium.HeadingPitchRange(0, -90, 0)});
  144. let regionIndex = 0;
  145. for(let r=0; r<gps.regions.length; r++) {
  146. addInfo(bingMapsProvider, viewer, regionIndex, gps.regions[r]);
  147. regionIndex++;
  148. }
  149. const i = gps.getInfo();
  150. var html =
  151. "Time (full): "+ timeFormat(i.totalTime) + "<br>" +
  152. "Time (mov.): "+ timeFormat(i.movementTime) + "<br>" +
  153. "Alt: &uarr;" + (i.ascendHeight).toFixed(1) + "m &darr;" + (i.descendHeight).toFixed(1) + "m<br>"+
  154. "Distance: <b>"+(i.distance/1000).toFixed(1)+"km</b><br/>" +
  155. "Top speed: <b>"+i.topSpeed.toFixed(1) + "km/h</b><br/>"+
  156. "Avg. speed: "+i.avgSpeed.toFixed(1)+"km/h<br/>"+
  157. "Data points: "+i.points+"<br/>"+
  158. "<a href='javascript:show(" + (gps.trips.length-1) + ")'>Graph</a>";
  159. $(".status").append(html);
  160. });
  161. plot = $.plot("#placeholder", d, options);
  162. overview = $.plot("#overview", d, optO);
  163. // now connect the two
  164. $("#placeholder").bind("plotpan", updateOverview);
  165. $("#placeholder").bind("plotzoom", updateOverview);
  166. function updateOverview(event, ranges) {
  167. var axes = plot.getAxes();
  168. for(var axis in axes) {
  169. axes[axis].from = axes[axis].min;
  170. axes[axis].to = axes[axis].max;
  171. }
  172. overview.setSelection(axes, true);
  173. }
  174. $("#overview").bind("plotselected", function (event, ranges) {
  175. $.each(plot.getXAxes(), function(_, axis) {
  176. var opts = axis.options;
  177. opts.min = ranges.xaxis.from;
  178. opts.max = ranges.xaxis.to;
  179. });
  180. plot.setupGrid();
  181. plot.draw();
  182. });
  183. $("#whole").click(function () {
  184. setZoom(false,false);
  185. });
  186. $("#right").click(function () {
  187. var min = plot.getXAxes()[0].options.min;
  188. var max = plot.getXAxes()[0].options.max;
  189. if(min != null && max != null)
  190. setZoom((min+max)/2, max * 1.5 - min/2);
  191. });
  192. $("#left").click(function () {
  193. var min = plot.getXAxes()[0].options.min;
  194. var max = plot.getXAxes()[0].options.max;
  195. if(min != null && max != null)
  196. setZoom(min * 1.5 - max/2, (min+max)/2);
  197. });
  198. }
  199. let firstShow = true;
  200. function show(id) {
  201. if(firstShow) {
  202. firstShow = false;
  203. d[0].data = smoothOut(gps.data.map(v => v.alt), 0.85);
  204. d[1].data = smoothOut(gps.data.map(v => v.speed), 0.85);
  205. for(let i=0; i<gps.data.length; i++) {
  206. const cur = gps.data[i];
  207. if((cur.timeDiff > 60 && cur.speed < 0.5) || cur.speed === null) {
  208. d[0].data[i] = null;
  209. d[1].data[i] = null;
  210. }
  211. d[0].data[i] = [cur.timestamp*1000, d[0].data[i]];
  212. d[1].data[i] = [cur.timestamp*1000, d[1].data[i]];
  213. }
  214. let opts = plot.getXAxes()[0].options;
  215. opts.panRange = [
  216. gps.data[0].timestamp*1000,
  217. gps.data[gps.data.length-1].timestamp*1000
  218. ];
  219. plot.setData(d);
  220. plot.setupGrid(); //only necessary if your new data will change the axes or grid
  221. plot.draw();
  222. overview.setData(d);
  223. overview.setupGrid(); //only necessary if your new data will change the axes or grid
  224. overview.draw();
  225. for(let t of gps.trips) {
  226. $("#tracks").append("<li><input type='button' onclick='setZoom("+t.startTime*1000+","+t.endTime*1000+")' value='" + timeFormat(t.startTime, "date") + " " + (t.distance/1000).toFixed(1) + "km'</li>");
  227. }
  228. }
  229. $("#shadow").css("visibility", "visible");
  230. $("#frame").css("visibility", "visible");
  231. console.log(id);
  232. let start = gps.trips[id].startTime*1000;
  233. let end = gps.trips[id].endTime*1000;
  234. setZoom(start,end);
  235. $("#track").click(function () {
  236. setZoom(start,end);
  237. });
  238. };
  239. function hide() {
  240. $(".popup").css("visibility", "hidden");
  241. };
  242. function setZoom(Xmin, Xmax) {
  243. var opts = plot.getXAxes()[0].options;
  244. if(Xmax==false) {
  245. Xmax = +new Date() - 60 * new Date().getTimezoneOffset() * 1000;
  246. }
  247. if(Xmin==false) {
  248. Xmin = d[0].data[0][0];
  249. overview.clearSelection();
  250. } else {
  251. overview.setSelection({ xaxis: { from: Xmin, to: Xmax}});
  252. }
  253. opts.min=Xmin;
  254. opts.max=Xmax;
  255. plot.setupGrid();
  256. plot.draw();
  257. plot.clearSelection();
  258. return false;
  259. }
  260. function addInfo(bingMapsProvider, viewer, id, data) {
  261. $('.sidebar').append("<div class='info' id='info_" + id + "'>" +
  262. timeFormat(data.timestamp, "short") + "<br>" +
  263. data.name + "<br><b>" +
  264. (data.distance/1000).toFixed(1) + " km</b></div>");
  265. $('#info_'+id).click(function() {
  266. viewer.camera.flyTo({
  267. destination: Cesium.Cartesian3.fromDegrees(data.lng, data.lat, data.distance),
  268. offset: new Cesium.HeadingPitchRange(0, -90, 0)
  269. });
  270. });
  271. }