points2trips.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import json
  2. import gzip
  3. import random
  4. from datetime import datetime, timedelta
  5. import colorsys
  6. from math import radians, cos, sin, asin, sqrt
  7. from django.contrib.gis.geos.point import Point
  8. from ..models import Marker, Trip
  9. max_time_diff = timedelta(hours=6)
  10. max_distance = 3000 # m
  11. class TripConverter:
  12. trips: list[Trip]
  13. def __init__(self, markers: list[Marker]):
  14. self.trips = []
  15. first_index = 0
  16. for i, point in enumerate(markers[1:]):
  17. prev_point = markers[i-1]
  18. if point.timestamp - prev_point.timestamp > max_time_diff or \
  19. Distance(point.location, prev_point.location) > max_distance:
  20. if i - first_index > 2:
  21. self.trips.append(create_trip(markers[first_index:i]))
  22. first_index = i
  23. if first_index < len(markers) - 2:
  24. self.trips.append(create_trip(markers[first_index:]))
  25. def save(self):
  26. for trip in self.trips:
  27. trip.save()
  28. def create_trip(markers: list[Marker]) -> Trip:
  29. print(len(markers), markers[0].timestamp, markers[-1].timestamp)
  30. trip = Trip.objects.filter(startTime__lte=markers[-1].timestamp, endTime__gte=markers[0].timestamp).first()
  31. if not trip:
  32. trip = Trip.objects.create(
  33. startTime = markers[0].timestamp,
  34. endTime = markers[-1].timestamp,
  35. name = f"Trip {markers[0].timestamp}",
  36. color = get_path_color(markers[0].timestamp),
  37. )
  38. # elif trip.startTime == markers[0].timestamp and trip.endTime == markers[-1].timestamp:
  39. # data = json.loads(gzip.decompress(trip.path))
  40. # if len(data) == len(markers):
  41. # print("Trip already exists")
  42. # return trip
  43. trip.startTime = markers[0].timestamp
  44. trip.endTime = markers[-1].timestamp
  45. total_distance = 0 # m
  46. topSpeed = 0 # km/h
  47. ascendHeight = 0 # m
  48. descendHeight = 0 # m
  49. movementTime = timedelta(0)
  50. lastSpeed = 0
  51. i = 1
  52. while i < len(markers):
  53. point = markers[i]
  54. prev_point = markers[i-1]
  55. dist = Distance(point.location, prev_point.location)
  56. if point.speed is not None and point.speed > 0:
  57. speed = point.speed
  58. else:
  59. speed = dist / abs(point.timestamp - prev_point.timestamp).seconds * 3.6
  60. if abs(speed - lastSpeed) / abs(point.timestamp - prev_point.timestamp).seconds > 10: # m/s²
  61. markers.remove(point)
  62. continue
  63. if abs(speed - lastSpeed) > 50: # m/s
  64. markers.remove(point)
  65. continue
  66. total_distance += dist
  67. topSpeed = max(topSpeed, speed)
  68. if speed > 2.0: # km/h
  69. movementTime += abs(point.timestamp - prev_point.timestamp)
  70. if point.alt is not None and prev_point.alt is not None:
  71. if point.alt > prev_point.alt:
  72. ascendHeight += point.alt - prev_point.alt
  73. else:
  74. descendHeight += prev_point.alt - point.alt
  75. i += 1
  76. trip.distance = round(total_distance, 1) # m
  77. trip.topSpeed = round(topSpeed, 1) # km/h
  78. trip.avgSpeed = round(total_distance / (movementTime or trip.endTime - trip.startTime).total_seconds() * 3.6, 1) # km/h
  79. trip.ascendHeight = round(ascendHeight, 1) # m
  80. trip.descendHeight = round(descendHeight, 1) # m
  81. trip.movementTime = movementTime
  82. trip.path = points_to_blob(markers)
  83. return trip
  84. def Distance(point1: Point, point2: Point) -> float:
  85. lon1 = radians(point1.x)
  86. lon2 = radians(point2.x)
  87. lat1 = radians(point1.y)
  88. lat2 = radians(point2.y)
  89. # Haversine formula
  90. dlon = lon2 - lon1
  91. dlat = lat2 - lat1
  92. a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
  93. c = 2 * asin(sqrt(a))
  94. r = 6371000 # Radius of earth in meters. Use 3956 for miles
  95. return c * r
  96. def get_path_color(time: datetime) -> str:
  97. random.seed(int(time.timestamp()))
  98. hue = random.random()
  99. saturation = 0.5 + random.random() / 2
  100. value = 0.5 + random.random() / 2
  101. rgb = colorsys.hsv_to_rgb(hue, saturation, value)
  102. return f"#{int(rgb[0]*255):02x}{int(rgb[1]*255):02x}{int(rgb[2]*255):02x}"
  103. def convert_points_to_trips():
  104. points = list(Marker.objects.all())
  105. converter = TripConverter(points)
  106. converter.save()
  107. def points_to_blob(markers) -> bytes:
  108. arr = []
  109. for marker in markers:
  110. arr.append({
  111. "lat": marker.location.y,
  112. "lng": marker.location.x,
  113. "alt": marker.alt,
  114. "hdop": marker.hdop,
  115. "speed": marker.speed,
  116. "timestamp": marker.timestamp.timestamp(),
  117. })
  118. data = json.dumps(arr).encode('utf-8')
  119. return gzip.compress(data)