main.js 9.3 KB

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