Skip to content

Commit ad5459b

Browse files
committed
bugfix, typing and cosmetics
1 parent d413417 commit ad5459b

File tree

6 files changed

+56
-39
lines changed

6 files changed

+56
-39
lines changed

traffic/__init__.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import imp
33
import inspect
44
import logging
5+
import os
6+
import subprocess
7+
import sys
58
from pathlib import Path
69

710
from appdirs import user_cache_dir, user_config_dir
@@ -31,9 +34,19 @@
3134
config = configparser.ConfigParser()
3235
config.read(config_file.as_posix())
3336

37+
38+
def edit_config():
39+
if sys.platform.startswith("darwin"):
40+
subprocess.call(("open", config_file))
41+
elif os.name == "nt": # For Windows
42+
os.startfile(config_dir)
43+
elif os.name == "posix": # For Linux, Mac, etc.
44+
subprocess.call(("xdg-open", config_file))
45+
46+
3447
_selected = [
35-
s.strip() for s in config.get("plugins", "enabled_plugins",
36-
fallback="").split(",")
48+
s.strip()
49+
for s in config.get("plugins", "enabled_plugins", fallback="").split(",")
3750
]
3851
_plugin_paths = [Path(__file__).parent / "plugins", config_dir / "plugins"]
3952
_all_plugins = []

traffic/core/flight.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
Set, Tuple, Union, cast)
88

99
import numpy as np
10+
from matplotlib.artist import Artist
1011
from matplotlib.axes._subplots import Axes
1112

1213
import geodesy.wgs84 as geo
1314
import pandas as pd
1415
import scipy.signal
16+
from cartopy.crs import PlateCarree
1517
from cartopy.mpl.geoaxes import GeoAxesSubplot
1618
from shapely.geometry import LineString, base
1719

@@ -32,6 +34,10 @@ def _split(data: pd.DataFrame, value, unit) -> Iterator[pd.DataFrame]:
3234
yield data
3335

3436

