import datetime as dt import json import math import os import pytz import subprocess import sys import yaml def to_iso6801_basic(ts): return ts.isoformat().replace(':', '').replace('-', '') \ .replace('+0000', 'Z') class Location: EARTH_RADIUS_METRES = 6.371e6 def __init__(self, latitude, longitude, altitude): self.latitude = latitude self.longitude = longitude self.altitude = altitude @property def latitude_radian(self): return math.radians(self.latitude) @property def longitude_radian(self): return math.radians(self.longitude) @staticmethod def haversine(angle_rad): # http://mathworld.wolfram.com/Haversine.html return math.sin(angle_rad / 2) ** 2 @staticmethod def haversine_inv(hav): # return math.atan2(math.sqrt(hav), math.sqrt(1-hav)) * 2 return math.asin(math.sqrt(hav)) * 2 @staticmethod def haversine_distance_metres(a, b): # https://en.wikipedia.org/wiki/Haversine_formula hav = Location.haversine(b.latitude_radian - a.latitude_radian) \ + math.cos(a.latitude_radian) * math.cos(b.latitude_radian) \ * Location.haversine(b.longitude_radian - a.longitude_radian) return Location.haversine_inv(hav) * Location.EARTH_RADIUS_METRES class Position(Location): def __init__(self, timestamp, latitude, longitude, altitude): self.timestamp = timestamp super().__init__( latitude=latitude, longitude=longitude, altitude=altitude, ) @classmethod def from_termux_location(cls, provider='network'): loc_json = subprocess.check_output(['termux-location', '-p', provider]) \ .decode(sys.stdout.encoding) req_ts = dt.datetime.now(pytz.utc) loc_attr = yaml.load(loc_json) if not loc_attr: return None else: return cls( timestamp=req_ts - dt.timedelta(milliseconds=loc_attr['elapsedMs']), latitude=loc_attr['latitude'], longitude=loc_attr['longitude'], altitude=loc_attr['altitude'], ) @classmethod def load_yaml(cls, path): name = os.path.basename(path) assert name.endswith('Z.yml'), path ts = pytz.utc.localize(dt.datetime.strptime( name[:-5], '%Y%m%dT%H%M%S.%f', )) with open(path, 'r') as f: attr = yaml.load(f.read()) return cls( timestamp=ts, latitude=attr['lat'], longitude=attr['lon'], altitude=attr['alt'], ) def save_yaml(self, target_dir_path): path = os.path.join( target_dir_path, '{}.yml'.format(to_iso6801_basic(self.timestamp)), ) attr = { 'lat': self.latitude, 'lon': self.longitude, 'alt': self.altitude, } with open(path, 'w') as f: f.write(yaml.dump(attr, default_flow_style=False)) return path