Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ec98b61
import matplotlib.backends.backend_agg explicitly
JanEisermann Sep 17, 2026
eff4ce8
accept any string for a string option in the configuration
JanEisermann Sep 17, 2026
669f935
store path and file name of the flight track in mssautoplot.json
JanEisermann Sep 17, 2026
5d6c7f0
store the flight track with its path in the autoplot dockwidget
JanEisermann Sep 17, 2026
0ff6c03
do not apply a selected autoplot row without a WMS layer
JanEisermann Sep 17, 2026
84dda5b
resolve a relative flight track of a selected configuration against t…
JanEisermann Sep 18, 2026
297798c
say where a missing flight track was looked up before downloading plots
JanEisermann Sep 18, 2026
236dfd2
keep the type check for path and url options of the configuration
JanEisermann Sep 22, 2026
9fc1ff2
resolve a flight track file in one place
JanEisermann Sep 22, 2026
b7d6a1e
keep the first of two autoplot rows which name the same flight track
JanEisermann Sep 22, 2026
4ff9b33
do not claim a flight track was never saved when its origin is unknown
JanEisermann Sep 22, 2026
b554a83
corrected copyright
JanEisermann Sep 23, 2026
5663c85
corrected help description
JanEisermann Sep 23, 2026
9726fbd
doc: how to work with stored flight-tracks
JanEisermann Sep 23, 2026
4524032
accept any string only in the entries of automated_plotting_flights
JanEisermann Sep 24, 2026
0ea63d4
skip the plots of a missing flight track instead of ending the run
JanEisermann Sep 24, 2026
14f0368
say a flight track was named without a directory only when it was
JanEisermann Sep 24, 2026
23e988e
doc: give the --fpath example without ~, which Windows shells do not …
JanEisermann Sep 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/autoplot_dock_widget.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ The **left treewidget** is used to configure the automated plotting flights list
- Flight, filename, and section parameters are configured in the **Top View**.
- The vertical parameter is configured in the **Side View**.

The filename is taken from the flight track currently loaded. The treewidget shows the
file name alone, while the configuration file stores it with its path, so that
mssautoplot finds it independent of the directory it is started in. A flight track
which was never saved has no file yet, for that one only its name is stored and the
file is looked up in the current working directory. Because a flight track without a
file cannot be plotted, the **Download Plots Button** stops before it downloads anything
and tells you where the file was looked up: next to the selected configuration file
which named it, or in the working directory for a name stored without a directory.

A configuration file selected with the **Select configuration file** button may name
its flight tracks without a path, or relative to its own directory, as long as it was
not written by this dockwidget. Such a name is resolved against the directory of the
selected configuration file, the working directory the GUI happens to be started in
is of no use here.

The **right treewidget** is used to configure the automated plotting flight sections, which are based on the view:

