__init__.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import datetime as dt
  2. import json
  3. import math
  4. import os
  5. import pytz
  6. import subprocess
  7. import sys
  8. import yaml
  9. def to_iso6801_basic(ts):
  10. return ts.isoformat().replace(':', '').replace('-', '') \
  11. .replace('+0000', 'Z')
  12. class Location:
  13. EARTH_RADIUS_METRES = 6.371e6
  14. def __init__(self, latitude, longitude, altitude):
  15. self.latitude = latitude
  16. self.longitude = longitude
  17. self.altitude = altitude
  18. @property
  19. def latitude_radian(self):
  20. return math.radians(self.latitude)
  21. @property
  22. def longitude_radian(self):
  23. return math.radians(self.longitude)
  24. @staticmethod
  25. def haversine(angle_rad):
  26. # http://mathworld.wolfram.com/Haversine.html
  27. return math.sin(angle_rad / 2) ** 2
  28. @staticmethod
  29. def haversine_inv(hav):
  30. # return math.atan2(math.sqrt(hav), math.sqrt(1-hav)) * 2
  31. return math.asin(math.sqrt(hav)) * 2
  32. @staticmethod
  33. def haversine_distance_metres(a, b):
  34. # https://en.wikipedia.org/wiki/Haversine_formula
  35. hav = Location.haversine(b.latitude_radian - a.latitude_radian) \
  36. + math.cos(a.latitude_radian) * math.cos(b.latitude_radian) \
  37. * Location.haversine(b.longitude_radian - a.longitude_radian)
  38. return Location.haversine_inv(hav) * Location.EARTH_RADIUS_METRES
  39. class Position(Location):
  40. def __init__(self, timestamp, latitude, longitude, altitude):
  41. self.timestamp = timestamp
  42. super().__init__(
  43. latitude=latitude,
  44. longitude=longitude,
  45. altitude=altitude,
  46. )
  47. @classmethod
  48. def from_termux_location(cls, provider='network'):
  49. loc_json = subprocess.check_output(['termux-location', '-p', provider]) \
  50. .decode(sys.stdout.encoding)
  51. req_ts = dt.datetime.now(pytz.utc)
  52. loc_attr = yaml.load(loc_json)
  53. if not loc_attr:
  54. return None
  55. else:
  56. return cls(
  57. timestamp=req_ts -
  58. dt.timedelta(milliseconds=loc_attr['elapsedMs']),
  59. latitude=loc_attr['latitude'],
  60. longitude=loc_attr['longitude'],
  61. altitude=loc_attr['altitude'],
  62. )
  63. def save_yaml(self, target_dir_path):
  64. path = os.path.join(
  65. target_dir_path,
  66. '{}.yml'.format(to_iso6801_basic(self.timestamp)),
  67. )
  68. attr = {
  69. 'lat': self.latitude,
  70. 'lon': self.longitude,
  71. 'alt': self.altitude,
  72. }
  73. with open(path, 'w') as f:
  74. f.write(yaml.dump(attr, default_flow_style=False))
  75. return path