tracks.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. class GPS {
  2. parse(trips) {
  3. this.trips = trips
  4. for(const trip of this.trips) {
  5. trip.totalTime = Math.abs(trip.endTime - trip.startTime)
  6. trip.minLat = trip.path.reduce((min, p) => p.lat < min ? p.lat : min, trip.path[0].lat)
  7. trip.maxLat = trip.path.reduce((max, p) => p.lat > max ? p.lat : max, trip.path[0].lat)
  8. trip.minLng = trip.path.reduce((min, p) => p.lng < min ? p.lng : min, trip.path[0].lng)
  9. trip.maxLng = trip.path.reduce((max, p) => p.lng > max ? p.lng : max, trip.path[0].lng)
  10. trip.center = trip.path[parseInt(trip.path.length/2)]
  11. }
  12. this.regions = this.getRegions()
  13. this.data = this.trips.map(t => t.path).reduce((a, b) => a.concat(b), [])
  14. }
  15. getRegions() {
  16. var trips = [];
  17. var s = 0;
  18. for(let i=0; i<=this.trips.length; i++) {
  19. while(i < this.trips.length - 2) {
  20. const cur = this.trips[i];
  21. const nxt = this.trips[i+1];
  22. if((nxt.startTime - cur.endTime) > 3600 * 24 * 7 || distance(cur.center, nxt.center) > 100000) {
  23. break;
  24. }
  25. i++;
  26. }
  27. let trip = this.trips.slice(s, i);
  28. let dist = trip.reduce((sum, p) => p.distance + sum, 0);
  29. if (dist > 30000) {
  30. trips.push({
  31. lat: median(trip.map(v => v.center.lat)),
  32. lng: median(trip.map(v => v.center.lng)),
  33. timestamp: median(trip.map(v => v.startTime)),
  34. distance: dist
  35. });
  36. }
  37. s = i;
  38. }
  39. return trips;
  40. }
  41. getInfo() {
  42. let i = {
  43. points: this.trips.reduce((sum, p) => p.path.length + sum, 0),
  44. distance: this.trips.reduce((sum, p) => p.distance + sum, 0),
  45. topSpeed: this.trips.reduce((max, p) => p.topSpeed > max ? p.topSpeed : max, 0),
  46. ascendHeight: this.trips.reduce((sum, p) => p.ascendHeight + sum, 0),
  47. descendHeight: this.trips.reduce((sum, p) => p.descendHeight + sum, 0),
  48. movementTime: this.trips.reduce((sum, p) => p.movementTime + sum, 0),
  49. totalTime: this.trips.reduce((sum, p) => p.totalTime + sum, 0)
  50. };
  51. i.avgSpeed = i.distance / i.movementTime * 3.6;
  52. return i;
  53. }
  54. }
  55. function distance (a, b) {
  56. // Convert degrees to radians
  57. var lat1 = a.lat * Math.PI / 180.0;
  58. var lon1 = a.lng * Math.PI / 180.0;
  59. var lat2 = b.lat * Math.PI / 180.0;
  60. var lon2 = b.lng * Math.PI / 180.0;
  61. // radius of earth in metres
  62. var r = 6378100;
  63. // P
  64. var rho1 = r * Math.cos(lat1);
  65. var z1 = r * Math.sin(lat1);
  66. var x1 = rho1 * Math.cos(lon1);
  67. var y1 = rho1 * Math.sin(lon1);
  68. // Q
  69. var rho2 = r * Math.cos(lat2);
  70. var z2 = r * Math.sin(lat2);
  71. var x2 = rho2 * Math.cos(lon2);
  72. var y2 = rho2 * Math.sin(lon2);
  73. // Dot product
  74. var dot = (x1 * x2 + y1 * y2 + z1 * z2);
  75. var cos_theta = dot / (r * r);
  76. var theta = Math.acos(cos_theta);
  77. // Distance in Metres
  78. return r * theta;
  79. }
  80. function timeFormat(sec, x = "time") {
  81. if(isNaN(sec))
  82. return "NaN";
  83. let d = new Date(sec*1000);
  84. let t = new Date(null,null,null,null,null,sec).toTimeString().match(/\d{2}:\d{2}:\d{2}/)[0];
  85. switch(x) {
  86. case "time":
  87. if(sec < 3600*24)
  88. return t;
  89. else
  90. return Math.floor(sec/3600/24) + "d " + t;
  91. case "datetime":
  92. return d.toLocaleDateString('de-DE', {
  93. year: "numeric",
  94. month: "2-digit",
  95. day: "2-digit",
  96. hour: "2-digit",
  97. minute: "2-digit",
  98. second: "2-digit",
  99. });
  100. case "date":
  101. return d.toLocaleDateString('de-DE', {
  102. year: "numeric",
  103. month: "2-digit",
  104. day: "2-digit"
  105. });
  106. case "short":
  107. return d.toLocaleDateString('de-DE', {
  108. year: "numeric",
  109. month: "2-digit"
  110. });
  111. }
  112. }
  113. function median(values) {
  114. values.sort( function(a,b) {return a - b;} );
  115. var half = Math.floor(values.length/2);
  116. return values[half];
  117. }
  118. function avg(v) {
  119. return v.reduce((a,b) => a+b, 0)/v.length;
  120. }
  121. function smoothOut(vector, variance) {
  122. var t_avg = avg(vector)*variance;
  123. var ret = Array(vector.length);
  124. for (var i = 0; i < vector.length; i++) {
  125. (function () {
  126. var prev = i>0 ? ret[i-1] : vector[i];
  127. var next = i<vector.length ? vector[i] : vector[i-1];
  128. ret[i] = avg([t_avg, avg([prev, vector[i], next])]);
  129. })();
  130. }
  131. return ret;
  132. }