From 90552000af3883359c811ab096d153a19ca5da4a Mon Sep 17 00:00:00 2001 From: ReimarBauer Date: Sat, 19 Aug 2023 08:25:14 +0200 Subject: [PATCH 1/9] improved handling on a server restart, token invalidation (#1906) --- mslib/msui/mscolab.py | 9 +++++++-- mslib/msui/mscolab_admin_window.py | 2 +- mslib/msui/msui.py | 10 +++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/mslib/msui/mscolab.py b/mslib/msui/mscolab.py index e63f4ce8d..13c227524 100644 --- a/mslib/msui/mscolab.py +++ b/mslib/msui/mscolab.py @@ -934,6 +934,7 @@ def get_recent_op_id(self): return op_id else: show_popup(self.ui, "Error", "Session expired, new login required") + self.signal_logout_mscolab() else: show_popup(self.ui, "Error", "Your Connection is expired. New Login required!") self.logout() @@ -1510,8 +1511,12 @@ def show_categories_to_ui(self): data = { "token": self.token } - r = requests.get(f'{self.mscolab_server_url}/operations', data=data, timeout=(2, 10)) - if r.text != "False": + r = None + try: + r = requests.get(f'{self.mscolab_server_url}/operations', data=data, timeout=(2, 10)) + except requests.exceptions.MissingSchema: + show_popup(self.ui, "Error", "Session expired, new login required") + if r is not None and r.text != "False": _json = json.loads(r.text) operations = _json["operations"] self.ui.filterCategoryCb.clear() diff --git a/mslib/msui/mscolab_admin_window.py b/mslib/msui/mscolab_admin_window.py index ee55d0a5f..1d23c5bba 100644 --- a/mslib/msui/mscolab_admin_window.py +++ b/mslib/msui/mscolab_admin_window.py @@ -180,7 +180,7 @@ def set_label_text(self): if r.text != "False": _json = json.loads(r.text) creator_name = _json["username"] - self.operationNameLabel.setText(f"Operation: {self.operation_name} by User: {creator_name}") + self.operationNameLabel.setText(f"Operation: {self.operation_name} by User: {creator_name}") self.usernameLabel.setText(f"Logged In: {self.user['username']}") def load_import_operations(self): diff --git a/mslib/msui/msui.py b/mslib/msui/msui.py index adb93614f..33d5c23b4 100644 --- a/mslib/msui/msui.py +++ b/mslib/msui/msui.py @@ -58,7 +58,7 @@ from mslib.utils import setup_logging from mslib.plugins.io.csv import load_from_csv, save_to_csv from mslib.msui.icons import icons, python_powered -from mslib.utils.qt import get_open_filenames, get_save_filename, Worker, Updater +from mslib.utils.qt import get_open_filenames, get_save_filename, show_popup, Worker, Updater from mslib.utils.config import read_config_file, config_loader from mslib.utils.auth import get_auth_from_url_and_name from PyQt5 import QtGui, QtCore, QtWidgets, QtTest @@ -881,8 +881,12 @@ def create_view_handler(self, _type): if self.local_active: self.create_view(_type, self.active_flight_track) else: - self.mscolab.waypoints_model.name = self.mscolab.active_operation_name - self.create_view(_type, self.mscolab.waypoints_model) + try: + self.mscolab.waypoints_model.name = self.mscolab.active_operation_name + self.create_view(_type, self.mscolab.waypoints_model) + except AttributeError: + # can happen, when the servers secret was changed + show_popup(self.mscolab.ui, "Error", "Session expired, new login required") def create_view(self, _type, model): """Method called when the user selects a new view to be opened. Creates From 23037e1c042042163427878b9e82bf2cc5172336 Mon Sep 17 00:00:00 2001 From: "J. Ungermann" Date: Wed, 23 Aug 2023 22:58:57 -0800 Subject: [PATCH 2/9] Fix error in drawing legends caused by auto-plotter refactoring. (#1928) See #1835 --- mslib/msui/wms_control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mslib/msui/wms_control.py b/mslib/msui/wms_control.py index ee052493f..6aa396acb 100644 --- a/mslib/msui/wms_control.py +++ b/mslib/msui/wms_control.py @@ -1481,7 +1481,7 @@ def append_multiple_images(self, imgs): # Add border around seperate legends if len(images) > 1: images = [ImageOps.expand(x, border=1, fill="black") for x in images] - max_height = int((self.view.fig.get_size_inches() * self.view.fig.get_dpi())[1] * 0.99) + max_height = int((self.view.plotter.fig.get_size_inches() * self.view.plotter.fig.get_dpi())[1] * 0.99) width = max([image.width for image in images]) height = sum([image.height for image in images]) result = Image.new("RGBA", (width, height)) From aebf7c726a41400f22351282a7e40cd4ba5a8a6b Mon Sep 17 00:00:00 2001 From: "J. Ungermann" Date: Fri, 25 Aug 2023 03:27:37 -0800 Subject: [PATCH 3/9] i1935 (#1936) * Retain value when editing data in TableView. Fix #1935 * trigger CIs --------- Co-authored-by: ReimarBauer --- mslib/msui/flighttrack.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/mslib/msui/flighttrack.py b/mslib/msui/flighttrack.py index bc9685d87..64080e717 100755 --- a/mslib/msui/flighttrack.py +++ b/mslib/msui/flighttrack.py @@ -693,15 +693,12 @@ def createEditor(self, parent, option, index): """ if index.column() == LOCATION: combobox = QtWidgets.QComboBox(parent) - locations = config_loader(dataset='locations') - adds = list(locations.keys()) + adds = set(config_loader(dataset='locations')) if self.parent() is not None: - for loc in [wp.location for wp in self.parent().waypoints_model.all_waypoint_data() if - wp.location != ""]: - if loc not in adds: - adds.append(loc) + for wp in self.parent().waypoints_model.all_waypoint_data(): + if wp.location != "": + adds.add(wp.location) combobox.addItems(sorted(adds)) - combobox.setEditable(True) return combobox else: @@ -709,14 +706,14 @@ def createEditor(self, parent, option, index): return QtWidgets.QItemDelegate.createEditor(self, parent, option, index) def setEditorData(self, editor, index): - text = index.model().data(index, QtCore.Qt.DisplayRole).value() + value = index.model().data(index, QtCore.Qt.DisplayRole).value() if index.column() in (LOCATION,): - i = editor.findText(text) + i = editor.findText(value) if i == -1: i = 0 editor.setCurrentIndex(i) else: - QtWidgets.QItemDelegate.setEditorData(self, editor, index) + editor.insert(str(value)) def setModelData(self, editor, model, index): """ From faf4e991062324fe4011404dfc7458b50f8509b9 Mon Sep 17 00:00:00 2001 From: "J. Ungermann" Date: Thu, 31 Aug 2023 22:46:17 -0800 Subject: [PATCH 4/9] Fix proper projection of points in kml files. (#1980) See #1977 --- mslib/msui/kmloverlay_dockwidget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mslib/msui/kmloverlay_dockwidget.py b/mslib/msui/kmloverlay_dockwidget.py index 59f9ea0fb..3669d9f42 100644 --- a/mslib/msui/kmloverlay_dockwidget.py +++ b/mslib/msui/kmloverlay_dockwidget.py @@ -66,7 +66,7 @@ def add_point(self, point, style, name): :param point: fastkml object specifying point :param name: name of placemark for annotation """ - x, y = (point.geometry.x, point.geometry.y) + x, y = self.map(point.geometry.x, point.geometry.y) self.patches.append(self.map.plot(x, y, "o", zorder=10, color=self.color)) if name is not None: self.patches.append([self.map.ax.annotate( @@ -107,7 +107,7 @@ def add_multipoint(self, point, style, name): :param point: fastkml object specifying point :param name: name of placemark for annotation """ - x, y = (point.x, point.y) + x, y = self.map(point.x, point.y) self.patches.append(self.map.plot(x, y, "o", zorder=10, color=self.color)) if name is not None: self.patches.append([self.map.ax.annotate( From 1b3ecc45eeaedb16b6295580686b09ab20959fd0 Mon Sep 17 00:00:00 2001 From: "J. Ungermann" Date: Fri, 1 Sep 2023 09:49:06 -0800 Subject: [PATCH 5/9] Drastically improved KML overlay performance for multiple files. (#1982) * Drastically improved KML overlay performance for multiple files. Fix #1981 * adopted tests --------- Co-authored-by: ReimarBauer --- mslib/msui/kmloverlay_dockwidget.py | 14 ++++++++++---- tests/_test_msui/test_kmloverlay_dockwidget.py | 6 +++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/mslib/msui/kmloverlay_dockwidget.py b/mslib/msui/kmloverlay_dockwidget.py index 3669d9f42..c1667ed9c 100644 --- a/mslib/msui/kmloverlay_dockwidget.py +++ b/mslib/msui/kmloverlay_dockwidget.py @@ -305,8 +305,6 @@ def __init__(self, parent=None, view=None): self.pushButton_color.clicked.connect(self.select_color) self.dsbx_linewidth.valueChanged.connect(self.select_linewidth) - self.listWidget.itemChanged.connect(self.flagop) # when item changes, flag operation happens. - self.settings_tag = "kmldock" settings = load_settings_qsettings( self.settings_tag, {"filename": "", "linewidth": 2, "colour": (0, 0, 0, 1), @@ -333,6 +331,8 @@ def __init__(self, parent=None, view=None): # When KMLoverlaywidget is opened, it ensures that the # color of individual KML files are already shown as icons. self.set_color_icons() + # must be connected here, lest the set_color_icons routine above causes many reloads + self.listWidget.itemChanged.connect(self.flagop) self.view.plot_kml(self) def update(self): @@ -460,6 +460,8 @@ def get_file(self): self, "Open KML File", os.path.dirname(str(self.directory_location)), "KML Files (*.kml)") if not filenames: return + + self.listWidget.itemChanged.disconnect(self.flagop) self.select_file(filenames) # set color icons according to linewidth to newly added KML files for filename in filenames: @@ -468,10 +470,12 @@ def get_file(self): for item in item_list: index = self.listWidget.row(item) self.listWidget.item(index).setIcon(self.show_color_icon(filename, self.set_color(filename))) + self.load_file() + self.listWidget.itemChanged.connect(self.flagop) def select_file(self, filenames): """ - Initializes selected file/ files + Initializes selected file/files """ for filename in filenames: if filename is None: @@ -489,7 +493,6 @@ def select_file(self, filenames): else: logging.info("%s file already added", text) self.labelStatusBar.setText("Status: KML Files added") - self.load_file() def create_list_item(self, text): """ @@ -512,8 +515,11 @@ def unselect_all(self): # unselects all files if self.listWidget.count() == 0: self.labelStatusBar.setText("Status: No Files to unselect. Click on Add KML Files to get started.") return + self.listWidget.itemChanged.disconnect(self.flagop) for index in range(self.listWidget.count()): self.listWidget.item(index).setCheckState(QtCore.Qt.Unchecked) + self.listWidget.itemChanged.connect(self.flagop) + self.load_file() self.labelStatusBar.setText("Status: All Files unselected") def remove_file(self): # removes checked files diff --git a/tests/_test_msui/test_kmloverlay_dockwidget.py b/tests/_test_msui/test_kmloverlay_dockwidget.py index 130197436..d3eaf43a7 100644 --- a/tests/_test_msui/test_kmloverlay_dockwidget.py +++ b/tests/_test_msui/test_kmloverlay_dockwidget.py @@ -37,6 +37,8 @@ save_kml = os.path.join(ROOT_DIR, "merged_file123.kml") +# ToDo refactoring, extract helper methods into functions +# ToDo review needed helper functions class Test_KmlOverlayDockWidget(object): def setup_method(self): @@ -70,6 +72,7 @@ def select_file(self, file): # Utility function for single file path = fs.path.join(sample_path, file) filename = (path,) # converted to tuple self.window.select_file(filename) + self.window.load_file() QtWidgets.QApplication.processEvents() return path @@ -103,7 +106,7 @@ def test_select_file(self, mockbox): assert mockbox.critical.call_count == 0 assert self.window.listWidget.count() == index assert len(self.window.dict_files) == index - assert self.count_patches() > 0 + assert self.count_patches() == 9 self.window.select_all() self.window.remove_file() @@ -118,6 +121,7 @@ def test_select_file_error(self, mockbox): path = fs.path.join(sample_path, "satellite_predictor.txt") filename = (path,) # converted to tuple self.window.select_file(filename) + self.window.load_file() QtWidgets.QApplication.processEvents() assert mockbox.critical.call_count == 1 self.window.listWidget.clear() From fa84f23bec506e942364706d5ed6fd69be89770a Mon Sep 17 00:00:00 2001 From: "J. Ungermann" Date: Sat, 2 Sep 2023 22:45:37 -0800 Subject: [PATCH 6/9] Fixed bug in updating operation names in top view windows (#1983) Fix #1961 --- mslib/msui/mpl_map.py | 41 +++++++++++++++++++-------------------- mslib/msui/viewwindows.py | 4 +--- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/mslib/msui/mpl_map.py b/mslib/msui/mpl_map.py index 4427a57d7..6c0496696 100644 --- a/mslib/msui/mpl_map.py +++ b/mslib/msui/mpl_map.py @@ -147,17 +147,10 @@ def __init__(self, identifier=None, CRS=None, BBOX_UNITS=None, OPERATION_NAME=No self.image = None # Print project name and CRS identifier into figure. - crs_text = "" - if self.operation_name is not None: - crs_text += self.operation_name - if self.crs is not None: - if len(crs_text) > 0: - crs_text += "\n" - crs_text += self.crs - if hasattr(self, "crs_text"): # update existing textbox - self.crs_text.set_text(crs_text) - else: - self.crs_text = self.ax.figure.text(0, 0, crs_text) + if not hasattr(self, "_info_text"): + self._info_text = self.ax.figure.text(0, 0, "") + self._infos = [None] * 4 + self.update_info_text(name=self.operation_name, crs=self.crs) if self.appearance["draw_graticule"]: pass @@ -175,6 +168,18 @@ def __init__(self, identifier=None, CRS=None, BBOX_UNITS=None, OPERATION_NAME=No self.airspaces = None self.airspacetext = None + def update_info_text(self, openaip=None, ourairports=None, name=None, crs=None): + if openaip is not None: + self._infos[0] = openaip + if ourairports is not None: + self._infos[1] = ourairports + if name is not None: + self._infos[2] = name + if crs is not None: + self._infos[3] = crs + self._info_text.set_text( + "\n".join([_i for _i in self._infos if _i])) # both None and "" + def set_identifier(self, identifier): self.identifier = identifier @@ -350,8 +355,7 @@ def set_draw_airports(self, value, port_type=["small_airport"], reload=True): Sets airports to visible or not visible """ if (reload or not value or len(port_type) == 0) and self.airports: - if OURAIRPORTS_NOTICE in self.crs_text.get_text(): - self.crs_text.set_text(self.crs_text.get_text().replace(f"{OURAIRPORTS_NOTICE}\n", "")) + self.update_info_text(ourairports="") self.airports.remove() self.airtext.remove() self.airports = None @@ -365,8 +369,7 @@ def set_draw_airspaces(self, value, airspaces=[], range_km=None, reload=True): Sets airspaces to visible or not visible """ if (reload or not value or len(airspaces) == 0) and self.airspaces: - if OPENAIP_NOTICE in self.crs_text.get_text(): - self.crs_text.set_text(self.crs_text.get_text().replace(f"{OPENAIP_NOTICE}\n", "")) + self.update_info_text(openaip="") self.airspaces.remove() self.airspacetext.remove() self.airspaces = None @@ -396,9 +399,7 @@ def draw_airspaces(self, countries=[], range_km=None): if not airspaces: return - if OPENAIP_NOTICE not in self.crs_text.get_text(): - self.crs_text.set_text(f"{OPENAIP_NOTICE}\n" + self.crs_text.get_text()) - + self.update_info_text(openaip=OPENAIP_NOTICE) airspaces.sort(key=lambda x: (x["bottom"], x["top"] - x["bottom"])) max_height = max(airspaces[-1]["bottom"], 0.001) cmap = get_cmap("Blues") @@ -464,9 +465,7 @@ def draw_airports(self, port_type): if not airports: return - if OURAIRPORTS_NOTICE not in self.crs_text.get_text(): - self.crs_text.set_text(f"{OURAIRPORTS_NOTICE}\n" + self.crs_text.get_text()) - + self.update_info_text(ourairports=OURAIRPORTS_NOTICE) self.airports = self.ax.scatter(lons, lats, marker="o", color="r", linewidth=1, s=9, edgecolor="black", zorder=6) self.airports.set_pickradius(1) diff --git a/mslib/msui/viewwindows.py b/mslib/msui/viewwindows.py index 9d74706fa..052c33a37 100644 --- a/mslib/msui/viewwindows.py +++ b/mslib/msui/viewwindows.py @@ -234,10 +234,8 @@ def setFlightTrackModel(self, model): # Update Top View flighttrack name if hasattr(self.mpl.canvas, "map"): - text = self.mpl.canvas.map.crs_text.get_text() - old_name = self.mpl.canvas.map.operation_name self.mpl.canvas.map.operation_name = model.name - self.mpl.canvas.map.crs_text.set_text(text.replace(old_name, model.name)) + self.mpl.canvas.map.update_info_text(name=model.name) self.mpl.canvas.map.ax.figure.canvas.draw() def getView(self): From 7a89df69663c2c44884849077191adeb94e1c8e2 Mon Sep 17 00:00:00 2001 From: "J. Ungermann" Date: Sat, 2 Sep 2023 23:22:57 -0800 Subject: [PATCH 7/9] =?UTF-8?q?Round=20vertical=20coordinate=20to=20closes?= =?UTF-8?q?t=20legal=20flight=20level=20upon=20insertio=E2=80=A6=20(#1979)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Round vertical coordinate to closest legal flight level upon insertion in side view. Fix #1976 * Empty-Commit --------- Co-authored-by: ReimarBauer --- mslib/msui/mpl_pathinteractor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mslib/msui/mpl_pathinteractor.py b/mslib/msui/mpl_pathinteractor.py index a40e97edf..d0d5062af 100644 --- a/mslib/msui/mpl_pathinteractor.py +++ b/mslib/msui/mpl_pathinteractor.py @@ -977,6 +977,8 @@ def button_release_insert_callback(self, event): y = event.ydata wpm = self.waypoints_model flightlevel = float(pressure2flightlevel(y * units.Pa).magnitude) + # round flightlevel to the nearest multiple of five (legal level) + flightlevel = 5.0 * round(flightlevel / 5) [lat, lon], best_index = self.plotter.get_lat_lon(event, wpm.all_waypoint_data()) loc = find_location(lat, lon) # skipped tolerance which uses appropriate_epsilon_km if loc is not None: From 3a29f6b76427a7cac4e4bc0fe50d0aecfcbc9920 Mon Sep 17 00:00:00 2001 From: "J. Ungermann" Date: Sat, 2 Sep 2023 23:39:05 -0800 Subject: [PATCH 8/9] Change ourairport url to more available and fast location. (#1990) Fix #1989 Co-authored-by: ReimarBauer --- mslib/utils/airdata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mslib/utils/airdata.py b/mslib/utils/airdata.py index b7ae556d2..dc7dfdead 100644 --- a/mslib/utils/airdata.py +++ b/mslib/utils/airdata.py @@ -102,7 +102,7 @@ def get_airports(force_download=False, url=None): _airports_mtime = Airspace().airports_mtime if url is None: - url = "https://ourairports.com/data/airports.csv" + url = "https://davidmegginson.github.io/ourairports-data/airports.csv" file_exists = os.path.exists(os.path.join(OSDIR, "downloads", "aip", "airports.csv")) From 87cda8c525886e1f12be2d2896076b97f950af2c Mon Sep 17 00:00:00 2001 From: ReimarBauer Date: Sun, 3 Sep 2023 20:32:22 +0200 Subject: [PATCH 9/9] prepare 8.3.0 (#1991) --- CHANGES.rst | 9 +++++++++ mslib/version.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 695fcbb07..5e1f2506b 100755 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,15 @@ Changelog ========= +Version 8.3.0 +~~~~~~~~~~~~~ + +Bug fix release and minor enhacements: +We improved the KML docking widget feature + +All changes: +https://github.com/Open-MSS/MSS/milestone/98 + Version 8.2.0 ~~~~~~~~~~~~~ diff --git a/mslib/version.py b/mslib/version.py index b2dfa20e9..877b16c9e 100644 --- a/mslib/version.py +++ b/mslib/version.py @@ -24,4 +24,4 @@ See the License for the specific language governing permissions and limitations under the License. """ -__version__ = u'8.2.0' +__version__ = u'8.3.0'