Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge stable to develop #1995

Merged
merged 10 commits into from
Sep 6, 2023
9 changes: 9 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -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
~~~~~~~~~~~~~

Expand Down
17 changes: 7 additions & 10 deletions mslib/msui/flighttrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -693,30 +693,27 @@ 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:
# All other columns get the standard editor.
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):
"""
Expand Down
14 changes: 10 additions & 4 deletions mslib/msui/kmloverlay_dockwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,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),
Expand All @@ -332,6 +330,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):
Expand Down Expand Up @@ -459,6 +459,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:
Expand All @@ -467,10 +469,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:
Expand All @@ -488,7 +492,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):
"""
Expand All @@ -511,8 +514,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
Expand Down
41 changes: 20 additions & 21 deletions mslib/msui/mpl_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions mslib/msui/mpl_pathinteractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 8 additions & 3 deletions mslib/msui/mscolab.py
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,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()
Expand Down Expand Up @@ -1509,15 +1510,19 @@ def show_categories_to_ui(self, ops=None):
"""
logging.debug('show_categories_to_ui')
if verify_user_token(self.mscolab_server_url, self.token) or ops:
r = None
if ops is not None:
r = ops
else:
data = {
"token": self.token
}
r = requests.get(f'{self.mscolab_server_url}/operations', data=data,
timeout=tuple(config_loader(dataset="MSCOLAB_timeout")))
if r.text != "False":
try:
r = requests.get(f'{self.mscolab_server_url}/operations', data=data,
timeout=tuple(config_loader(dataset="MSCOLAB_timeout")))
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.currentIndexChanged.disconnect(self.operation_category_handler)
Expand Down
4 changes: 2 additions & 2 deletions mslib/msui/mscolab_admin_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ 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}")
self.creatorNameLabel.setText(f"Creator: {creator_name}")
self.operationNameLabel.setText(f"Operation: {self.operation_name}")
self.creatorNameLabel.setText(f"Creator: {creator_name}")
self.usernameLabel.setText(f"Logged In: {self.user['username']}")

def load_import_operations(self):
Expand Down
10 changes: 7 additions & 3 deletions mslib/msui/msui_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
from mslib.msui.updater import UpdaterUI
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
from mslib.utils.qt import get_open_filenames, get_save_filename, show_popup
from mslib.utils.config import read_config_file, config_loader
from PyQt5 import QtGui, QtCore, QtWidgets

Expand Down Expand Up @@ -824,8 +824,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
Expand Down
4 changes: 1 addition & 3 deletions mslib/msui/viewwindows.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion mslib/msui/wms_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -1484,7 +1484,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.get_size_inches() * self.view.plotter.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))
Expand Down
2 changes: 1 addition & 1 deletion mslib/utils/airdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"))

Expand Down
2 changes: 1 addition & 1 deletion mslib/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
6 changes: 5 additions & 1 deletion tests/_test_msui/test_kmloverlay_dockwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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()

Expand All @@ -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()
Expand Down