Skip to content

Commit

Permalink
some refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
xoolive committed Jul 25, 2023
1 parent 5fe20b2 commit 098da98
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 211 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,11 @@
"altair": ("https://altair-viz.github.io", None),
"cartes": ("https://cartes-viz.github.io", None),
"ipyleaflet": ("https://ipyleaflet.readthedocs.io/en/latest/", None),
"plotly": ("https://plotly.com/python-api-reference/", None),
}


def setup(app: Any) -> None:

# <!-- Import Vega & Vega-Lite -->
app.add_js_file("https://cdn.jsdelivr.net/npm/vega@5")
app.add_js_file("https://cdn.jsdelivr.net/npm/vega-lite@4")
Expand Down
195 changes: 89 additions & 106 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,12 @@ Data visualization
The traffic library offers facilities to leverage the power of common
visualization renderers including `Matplotlib <https://matplotlib.org/>`_,
`Altair <https://altair-viz.github.io/>`__ and `Plotly
<https://plotly.com/python/>`__.
<https://plotly.com/python/>`__.

- with Matplotlib, the ``traffic`` style context (optional) offers a convenient
initial stylesheet:
Matplotlib
~~~~~~~~~~

The ``traffic`` style context (optional) offers a convenient initial stylesheet:

.. jupyter-execute::

Expand All @@ -228,134 +230,89 @@ visualization renderers including `Matplotlib <https://matplotlib.org/>`_,
ax.tick_params(axis='x', labelrotation=0)
ax.xaxis.set_major_formatter(DateFormatter("%H:%M"))

- | The :meth:`~traffic.core.Flight.chart` method triggers an initial representation with Altair which can be further refined.
| For example, with the following subset of trajectories:
.. jupyter-execute::

subset = quickstart[["TVF22LK", "EJU53MF", "TVF51HP", "TVF78YY", "VLG8030"]]
Altair
~~~~~~

.. jupyter-execute::
:hide-code:
:hide-output:
| The :meth:`~traffic.core.Flight.chart` method triggers an initial representation with Altair which can be further refined.
| For example, with the following subset of trajectories:
subset = subset.query("altitude.isnull() or altitude < 20000")
.. jupyter-execute::

.. jupyter-execute::
subset = quickstart[["TVF22LK", "EJU53MF", "TVF51HP", "TVF78YY", "VLG8030"]]

subset[0].chart()
.. jupyter-execute::
:hide-code:
:hide-output:

Even a simple visualization without a physical features plotted on the
y-channel can be meaningful. The following proposition helps visualizing when
aircraft are airborne:
subset = subset.query("altitude.isnull() or altitude < 20000")

.. jupyter-execute::
.. jupyter-execute::

import altair as alt
subset[0].chart()

# necessary line if you see an error about a maximum number of rows
alt.data_transformers.disable_max_rows()
Even a simple visualization without a physical features plotted on the
y-channel can be meaningful. The following proposition helps to visualize when
aircraft are airborne:

alt.layer(
*(
flight.chart().encode(
alt.Y("callsign", sort="x", title=None),
alt.Color("callsign", legend=None),
)
for flight in subset
)
).configure_line(strokeWidth=4)
.. jupyter-execute::

The y-channel is however most often used to plot physical quantities such as
altitude, ground speed, or more.
import altair as alt

.. jupyter-execute::
# necessary line if you see an error about a maximum number of rows
alt.data_transformers.disable_max_rows()

alt.layer(
*(
flight.chart().encode(
alt.Y("altitude"),
alt.Color("callsign"),
alt.Y("callsign", sort="x", title=None),
alt.Color("callsign", legend=None),
)
for flight in subset
)
)
Simple plots are beautiful by default, but it is still possible to further
refine them. For more advanced tricks with Altair, refer to their `online
documentation <https://altair-viz.github.io/>`_.

.. jupyter-execute::

chart = (
alt.layer(
*(
flight.chart().encode(
alt.X(
"utcdayhoursminutesseconds(timestamp)",
axis=alt.Axis(format="%H:%M"),
title=None,
),
alt.Y("altitude", title=None, scale=alt.Scale(domain=(0, 18000))),
alt.Color("callsign"),
)
for flight in subset
)
)
.properties(title="altitude (in ft)") # "trick" to display the y-axis title horizontally
.configure_legend(orient="bottom")
.configure_title(anchor="start", font="Lato", fontSize=16)
)
chart
- Plot a flight on a mapbox layer with plotly:
.. jupyter-execute::
).configure_line(strokeWidth=4)
flight = quickstart[0]
flight.map_plotly(
line=False,
**{
"color": "altitude",
"hover_name": "callsign",
"hover_data": "timestamp",
"title": "A trajectory",
},
)
The line parameter is set to False to disable drawing a line connecting the
points. Additional keyword arguments can be passed in as a dictionary to
customize the appearance of the map. For example you can change the mapbox layer:
The y-channel is however most often used to plot physical quantities such as
altitude, ground speed, or more.

.. jupyter-execute::
.. jupyter-execute::