- For **Top View**, it is `"automated_plotting_hsecs": [["URL", "Layer", "Styles", "Level"]]`.
Expand Down
21 changes: 21 additions & 0 deletions docs/mssautoplot.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ The CLI tool has the following parameters:
+--------------+-------+----------------------------------------------------------------------+
| ``--ftrack`` | TEXT | Flight track. |
+--------------+-------+----------------------------------------------------------------------+
| ``--fpath`` | TEXT | Directory of the flight track files. Replaces the directory |
| | | stored with the file name in the config file; |
| | | only the base name of the entry is kept. |
+--------------+-------+----------------------------------------------------------------------+
| ``--itime`` | TEXT | Initial time. |
+--------------+-------+----------------------------------------------------------------------+
| ``--vtime`` | TEXT | Valid time. |
Expand Down Expand Up @@ -50,6 +54,11 @@ For downloading plots of multiple flight tracks, specify the flight track and it
["flight1", "section1", "vertical1", "filename1", "init_time1", "time1"]
["flight2", "section2, "vertical2", "filename2", "init_time2", "time2"]]

The filename is stored together with its path, e.g. "/home/mss/flights/flight1.ftml"
or "~/flights/flight1.ftml". Only then the flight track is found independent of the
directory mssautoplot is called in. A bare file name without a path is looked up in
the current working directory.

3. ``mssautoplot --cpath mssautoplot.json --stime="2019-09-01T00:00:00" --etime="2019-09-02T00:00:00" --intv=6``

The above command will download plots of the with/without flight track from start time "2019-09-01T00:00:00" to end time "2019-09-02T00:00:00". The user would need to compulsorily specify the init_time and time in mss_autoplot.json inorder to use this functionality.
Expand All @@ -61,6 +70,18 @@ This command stores the data of topview as PNG for overlays without axis, titles
This could be used in systems like PLANET. You may want to set large values in the layout
of topview in the mssautoplot.json.

5. ``mssautoplot --cpath mssautoplot.json --fpath /home/mss/flights/campaign2``

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on recent windows systems "~" is not working as before.

This command reads the flight tracks from the given directory instead of the directory
stored in mss_autoplot.json. Only the base name of each stored flight track is kept,
any subdirectory stored with it is discarded, so the "/home/mss/campaign1/example.ftml"
of the configuration is loaded from "/home/mss/flights/campaign2/example.ftml".
This lets the same configuration be used for the flight tracks of several campaigns,
provided they share the file names.

On Windows give the directory in full, e.g. ``--fpath C:\Users\mss\flights\campaign2``,
recent Windows shells do not expand "~" reliably.

Comment thread
JanEisermann marked this conversation as resolved.

Settings file
--------------
Expand Down
83 changes: 63 additions & 20 deletions mslib/autoplot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import defusedxml.ElementTree as etree
import PIL.Image
import matplotlib
import matplotlib.backends.backend_agg
from slugify import slugify

import mslib
Expand Down Expand Up @@ -70,6 +71,28 @@
mpl_logger = configure_mpl_logger()


def resolve_ftml_path(filename, fpath=None, directory=None):
"""Resolve the flight track file of an "automated_plotting_flights" entry.

The single place which defines where such an entry is looked up, used by the
CLI and by the dockwidget which writes the entries.

The entry stores the path together with the file name, "~" is expanded. A
relative name is taken relative to <directory>, the directory of the
configuration file which named it, and relative to the current working
directory without one. <fpath>, the --fpath option of the CLI, replaces the
directory of the entry altogether.

Returns an absolute Path, which does not need to exist.
"""
path = Path(filename).expanduser()
if fpath:
path = Path(fpath).expanduser() / path.name
elif directory is not None and not path.is_absolute():
path = Path(directory) / path
return path.resolve()


def load_from_ftml(filename):
"""Load a flight track from an XML file at <filename>.
"""
Expand Down Expand Up @@ -187,7 +210,8 @@ def get_op_id(msc_url, token, op_name):


class Plotting:
def __init__(self, cpath, msc_url=None, msc_auth_password=None, username=None, password=None, pdlg=None, raw=False):
def __init__(self, cpath, msc_url=None, msc_auth_password=None, username=None, password=None, pdlg=None, raw=False,
fpath=None):
"""
Initialize the Plotting object with the provided parameters.

Expand All @@ -197,9 +221,12 @@ def __init__(self, cpath, msc_url=None, msc_auth_password=None, username=None, p
:msc_auth_password: Authentication password for MSColab service
:username: User's username
:password: User's password
:fpath: Directory of the flight track files, overrides the directory
stored in the configuration file
"""
read_config_file(cpath)
self.pdlg = pdlg
self.fpath = fpath
self.config = config_loader()
self.num_interpolation_points = self.config["num_interpolation_points"]
self.num_labels = self.config["num_labels"]
Expand All @@ -224,14 +251,6 @@ def __init__(self, cpath, msc_url=None, msc_auth_password=None, username=None, p
if filename != "" and filename == flight:
self.read_operation(flight, msc_url, msc_auth_password, username, password)
elif filename != "":
# Todo add the dir to the file in the mssautoplot.json
dirpath = "./"
file_path = os.path.join(dirpath, filename)
exists = os.path.exists(file_path)
if not exists:
print("Filename {} doesn't exist".format(filename))
self.pdlg.close()
raise SystemExit("Filename {} doesn't exist".format(filename))
self.read_ftml(filename)

def setup(self):
Expand Down Expand Up @@ -264,8 +283,25 @@ def update_path_ops(self, filename=None):
self.plotter.update_from_waypoints(self.wp_model_data)
self.plotter.redraw_path(waypoints_model_data=self.wp_model_data)

def ftml_file(self, filename):
"""
Flight track file of an "automated_plotting_flights" entry, checked for existence.

Raises FileNotFoundError when the file is not there. draw() in main() catches it,
so a missing file skips the plots of that entry only and the run goes on with
the next one.
"""
file_path = resolve_ftml_path(filename, self.fpath)
if not file_path.exists():
message = "Filename {} doesn't exist".format(file_path)
print(message)
if self.pdlg is not None:
self.pdlg.close()
raise FileNotFoundError(message)
return file_path

def read_ftml(self, filename):
self.wps, self.wp_model_data = load_from_ftml(filename)
self.wps, self.wp_model_data = load_from_ftml(self.ftml_file(filename))
self.wp_lats, self.wp_lons, self.wp_locs = [[x[i] for x in self.wps] for i in [0, 1, 3]]
self.wp_press = [mslib.utils.thermolib.flightlevel2pressure(wp[2] * units.hft).to("Pa").m for wp in self.wps]
self.path = [(wp[0], wp[1], datetime.now()) for wp in self.wps]
Expand All @@ -290,8 +326,9 @@ def read_operation(self, op_name, msc_url, msc_auth_password, username, password


class TopViewPlotting(Plotting):
def __init__(self, cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw=False):
super(TopViewPlotting, self).__init__(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw)
def __init__(self, cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw=False, fpath=None):
super(TopViewPlotting, self).__init__(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw,
fpath)
self.pdlg = pdlg
self.myfig = viewplotter.TopViewPlotter()
self.myfig.fig.canvas.draw()
Expand Down Expand Up @@ -359,8 +396,9 @@ def draw(self, flight, section, vertical, filename, init_time, time, url, layer,


class SideViewPlotting(Plotting):
def __init__(self, cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw=False):
super(SideViewPlotting, self).__init__(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw)
def __init__(self, cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw=False, fpath=None):
super(SideViewPlotting, self).__init__(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw,
fpath)
self.pdlg = pdlg
self.myfig = viewplotter.SideViewPlotter()
self.ax = self.myfig.ax
Expand Down Expand Up @@ -475,8 +513,9 @@ def draw(self, flight, section, vertical, filename, init_time, time, url, layer,

class LinearViewPlotting(Plotting):
# ToDo Implement access of MSColab
def __init__(self, cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw=False):
super(LinearViewPlotting, self).__init__(cpath, msc_url, msc_auth_password, msc_username, msc_password, raw)
def __init__(self, cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw=False, fpath=None):
super(LinearViewPlotting, self).__init__(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg,
raw, fpath)
self.pdlg = pdlg
self.myfig = viewplotter.LinearViewPlotter()
self.ax = self.myfig.ax
Expand Down Expand Up @@ -573,14 +612,17 @@ def draw(self, flight, section, vertical, filename, init_time, time, url, layer,
@click.option('--cpath', default=constants.MSS_AUTOPLOT, help='Path of the configuration file.')
@click.option('--view', default="top", help='View of the plot (top/side/linear).')
@click.option('--ftrack', default="", help='Flight track.')
@click.option('--fpath', default="", help='Directory of the flight track files. Replaces the directory stored '
'with the file name in the configuration file, '
'any subdirectory of that stored path is discarded.')
@click.option('--itime', default="", help='Initial time.')
@click.option('--vtime', default="", help='Valid time.')
@click.option('--intv', default=0, help='Time interval.')
@click.option('--stime', default="", help='Starting time for downloading multiple plots with a fixed interval.')
@click.option('--etime', default="", help='Ending time for downloading multiple plots with a fixed interval.')
@click.option('--raw', default=False, help='Saves the raw image with its projection in topview')
@click.pass_context
def main(ctx, cpath, view, ftrack, itime, vtime, intv, stime, etime, raw):
def main(ctx, cpath, view, ftrack, fpath, itime, vtime, intv, stime, etime, raw):
pdlg = None

def close_process_dialog(pdlg):
Expand Down Expand Up @@ -627,13 +669,14 @@ def close_process_dialog(pdlg):

# Choose view (top or side)
if view == "top":
top_view = TopViewPlotting(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw)
top_view = TopViewPlotting(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw, fpath)
sec = "automated_plotting_hsecs"
elif view == "side":
side_view = SideViewPlotting(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw)
side_view = SideViewPlotting(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw, fpath)
sec = "automated_plotting_vsecs"
elif view == "linear":
linear_view = LinearViewPlotting(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw)
linear_view = LinearViewPlotting(cpath, msc_url, msc_auth_password, msc_username, msc_password, pdlg, raw,
fpath)
sec = "automated_plotting_lsecs"
else:
print("Invalid view")
Expand Down
Loading
Loading