37+
class Position(PointMixin, pd.core.series.Series):
38+
pass
39+
40+
3541
class Flight(DataFrameMixin, ShapelyMixin, GeographyMixin):
3642
"""Flight is the basic class associated to an aircraft itinerary.
3743
@@ -101,7 +107,7 @@ def stop(self) -> pd.Timestamp:
101107
@property
102108
def callsign(self) -> Union[str, Set[str], None]:
103109
"""Returns the unique callsign value(s) of the DataFrame."""
104-
if 'callsign' not in self.data.columns:
110+
if "callsign" not in self.data.columns:
105111
return None
106112
tmp = set(self.data.callsign)
107113
if len(tmp) == 1:
@@ -537,18 +543,16 @@ def comet(b, **kwargs) -> "Flight":
537543
).ffill()
538544
)
539545

540-
def at(self, time: Optional[timelike] = None) -> Optional[pd.core.series.Series]:
541-
class Position(PointMixin, pd.core.series.Series):
542-
pass
546+
def at(self, time: Optional[timelike] = None) -> Optional[Position]:
543547

544548
if time is None:
545549
return Position(self.data.ffill().iloc[-1])
546550

547551
index = to_datetime(time)
548552
df = self.data.set_index("timestamp")
549553
if index not in df.index:
550-
id_ = getattr(self, 'flight_id', self.callsign)
551-
logging.warn(f'No index {index} for flight {id_}')
554+
id_ = getattr(self, "flight_id", self.callsign)
555+
logging.warn(f"No index {index} for flight {id_}")
552556
return None
553557
return Position(df.loc[index])
554558

@@ -618,13 +622,13 @@ def _clip_generator():
618622

619623
# -- Visualisation --
620624

621-
def plot(self, ax: GeoAxesSubplot, **kwargs) -> None:
622-
if "projection" in ax.__dict__ and "transform" not in kwargs:
623-
from cartopy.crs import PlateCarree
625+
def plot(self, ax: GeoAxesSubplot, **kwargs) -> List[Artist]:
624626

627+
if "projection" in ax.__dict__ and "transform" not in kwargs:
625628
kwargs["transform"] = PlateCarree()
626629
if self.shape is not None:
627630
return ax.plot(*self.shape.xy, **kwargs)
631+
return []
628632

629633
def plot_time(
630634
self,

traffic/core/mixins.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import datetime
12
from functools import lru_cache, partial
23
from pathlib import Path
34
from typing import List, Tuple, Union
@@ -195,6 +196,8 @@ class PointMixin(object):
195196

196197
latitude: float
197198
longitude: float
199+
altitude: float
200+
timestamp: datetime
198201

199202
def plot(
200203
self, ax: Axes, text_kw=None, shift=dict(units="dots", x=15), **kwargs

traffic/core/traffic.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,7 @@ def end_time(self) -> pd.Timestamp:
136136
@lru_cache()
137137
def callsigns(self) -> Set[str]:
138138
"""Return only the most relevant callsigns"""
139-
sub = (
140-
self.data.query("callsign == callsign")
141-
.groupby(["callsign", "icao24"])
142-
.filter(lambda x: len(x) > 10)
143-
)
139+
sub = self.data.query("callsign == callsign")
144140
return set(cs for cs in sub.callsign if len(cs) > 3 and " " not in cs)
145141

146142
@property

traffic/data/adsb/decode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class StoppableThread(threading.Thread):
2727

2828
def __init__(self, *args, **kwargs):
2929
super().__init__(*args, **kwargs)
30+
self.daemon = True # is it redundant?
3031
self._stop_event = threading.Event()
3132

3233
def stop(self):

traffic/drawing/ipywidgets.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -180,20 +180,21 @@ def create_map(
180180
with plt.style.context("traffic"):
181181

182182
self.fig_map.clear()
183-
self.ax = self.fig_map.add_subplot(
183+
self.trajectories.clear()
184+
self.ax_map = self.fig_map.add_subplot(
184185
111, projection=self.projection
185186
)
186-
self.ax.add_feature(countries())
187+
self.ax_map.add_feature(countries())
187188
if projection.__class__.__name__.split(".")[-1] in [
188189
"Lambert93"
189190
]:
190-
self.ax.add_feature(rivers())
191+
self.ax_map.add_feature(rivers())
191192

192193
self.fig_map.set_tight_layout(True)
193-
self.ax.background_patch.set_visible(False)
194-
self.ax.outline_patch.set_visible(False)
195-
self.ax.format_coord = lambda x, y: ""
196-
self.ax.set_global()
194+
self.ax_map.background_patch.set_visible(False)
195+
self.ax_map.outline_patch.set_visible(False)
196+
self.ax_map.format_coord = lambda x, y: ""
197+
self.ax_map.set_global()
197198

198199
self.default_plot()
199200
self.canvas_map.draw_idle()
@@ -205,8 +206,9 @@ def default_plot(self) -> None:
205206
for elt in value:
206207
elt.remove()
207208
self.trajectories.clear()
209+
self.ax_map.set_prop_cycle(None)
208210

209-
lon_min, lon_max, lat_min, lat_max = self.ax.get_extent(
211+
lon_min, lon_max, lat_min, lat_max = self.ax_map.get_extent(
210212
PlateCarree()
211213
)
212214
cur_flights = list(
@@ -223,7 +225,7 @@ def params(at):
223225
return dict(s=8, text_kw=dict(s=""))
224226

225227
for at in cur_flights:
226-
self.trajectories[at.callsign] += at.plot(self.ax, **params(at))
228+
self.trajectories[at.callsign] += at.plot(self.ax_map, **params(at))
227229

228230
self.canvas_map.draw_idle()
229231

@@ -238,13 +240,11 @@ def create_timeplot(self) -> None:
238240
def on_projection_change(self, change: Dict[str, Any]) -> None:
239241
with self.output:
240242
if change["name"] == "value":
241-
self.trajectories.clear()
242243
self.create_map(change["new"])
243244

244245
def on_clear_button(self, elt: Dict[str, Any]) -> None:
245246
with self.output:
246247
self.t_view = self.traffic.sort_values("timestamp")
247-
self.trajectories.clear()
248248
self.create_map(self.projection)
249249
self.create_timeplot()
250250

@@ -268,20 +268,20 @@ def on_area_click(self, elt: Dict[str, Any]) -> None:
268268
return
269269
from ..data import airac
270270

271-
self.ax.set_extent(airac[elt["new"][0]])
271+
self.ax_map.set_extent(airac[elt["new"][0]])
272272
self.canvas_map.draw_idle()
273273

274274
def on_extent_button(self, elt: Dict[str, Any]) -> None:
275275
with self.output:
276276
if len(self.area_select.value) == 0:
277277
if len(self.area_input.value) == 0:
278-
self.ax.set_global()
278+
self.ax_map.set_global()
279279
else:
280-
self.ax.set_extent(location(self.area_input.value))
280+
self.ax_map.set_extent(location(self.area_input.value))
281281
else:
282282
from ..data import airac
283283

284-
self.ax.set_extent(airac[self.area_select.value[0]])
284+
self.ax_map.set_extent(airac[self.area_select.value[0]])
285285

286286
t1, t2 = self.time_slider.index
287287
low, up = self.altitude_select.value
@@ -319,32 +319,32 @@ def on_plot_button(self, elt: Dict[str, Any]) -> None:
319319
if len(self.area_input.value) == 0:
320320
return self.default_plot()
321321
location(self.area_input.value).plot(
322-
self.ax, color="grey", linestyle="dashed"
322+
self.ax_map, color="grey", linestyle="dashed"
323323
)
324324
else:
325325
from ..data import airac
326326

327327
airspace = airac[self.area_select.value[0]]
328328
if airspace is not None:
329-
airspace.plot(self.ax)
329+
airspace.plot(self.ax_map)
330330
self.canvas_map.draw_idle()
331331

332332
def on_plot_airport(self, elt: Dict[str, Any]) -> None:
333333
with self.output:
334334
if len(self.area_input.value) == 0:
335335
from cartotools.osm import request, tags
336336

337-
west, east, south, north = self.ax.get_extent(crs=PlateCarree())
337+
west, east, south, north = self.ax_map.get_extent(crs=PlateCarree())
338338
if abs(east - west) > 1 or abs(north - south) > 1:
339339
# that would be a too big request
340340
return
341341
request((west, south, east, north), **tags.airport).plot(
342-
self.ax
342+
self.ax_map
343343
)
344344
else:
345345
from ..data import airports
346346

347-
airports[self.area_input.value].plot(self.ax)
347+
airports[self.area_input.value].plot(self.ax_map)
348348
self.canvas_map.draw_idle()
349349

350350
def on_id_change(self, change: Dict[str, Any]) -> None:
@@ -364,9 +364,9 @@ def on_id_change(self, change: Dict[str, Any]) -> None:
364364
f = self.t_view[c]
365365
if f is not None:
366366
try:
367-
self.trajectories[c] += f.plot(self.ax)
367+
self.trajectories[c] += f.plot(self.ax_map)
368368
self.trajectories[c] += f.at().plot(
369-
self.ax, s=8, text_kw=dict(s=c)
369+
self.ax_map, s=8, text_kw=dict(s=c)
370370
)
371371
except TypeError: # NoneType object is not iterable
372372
pass
@@ -401,7 +401,7 @@ def on_id_change(self, change: Dict[str, Any]) -> None:
401401

402402
def on_filter(self, low, up, t1, t2) -> None:
403403
with self.output:
404-
west, east, south, north = self.ax.get_extent(crs=PlateCarree())
404+
west, east, south, north = self.ax_map.get_extent(crs=PlateCarree())
405405

406406
self.t_view = (
407407
self.traffic.between(self.dates[t1], self.dates[t2])

0 commit comments

Comments
 (0)