-
Notifications
You must be signed in to change notification settings - Fork 17
/
utils.py
177 lines (145 loc) · 5.73 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
from typing import *
import logging
import math
import bing
from planedb import *
from datetime import datetime, timedelta
def deg2rad(deg: float) -> float:
"""Convert degrees to radians
Arguments:
deg {float} -- Angle in degrees
Returns:
float -- Angle in radians
"""
return deg * (math.pi/180)
def bearing(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
"""Calculate bearing from lat1/lon2 to lat2/lon2
Arguments:
lat1 {float} -- Start latitude
lon1 {float} -- Start longitude
lat2 {float} -- End latitude
lon2 {float} -- End longitude
Returns:
float -- bearing in degrees
"""
rlat1 = math.radians(lat1)
rlat2 = math.radians(lat2)
rlon1 = math.radians(lon1)
rlon2 = math.radians(lon2)
dlon = math.radians(lon2-lon1)
b = math.atan2(math.sin(dlon)*math.cos(rlat2),math.cos(rlat1)*math.sin(rlat2)-math.sin(rlat1)*math.cos(rlat2)*math.cos(dlon)) # bearing calc
bd = math.degrees(b)
br,bn = divmod(bd+360,360) # the bearing remainder and final bearing
return bn
def coordinate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
"""Calculate distance in meters between the two coordinates
Arguments:
lat1 {float} -- Start latitude
lon1 {float} -- Start longitude
lat2 {float} -- End latitude
lon2 {float} -- End longitude
Returns:
float -- Distance in meters
"""
R = 6371 # Radius of the earth in km
dLat = deg2rad(lat2-lat1)
dLon = deg2rad(lon2-lon1)
a = math.sin(dLat/2) * math.sin(dLat/2) + math.cos(deg2rad(lat1)) * math.cos(deg2rad(lat2)) * math.sin(dLon/2) * math.sin(dLon/2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
d = R * c * 1000 # Distance in m
return d
def calc_travel(lat: float, lon: float, utc_start: datetime, speed_kts: float, heading: float) -> Tuple[float, float]:
"""Calculate travel from lat, lon starting at a certain time with given speed and heading
Arguments:
lat {float} -- Starting latitude
lon {float} -- Starting longitude
utc_start {datetime} -- Start time
speed_kts {float} -- Speed in knots
heading {float} -- Heading in degress
Returns:
Tuple[float, float] -- The new lat/lon as a tuple
"""
age = datetime.utcnow() - utc_start
age_s = age.total_seconds()
R = 6378.1 # Radius of the Earth
brng = math.radians(heading) # Bearing is 90 degrees converted to radians.
speed_mps = 0.514444 * speed_kts # knots -> m/s
d = (age_s * speed_mps) / 1000.0 # Distance in km
lat1 = math.radians(lat) # Current lat point converted to radians
lon1 = math.radians(lon) # Current long point converted to radians
lat2 = math.asin(math.sin(lat1)*math.cos(d/R) + math.cos(lat1)*math.sin(d/R)*math.cos(brng))
lon2 = lon1 + math.atan2(math.sin(brng)*math.sin(d/R)*math.cos(lat1), math.cos(d/R)-math.sin(lat1)*math.sin(lat2))
lat2 = math.degrees(lat2)
lon2 = math.degrees(lon2)
return (lat2, lon2)
def blacklisted(url: str) -> bool:
"""Return True if the URL points to a blacklisted source (ie. one that is on to us and reponds with a 403)
Args:
url (str): URL to image
Returns:
bool: True if URL is blacklisted
"""
if "airliners.net" in url:
return True
if "planefinder.net" in url:
return True
if "carsbase.com" in url:
return True
return False
def image_search(icao24: str, operator: str = None, type: str = None, registration: str = None, update_planedb: bool = True) -> str:
"""Search Bing for plane images. If found, update planedb with URL
Arguments:
icao24 {str} -- ICAO24 designation
operator {str} -- Operator of aircraft
type {str} -- Aircraft type
registration {str} -- Aircraft registration
Returns:
str -- URL of image, hopefully
@todo: don't search for
Bluebird Nordic Boeing 737 4Q8SF TF-BBM
but rather
"Bluebird Nordic" "Boeing 737" "TF-BBM"
or
"Bluebird Nordic" "TF-BBM"
or
"Bluebird Nordic" Boeing "TF-BBM"
"""
img_url = None
# Bing sometimes refuses to search for "Scandinavian Airlines System" :-/
op = None
if operator is not None:
op = operator.replace("Scandinavian Airlines System", "SAS")
searchTerm = ""
if op is not None:
searchTerm = "%s %s" % (searchTerm, op)
if type is not None:
searchTerm = "%s %s" % (searchTerm, type)
if registration is not None:
searchTerm = "%s %s" % (searchTerm, registration)
logging.debug("Searching for %s" % searchTerm)
imageUrls = bing.imageSearch(searchTerm)
if not imageUrls:
imageUrls = bing.imageSearch(registration)
if imageUrls:
# Filter sources as picking a random image has been known to produce naked women...
img_url = None
for temp in imageUrls:
# These are prisitine sources
if "planespotters" in temp or "jetphotos" in temp:
img_url = temp
break
if img_url is None:
for temp in imageUrls:
if blacklisted(temp):
continue
if "flugzeug" in temp or "plane" in temp or "airport" in temp:
img_url = temp
break
if update_planedb and img_url is not None:
logging.info("Added image %s for %s", img_url, icao24)
if not planedb.update_aircraft(icao24, {'image' : img_url}):
logging.error("Failed to update PlaneDB image for %s" % (icao24))
return img_url
else:
logging.error("Image search came up short for '%s', blacklisted (%s)?" % (searchTerm, icao24))
return img_url