flight.map_plotly(
line=False,
**{
"color": "altitude",
"hover_name": "callsign",
"hover_data": "timestamp",
"title": "A trajectory",
"mapbox_style": "open-street-map",
},
)
alt.layer(
*(
flight.chart().encode(
alt.Y("altitude"),
alt.Color("callsign"),
)
for flight in subset
)
)
- Plot and animate flight on a mapbox layer with plotly:
.. jupyter-execute::
Simple plots are beautiful by default, but it is still possible to further
refine them. For more advanced tricks with Altair, refer to their `online
documentation <https://altair-viz.github.io/>`_.

flight = quickstart[0].first("3T").resample("5s")
flight.map_plotly(
line=False, **{
"animation_frame": "timestamp",
"hover_data": "altitude"}
)
.. jupyter-execute::

- Plot a Traffic object on a mapbox layer with plotly:
.. jupyter-execute::
chart = (
alt.layer(
*(
flight.chart().encode(
alt.X(
"utcdayhoursminutesseconds(timestamp)",
axis=alt.Axis(format="%H:%M"),
title=None,
),
alt.Y("altitude", title=None, scale=alt.Scale(domain=(0, 18000))),
alt.Color("callsign"),
)
for flight in subset
)
)
.properties(title="altitude (in ft)") # "trick" to display the y-axis title horizontally
.configure_legend(orient="bottom")
.configure_title(anchor="start", font="Lato", fontSize=16)
)
chart
flights = quickstart[:10].first("3T").resample("5s").assign_id().eval()
flights.map_plotly(
**{"color": "flight_id", "hover_data": "altitude"}
)

Making maps
-----------
Expand Down Expand Up @@ -416,6 +373,32 @@ Maps are also available with Matplotlib, Altair, and thanks to `ipyleaflet

subset.map_leaflet(zoom=8)

- Plotly can also produce nice interactive maps:

.. jupyter-execute::

subset.after('2021-10-07 12:35Z').resample('10s').eval().map_plotly(
color="callsign",
animation_frame="timestamp",
hover_name="callsign",
hover_data="altitude",
height=500,
center=airports['LFPO'],
zoom=7
)

These trajectories can also be plotted without animations:

.. jupyter-execute::


subset.resample('10s').eval().map_plotly(
color="callsign",
height=500,
center=airports['LFPO'],
zoom=7
)


Low-altitude trajectory patterns in Paris metropolitan area
-----------------------------------------------------------
Expand Down
9 changes: 4 additions & 5 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,18 @@ Flask-Cors = { version = ">=3.0.10", optional = true }
waitress = { version = ">=2.1.1", optional = true }
pyspark = { version = ">=3.3.0", optional = true }
pyrtlsdr = { version = "^0.2.93", optional = true }
plotly = {version = ">=5.15.0", optional = true }
pyopensky = { git = "https://github.com/open-aviation/pyopensky", branch = "v2.0", optional = true }
# pyopensky = { path = "../pyopensky", develop = true }
pyb2b = { version = ">=0.1", optional = true }
# pyb2b = { path = "../pyb2b", develop = true }
plotly = "^5.15.0"

[tool.poetry.extras]
full = ["xarray", "libarchive", "scikit-learn", "textual", "pyspark"]
web = ["Flask", "Flask-Cors", "waitress", "textual"]
web = ["Flask", "Flask-Cors", "waitress", "textual", "plotly"]
sdr = ["pyrtlsdr"]
trino = ["pyopensky"]
b2b = ["pyb2b"]
plotly = ["plotly"]

[tool.poetry.group.dev.dependencies]
poetry = ">=1.5.1"
Expand Down
43 changes: 0 additions & 43 deletions src/traffic/core/flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -2974,49 +2974,6 @@ def plot(
return ax.plot(*self.shape.xy, **kwargs) # type: ignore
return []

def map_plotly(self, line=True, **kwargs: Any) -> Any:
"""Plot the flight trajectory on a Mapbox map.
:param line: Whether to draw a line connecting the points or a scatter
plot. Default is True.
:param **kwargs: Additional keyword arguments to pass to the
`px.scatter_mapbox` function.
:return: A Plotly figure representing the flight trajectory on
a mapbox layer.
Example usage:
.. code:: python
flight.map_plotly(
line=False,
**{"animation_frame": "timestamp", "hover_data": "altitude"}
)
"""
import plotly.express as px

if "mapbox_style" not in kwargs:
kwargs["mapbox_style"] = "carto-positron"

if line and "animation_frame" in kwargs:
raise ValueError(
"Cannot animate when line=True. "
)
if line:
return px.line_mapbox(
self.data,
lon="longitude",
lat="latitude",
**kwargs,
)
else:
return px.scatter_mapbox(
self.data,
lon="longitude",
lat="latitude",
**kwargs,
)

def chart(self, *features: str) -> "alt.Chart": # coverage: ignore
"""
Initializes an altair Chart based on Flight data.
Expand Down
Loading

0 comments on commit 098da98

Please sign in to comment.