def read(self, service_url, timeout=config_loader(dataset="WMS_request_timeout")): """Get and parse a WMS capabilities document, returning an elementtree instance service_url is the base url, to which is appended the service, version, and request parameters """ getcaprequest = self.capabilities_url(service_url) # Don't specify a version if it is to be determined getcaprequest = getcaprequest.replace("&version=None", "").replace("?version=None", "") proxies = config_loader(dataset="proxies") # now split it up again to use the generic openURL function... spliturl = getcaprequest.split('?') u = openURL(spliturl[0], spliturl[1], method='Get', auth=self.auth, proxies=proxies) # (mss) Store capabilities document. self.capabilities_document = strip_bom(u.read()) return etree.fromstring(self.capabilities_document)
def test_sample_config_file(self): utils_path = os.path.dirname(os.path.abspath(utils.__file__)) config_file = os.path.join( utils_path, '../', '../', 'docs', 'samples', 'config', 'mss', 'mss_settings.json.sample', ) read_config_file(path=config_file) data = config_loader(dataset="new_flighttrack_flightlevel") assert data == 250 with pytest.raises(KeyError): config_loader(dataset="UNDEFINED") with pytest.raises(KeyError): assert config_loader(dataset="UNDEFINED") with pytest.raises(FileNotFoundError): config_file = os.path.join( utils_path, '../', '../', 'docs', 'samples', 'config', 'mss', 'non_existent_mss_settings.json.sample', ) read_config_file(config_file)
def get_pickertype(tag, typ): default = config_loader(dataset="filepicker_default") if typ is None: if tag is None: typ = default else: typ = config_loader(dataset=tag) return typ
def test_add_user(self): self._connect_to_mscolab() self._create_user("something", "*****@*****.**", "something") assert config_loader( dataset="MSCOLAB_mailid") == "*****@*****.**" assert config_loader(dataset="MSCOLAB_password") == "something" # assert self.window.stackedWidget.currentWidget() == self.window.newuserPage assert self.main_window.usernameLabel.text() == 'something' assert self.main_window.mscolab.connect_window is None
def test_sample_config_file(self): config_file = os.path.join( self.sample_path, 'msui_settings.json.sample', ) read_config_file(path=config_file) data = config_loader(dataset="new_flighttrack_flightlevel") assert data == 250 with pytest.raises(KeyError): config_loader(dataset="UNDEFINED") with pytest.raises(KeyError): assert config_loader(dataset="UNDEFINED")
def _add_hexagon(self): table_view = self.view.tableWayPoints waypoints_model = self.view.waypoints_model params = self._get_parameters() if params["radius"] < 0.01: QtWidgets.QMessageBox.warning( self, "Add hexagon", "You cannot create a hexagon with zero radius!") return points = create_hexagon(params["center_lat"], params["center_lon"], params["radius"], params["angle"], params["direction"] == "clockwise") index = table_view.currentIndex() if not index.isValid(): row = 0 flightlevel = config_loader(dataset="new_flighttrack_flightlevel") else: row = index.row() + 1 flightlevel = waypoints_model.waypoint_data(row - 1).flightlevel waypoints = [] for i, point in enumerate(points): waypoints.append( ft.Waypoint(lon=float(point[1]), lat=float(point[0]), flightlevel=float(flightlevel), comments=f"Hexagon {(i + 1):d}")) waypoints_model.insertRows(row, rows=len(waypoints), waypoints=waypoints) index = waypoints_model.index(row, 0) table_view.setCurrentIndex(index) table_view.resizeRowsToContents()
def __init__(self, lat=0, lon=0, flightlevel=0, location="", comments=""): self.location = location locations = config_loader(dataset='locations') if location in locations: self.lat, self.lon = locations[location] else: self.lat = lat self.lon = lon self.flightlevel = flightlevel self.pressure = thermolib.flightlevel2pressure(flightlevel * units.hft).magnitude self.distance_to_prev = 0. self.distance_total = 0. self.comments = comments # Performance fields (for values read from the flight performance # service). self.leg_time = None # time from previous waypoint self.cum_time = None # total time of flight self.utc_time = None # time in UTC since given takeoff time self.leg_fuel = None # fuel consumption since previous waypoint self.rem_fuel = None # total fuel consumption self.weight = None # aircraft gross weight self.ceiling_alt = None # aircraft ceiling altitude self.ascent_rate = None # aircraft ascent rate self.wpnumber_major = None self.wpnumber_minor = None
def createEditor(self, parent, option, index): """ Create a combobox listing predefined locations in the LOCATION column. """ if index.column() == LOCATION: combobox = QtWidgets.QComboBox(parent) locations = config_loader(dataset='locations') adds = list(locations.keys()) 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) 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 setModelData(self, editor, model, index): """ For the LOCATION column: If the user selects a location from the combobox, get the corresponding coordinates. """ if index.column() == LOCATION: loc = editor.currentText() locations = config_loader(dataset='locations') if loc in locations: lat, lon = locations[loc] # Don't update distances and flight performance twice, hence # set update=False for LAT. model.setData(index.sibling(index.row(), LAT), QtCore.QVariant(lat), update=False) model.setData(index.sibling(index.row(), LON), QtCore.QVariant(lon)) else: for wp in self.parent().waypoints_model.all_waypoint_data(): if loc == wp.location: lat, lon = wp.lat, wp.lon # Don't update distances and flight performance twice, hence # set update=False for LAT. model.setData(index.sibling(index.row(), LAT), QtCore.QVariant(lat), update=False) model.setData(index.sibling(index.row(), LON), QtCore.QVariant(lon)) model.setData(index, QtCore.QVariant(editor.currentText())) else: QtWidgets.QItemDelegate.setModelData(self, editor, model, index)
def changeMapSection(self, index=0, only_kwargs=False): """ Change the current map section to one of the predefined regions. """ # Get the initial projection parameters from the tables in mss_settings. current_map_key = self.cbChangeMapSection.currentText() predefined_map_sections = config_loader( dataset="predefined_map_sections") current_map = predefined_map_sections.get(current_map_key, { "CRS": current_map_key, "map": {} }) proj_params = get_projection_params(current_map["CRS"]) # Create a keyword arguments dictionary for basemap that contains # the projection parameters. kwargs = current_map["map"] kwargs.update({ "CRS": current_map["CRS"], "BBOX_UNITS": proj_params["bbox"], "OPERATION_NAME": self.waypoints_model.name }) kwargs.update(proj_params["basemap"]) if only_kwargs: # Return kwargs dictionary and do NOT redraw the map. return kwargs logging.debug("switching to map section '%s' - '%s'", current_map_key, kwargs) self.mpl.canvas.redraw_map(kwargs) self.mpl.navbar.clear_history()
def __init__(self, url, version=None, xml=None, username=None, password=None, parse_remote_metadata=False, headers=None, timeout=config_loader(dataset="WMS_request_timeout"), auth=None): """Initialize.""" if auth: if username: auth.username = username if password: auth.password = password self.url = url self.version = version self.timeout = timeout self.headers = headers self._capabilities = None self.auth = auth or Authentication(username, password) # Authentication handled by Reader reader = WMSCapabilitiesReader(self.version, url=self.url, headers=headers, auth=self.auth) if xml: # read from stored xml self._capabilities = reader.readString(xml) else: # read from server self._capabilities = reader.read(self.url, timeout=self.timeout) self.request = reader.request if not self.version: self.version = self._capabilities.attrib["version"] if self.version not in ["1.1.1", "1.3.0"]: self.version = "1.1.1" reader.version = self.version self.WMS_NAMESPACE = "{http://www.opengis.net/wms}" if self.version == "1.3.0" else "" self.OGC_NAMESPACE = "{http://www.opengis.net/ogc}" if self.version == "1.3.0" else "" # avoid building capabilities metadata if the # response is a ServiceExceptionReport se = self._capabilities.find('ServiceException') if se is not None: err_message = str(se.text).strip() raise ServiceException(err_message) # (mss) Store capabilities document. self.capabilities_document = reader.capabilities_document # (mss) # build metadata objects self._buildMetadata(parse_remote_metadata)
def test_option_types(self): # check if all config options are added to the appropriate type of options config_keys = set(config_loader(default=True).keys()) option_types = set(mss_default.fixed_dict_options + mss_default.key_value_options + list(mss_default.dict_option_structure.keys()) + list(mss_default.list_option_structure.keys())) assert config_keys == option_types
def test_existing_config_file_different_parameters(self): """ on a user defined mss_settings_json without a defined num_labels this test should return its default value """ create_mss_settings_file('{"num_interpolation_points": 20 }') if not fs.open_fs(MSS_CONFIG_PATH).exists("mss_settings.json"): pytest.skip('undefined test mss_settings.json') with fs.open_fs(MSS_CONFIG_PATH) as file_dir: file_content = file_dir.readtext("mss_settings.json") assert "num_labels" not in file_content default_data = config_loader(default=True) config_file = fs.path.combine(MSS_CONFIG_PATH, "mss_settings.json") read_config_file(path=config_file) data = config_loader() assert data["num_labels"] == default_data["num_labels"] num_labels = config_loader(dataset="num_labels") assert num_labels == default_data["num_labels"] num_interpolation_points = config_loader( dataset="num_interpolation_points") assert num_interpolation_points == 20 assert data["num_interpolation_points"] == 20 with pytest.raises(KeyError): config_loader(dataset="UNDEFINED") with pytest.raises(KeyError): assert config_loader(dataset="UNDEFINED")
def test_add_users_with_updating_credentials_in_config_file( self, mockmessage): create_msui_settings_file( '{"MSCOLAB_mailid": "*****@*****.**", "MSCOLAB_password": "******"}' ) read_config_file() # check current settings assert config_loader( dataset="MSCOLAB_mailid") == "*****@*****.**" assert config_loader(dataset="MSCOLAB_password") == "something" self._connect_to_mscolab() assert self.window.mscolab_server_url is not None self._create_user("anand", "*****@*****.**", "anand") # check changed settings assert config_loader(dataset="MSCOLAB_mailid") == "*****@*****.**" assert config_loader(dataset="MSCOLAB_password") == "anand" # check user is logged in assert self.main_window.usernameLabel.text() == "anand"
def openTool(self, index): """ Slot that handles requests to open tool windows. """ index = self.controlToBeCreated(index) if index >= 0: if index == WMS: # Open a WMS control widget. title = "Web Service Plot Control" widget = wms.LSecWMSControlWidget( default_WMS=config_loader(dataset="default_LSEC_WMS"), waypoints_model=self.waypoints_model, view=self.mpl.canvas, wms_cache=config_loader(dataset="wms_cache")) self.mpl.canvas.waypoints_interactor.signal_get_lsec.connect(widget.call_get_lsec) else: raise IndexError("invalid control index") # Create the actual dock widget containing <widget>. self.createDockWidget(index, title, widget)
def update_predefined_maps(self, extra=None): self.cbChangeMapSection.clear() predefined_map_sections = config_loader( dataset="predefined_map_sections") self.cbChangeMapSection.addItems(sorted( predefined_map_sections.keys())) if extra is not None and len(extra) > 0: self.cbChangeMapSection.insertSeparator( self.cbChangeMapSection.count()) self.cbChangeMapSection.addItems(sorted(extra))
def test_modify_config_file_with_existing_parameters(self): """ Test to check if modify_config_file properly modifies a key-value pair in the config file """ create_msui_settings_file('{"MSCOLAB_mailid": "*****@*****.**"}') if not fs.open_fs(MSUI_CONFIG_PATH).exists("msui_settings.json"): pytest.skip('undefined test msui_settings.json') data_to_save_in_config_file = {"MSCOLAB_mailid": "*****@*****.**"} modify_config_file(data_to_save_in_config_file) config_file = fs.path.combine(MSUI_CONFIG_PATH, "msui_settings.json") read_config_file(path=config_file) data = config_loader() assert data["MSCOLAB_mailid"] == "*****@*****.**"
def update_predefined_maps(self, extra=None): current_map_key = self.cbChangeMapSection.currentText() self.cbChangeMapSection.clear() predefined_map_sections = config_loader( dataset="predefined_map_sections") self.cbChangeMapSection.addItems(sorted( predefined_map_sections.keys())) if extra is not None and len(extra) > 0: self.cbChangeMapSection.insertSeparator( self.cbChangeMapSection.count()) self.cbChangeMapSection.addItems(sorted(extra)) # set initial map key again if current_map_key in predefined_map_sections.keys(): self.cbChangeMapSection.setCurrentText(current_map_key)
def openTool(self, index): """ Slot that handles requests to open control windows. """ index = self.controlToBeCreated(index) if index >= 0: if index == WMS: # Create a new WMSDockWidget. title = "Web Map Service (Top View)" widget = wc.HSecWMSControlWidget( default_WMS=config_loader(dataset="default_WMS"), view=self.mpl.canvas, wms_cache=config_loader(dataset="wms_cache")) widget.signal_disable_cbs.connect(self.disable_cbs) widget.signal_enable_cbs.connect(self.enable_cbs) elif index == SATELLITE: title = "Satellite Track Prediction" widget = sat.SatelliteControlWidget(parent=self, view=self.mpl.canvas) elif index == REMOTESENSING: title = "Remote Sensing Tools" widget = rs.RemoteSensingControlWidget(parent=self, view=self.mpl.canvas) elif index == KMLOVERLAY: title = "KML Overlay" widget = kml.KMLOverlayControlWidget(parent=self, view=self.mpl.canvas) elif index == AIRDATA: title = "Airdata" widget = ad.AirdataDockwidget(parent=self, view=self.mpl.canvas) else: raise IndexError("invalid control index") # Create the actual dock widget containing <widget>. self.createDockWidget(index, title, widget)
def find_location(lat, lon, tolerance=5): """ Checks if a location is present at given coordinates :param lat: latitude :param lon: longitude :param tolerance: maximum distance between location and coordinates in km :return: None or lat/lon, name """ locations = config_loader(dataset='locations') distances = sorted([(get_distance(lat, lon, loc_lat, loc_lon), loc) for loc, (loc_lat, loc_lon) in locations.items()]) if len(distances) > 0 and distances[0][0] <= tolerance: return locations[distances[0][1]], distances[0][1] else: return None
def __init__(self, token, op_id, user, operation_name, conn, parent=None, mscolab_server_url=config_loader(dataset="default_MSCOLAB")): """ token: access_token op_id: operation id user: logged in user operation_name: name of operation, conn: socket connection parent: parent of widget mscolab_server_url: server url of mscolab """ super(MSColabVersionHistory, self).__init__(parent) self.setupUi(self) # Initialise Variables self.token = token self.op_id = op_id self.user = user self.operation_name = operation_name self.conn = conn self.mscolab_server_url = mscolab_server_url # Event handlers self.refreshBtn.clicked.connect(self.handle_refresh) self.checkoutBtn.clicked.connect(self.handle_undo) self.nameVersionBtn.clicked.connect(self.handle_named_version) self.deleteVersionNameBtn.clicked.connect( self.handle_delete_version_name) self.versionFilterCB.currentIndexChanged.connect( lambda: self.load_all_changes()) self.changes.currentItemChanged.connect(self.preview_change) # Setup UI self.deleteVersionNameBtn.setVisible(False) self.set_label_text() self.set_change_list_style() self.toggle_version_buttons(False) self.load_current_waypoints() self.load_all_changes()
def verify_user_token(mscolab_server_url, token): if config_loader(dataset="mscolab_skip_verify_user_token"): return True data = {"token": token} try: r = requests.get(f'{mscolab_server_url}/test_authorized', data=data) except requests.exceptions.SSLError: logging.debug("Certificate Verification Failed") return False except requests.exceptions.InvalidSchema: logging.debug("Invalid schema of url") return False except requests.exceptions.ConnectionError as ex: logging.error("unexpected error: %s %s", type(ex), ex) return False except requests.exceptions.MissingSchema as ex: # self.mscolab_server_url can be None?? logging.error("unexpected error: %s %s", type(ex), ex) return False return r.text == "True"
def test_existing_empty_config_file(self): """ on a user defined empty mss_settings_json this test should return the default value for num_labels """ create_mss_settings_file('{ }') if not fs.open_fs(MSS_CONFIG_PATH).exists("mss_settings.json"): pytest.skip('undefined test mss_settings.json') with fs.open_fs(MSS_CONFIG_PATH) as file_dir: file_content = file_dir.readtext("mss_settings.json") assert ":" not in file_content default_data = config_loader(default=True) config_file = fs.path.combine(MSS_CONFIG_PATH, "mss_settings.json") read_config_file(path=config_file) data = config_loader() assert data["num_labels"] == default_data["num_labels"] num_labels = config_loader(dataset="num_labels") assert num_labels == default_data["num_labels"] with pytest.raises(KeyError): config_loader(dataset="UNDEFINED") with pytest.raises(KeyError): assert config_loader(dataset="UNDEFINED")
def __init__(self, token, op_id, user, operation_name, access_level, conn, parent=None, mscolab_server_url=config_loader(dataset="default_MSCOLAB")): """ token: access_token op_id: operation id user: logged in user operation_name: active operation name, access_level: access level of user logged in conn: to send messages, recv messages, if a direct slot-signal can't be setup to be connected at parents' parent: widget parent mscolab_server_url: server url for mscolab """ super(MSColabChatWindow, self).__init__(parent) self.setupUi(self) self.mscolab_server_url = mscolab_server_url self.token = token self.user = user self.op_id = op_id self.operation_name = operation_name self.conn = conn self.access_level = access_level self.text = "" self.attachment = None self.attachment_type = None self.active_edit_id = None self.active_message_reply = None self.current_search_index = None self.markdown = Markdown( extensions=['nl2br', 'sane_lists', DeregisterSyntax()]) self.messageText = MessageTextEdit(self.centralwidget) self.setup_message_text() # Signals self.searchMessageLineEdit.textChanged.connect( self.handle_search_text_changed) self.searchPrevBtn.clicked.connect(self.handle_prev_message_search) self.searchNextBtn.clicked.connect(self.handle_next_message_search) self.previewBtn.clicked.connect(self.toggle_preview) self.sendMessageBtn.clicked.connect(self.send_message) self.uploadBtn.clicked.connect(self.handle_upload) self.editMessageBtn.clicked.connect(self.edit_message) self.cancelBtn.clicked.connect(self.send_message_state) # Socket Connection handlers self.conn.signal_operation_permissions_updated.connect( self.handle_permissions_updated) self.conn.signal_message_receive.connect(self.handle_incoming_message) self.conn.signal_message_reply_receive.connect( self.handle_incoming_message_reply) self.conn.signal_message_edited.connect(self.handle_message_edited) self.conn.signal_message_deleted.connect(self.handle_deleted_message) # Set Label text self.set_label_text() # Hide Edit Message section self.send_message_state() # load all users self.load_users() # load messages self.load_all_messages() if access_level == "viewer": self.messageText.setEnabled(False) self.previewBtn.setEnabled(False) self.uploadBtn.setEnabled(False) self.sendMessageBtn.setEnabled(False)
from mslib.msui.mss_qt import get_open_filename, get_save_filename, show_popup from mslib.msui.mss_qt import ui_configuration_editor_window as ui_conf from PyQt5 import QtWidgets, QtCore, QtGui from mslib.msui.constants import MSS_SETTINGS from mslib.msui.icons import icons from mslib.utils.config import MissionSupportSystemDefaultConfig as mss_default from mslib.utils.config import config_loader, dict_raise_on_duplicates_empty, merge_data from mslib.support.qt_json_view import delegate from mslib.support.qt_json_view.view import JsonView from mslib.support.qt_json_view.model import JsonModel from mslib.support.qt_json_view.datatypes import match_type, DataType, TypeRole, ListType InvalidityRole = TypeRole + 1 DummyRole = TypeRole + 2 default_options = config_loader(default=True) def get_root_index(index, parents=False): parent_list = [] while index.parent().isValid(): index = index.parent() parent_list.append(index) parent_list.reverse() if parents: return index, parent_list return index class JsonDelegate(delegate.JsonDelegate): def paint(self, painter, option, index):
def __init__(self, parent=None): super(ConfigurationEditorWindow, self).__init__(parent) self.setupUi(self) options = config_loader() self.path = MSS_SETTINGS self.last_saved = copy.deepcopy(options) self.optCb.addItem("All") for option in sorted(options.keys(), key=str.lower): self.optCb.addItem(option) # Create view and place in parent widget self.view = JsonView() self.view.setItemDelegate(JsonDelegate()) self.view.setSelectionMode( QtWidgets.QAbstractItemView.ExtendedSelection) self.jsonWidget.setLayout(QtWidgets.QVBoxLayout()) self.jsonWidget.layout().setContentsMargins(0, 0, 0, 0) self.jsonWidget.layout().addWidget(self.view) # Create proxy model for filtering self.proxy_model = JsonSortFilterProxyModel() self.json_model = JsonModel(data=options, editable_keys=True, editable_values=True) self.json_model.setHorizontalHeaderLabels(['Option', 'Value']) # Set view model self.proxy_model.setSourceModel(self.json_model) self.view.setModel(self.proxy_model) # Setting proxy model and view attributes self.proxy_model.setFilterKeyColumn(0) # Add actions to toolbar self.import_file_action = QtWidgets.QAction( QtGui.QIcon(icons("config_editor", "Folder-new.svg")), "Import config", self) self.import_file_action.setStatusTip( "Import an external configuration file") self.toolBar.addAction(self.import_file_action) self.save_file_action = QtWidgets.QAction( QtGui.QIcon(icons("config_editor", "Document-save.svg")), "Save config", self) self.save_file_action.setStatusTip("Save current configuration") self.toolBar.addAction(self.save_file_action) self.export_file_action = QtWidgets.QAction( QtGui.QIcon(icons("config_editor", "Document-save-as.svg")), "Export config", self) self.export_file_action.setStatusTip("Export current configuration") self.toolBar.addAction(self.export_file_action) # Button signals self.optCb.currentIndexChanged.connect(self.set_option_filter) self.addOptBtn.clicked.connect(self.add_option_handler) self.removeOptBtn.clicked.connect(self.remove_option_handler) self.restoreDefaultsBtn.clicked.connect(self.restore_defaults) self.moveUpTb.clicked.connect(lambda: self.move_option(move=1)) self.moveDownTb.clicked.connect(lambda: self.move_option(move=-1)) # File action signals self.import_file_action.triggered.connect(self.import_config) self.save_file_action.triggered.connect(self.save_config) self.export_file_action.triggered.connect(self.export_config) # View/Model signals self.view.selectionModel().selectionChanged.connect( self.tree_selection_changed) self.json_model.dataChanged.connect(self.update_view) # set behaviour of widgets self.moveUpTb.hide() self.moveDownTb.hide() self.moveUpTb.setAutoRaise(True) self.moveUpTb.setArrowType(QtCore.Qt.UpArrow) self.moveUpTb.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly) self.moveDownTb.setAutoRaise(True) self.moveDownTb.setArrowType(QtCore.Qt.DownArrow) self.moveDownTb.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly) self.moveUpTb.setEnabled(False) self.moveDownTb.setEnabled(False) self.addOptBtn.setEnabled(False) self.removeOptBtn.setEnabled(False) self.restoreDefaultsBtn.setEnabled(False) # set tooltip and make keys non-editable self.set_noneditable_items(QtCore.QModelIndex()) # json view attributes self.view.setAlternatingRowColors(True) self.view.setColumnWidth(0, self.view.width() // 2) # Add invalidity roles and update status of keys self.update_view() self.restart_on_save = True
def test_default_config(self): data = config_loader(default=True) assert isinstance(data, dict) assert data == config_loader(default=True) assert data["num_labels"] == 10 assert data["num_interpolation_points"] == 201
def test_default_config_dataset(self): default_data = config_loader(default=True) num_labels = config_loader(dataset="num_labels", default=True) assert num_labels == default_data["num_labels"]
def main(): parser = argparse.ArgumentParser(description=""" This script automatically retrieves and stores a set of plots for the configured flights. The configuration is placed within the normal MSS frontend JSON file. E.g. "automated_plotting": { "flights": [ ["ST25", "01 SADPAP (stereo)", "500,50", "ST25-joern.ftml", "2019-07-01T00:00:00Z", "2019-09-01T12:00:00Z"] ], "hsecs": [ ["https://mss-server/campaigns2019", "ecmwf.PVTropo01", "default", "4.0"], ["https://mss-server/campaigns2019", "ecmwf.ertel_potential_vorticity_pl", "ertel_potential_vorticity_bh", "200.0"] ], "vsecs": [ ["https://mss-server/campaigns2019", "ecmwf.VS_ertel_potential_vorticity_ml", "ertel_potential_vorticity_bh"], ["https://mss-server/campaigns2019", "ecmwf.TroposphereInversionLayer", ""] ] } will plot flight "ST25" with configured map section "01 SADPAP (stereo)" and vertical range 500hPa to 50hPa from the given FTML file for init time "2019-07-01T00:00:00Z" and valid time "2019-09-01T12:00:00Z". The plots are defined in the hsecs (horizontal cross-sections) and vsecs (vertical cross-sections) entries given each the URL of the server, the layer name, the style, and, for hsec only, the elevation to plot (if necessary). """) parser.add_argument("-v", "--version", help="show version", action="store_true", default=False) parser.add_argument("--debug", help="show debugging log messages on console", action="store_true", default=False) parser.add_argument( "--logfile", help="Specify logfile location. Set to empty string to disable.", action="store", default=os.path.join(mslib.msui.constants.MSUI_CONFIG_PATH, "msui.log")) args = parser.parse_args() if args.version: print( "***********************************************************************" ) print("\n Mission Support System (mss_retriever)\n") print( "***********************************************************************" ) print("Documentation: http://mss.rtfd.io") print("Version:", mslib.__version__) sys.exit() mslib.utils.setup_logging(args) read_config_file(path=mslib.msui.constants.MSUI_SETTINGS) config = config_loader() num_interpolation_points = config["num_interpolation_points"] num_labels = config["num_labels"] tick_index_step = num_interpolation_points // num_labels fig = plt.figure() for flight, section, vertical, filename, init_time, time in \ config["automated_plotting"]["flights"]: params = mslib.utils.coordinate.get_projection_params( config["predefined_map_sections"][section]["CRS"].lower()) params["basemap"].update( config["predefined_map_sections"][section]["map"]) wps = load_from_ftml(filename) wp_lats, wp_lons, wp_locs = [[x[i] for x in wps] for i in [0, 1, 3]] wp_presss = [ thermolib.flightlevel2pressure(wp[2] * units.hft).magnitude for wp in wps ] for url, layer, style, elevation in config["automated_plotting"][ "hsecs"]: fig.clear() ax = fig.add_subplot(111, zorder=99) bm = mslib.msui.mpl_map.MapCanvas(ax=ax, **(params["basemap"])) # plot path and labels bm.plot(wp_lons, wp_lats, color="blue", marker="o", linewidth=2, markerfacecolor="red", latlon=True, markersize=4, zorder=100) for i, (lon, lat, loc) in enumerate(zip(wp_lons, wp_lats, wp_locs)): textlabel = f"{loc if loc else str(i)} " x, y = bm(lon, lat) plt.text(x, y, textlabel, **TEXT_CONFIG) plt.tight_layout() # retrieve and draw WMS image ax_bounds = plt.gca().bbox.bounds width, height = int(round(ax_bounds[2])), int(round(ax_bounds[3])) bbox = params['basemap'] req = requests.get( url, auth=tuple(config["WMS_login"][url]), params={ "version": "1.3.0", "request": "GetMap", "format": "image/png", "exceptions": "XML", "crs": config["predefined_map_sections"][section]["CRS"], "layers": layer, "styles": style, "elevation": elevation, "dim_init_time": init_time, "time": time, "width": width, "height": height, "bbox": f"{bbox['llcrnrlat']},{bbox['llcrnrlon']},{bbox['urcrnrlat']},{bbox['urcrnrlon']}" }) if req.headers['Content-Type'] == "text/xml": print(flight, section, vertical, filename, init_time, time) print(url, layer, style, elevation) print("WMS Error:") print(req.text) exit(1) image_io = io.BytesIO(req.content) img = PIL.Image.open(image_io) bm.imshow(img, interpolation="nearest", origin="upper") bm.drawcoastlines() bm.drawcountries() fig.savefig(f"{flight}_{layer}.png") # prepare vsec plots path = [(wp[0], wp[1], datetime.datetime.now()) for wp in wps] lats, lons = mslib.utils.coordinate.path_points( [_x[0] for _x in path], [_x[1] for _x in path], numpoints=num_interpolation_points + 1, connection="greatcircle") intermediate_indexes = [] ipoint = 0 for i, (lat, lon) in enumerate(zip(lats, lons)): if abs(lat - wps[ipoint][0]) < 1E-10 and abs( lon - wps[ipoint][1]) < 1E-10: intermediate_indexes.append(i) ipoint += 1 if ipoint >= len(wps): break for url, layer, style in config["automated_plotting"]["vsecs"]: fig.clear() # setup ticks and labels ax = fig.add_subplot(111, zorder=99) ax.set_yscale("log") p_bot, p_top = [float(x) * 100 for x in vertical.split(",")] bbox = ",".join( str(x) for x in (num_interpolation_points, p_bot / 100, num_labels, p_top / 100)) ax.grid(visible=True) ax.patch.set_facecolor("None") pres_maj = mslib.msui.mpl_qtwidget.MplSideViewCanvas._pres_maj pres_min = mslib.msui.mpl_qtwidget.MplSideViewCanvas._pres_min major_ticks = pres_maj[(pres_maj <= p_bot) & (pres_maj >= p_top)] minor_ticks = pres_min[(pres_min <= p_bot) & (pres_min >= p_top)] labels = [ f"{int(_mt / 100)}" if (_mt / 100.) - int(_mt / 100.) == 0 else f"{float(_mt / 100)}" for _mt in major_ticks ] if len(labels) > 20: labels = [ "" if _x.split(".")[-1][0] in "975" else _x for _x in labels ] elif len(labels) > 10: labels = [ "" if _x.split(".")[-1][0] in "9" else _x for _x in labels ] ax.set_ylabel("pressure (hPa)") ax.set_yticks(minor_ticks, minor=True) ax.set_yticks(major_ticks, minor=False) ax.set_yticklabels([], minor=True, fontsize=10) ax.set_yticklabels(labels, minor=False, fontsize=10) ax.set_ylim(p_bot, p_top) ax.set_xlim(0, num_interpolation_points) ax.set_xticks(range(0, num_interpolation_points, tick_index_step)) ax.set_xticklabels([ f"{x[0]:2.1f}, {x[1]:2.1f}" for x in zip(lats[::tick_index_step], lons[::tick_index_step]) ], rotation=25, fontsize=10, horizontalalignment="right") ax.set_xlabel("lat/lon") # plot path and waypoint labels ax.plot(intermediate_indexes, wp_presss, color="blue", marker="o", linewidth=2, markerfacecolor="red", markersize=4) for i, (idx, press, loc) in enumerate( zip(intermediate_indexes, wp_presss, wp_locs)): textlabel = f"{loc if loc else str(i)} " plt.text(idx + 1, press, textlabel, rotation=90, **TEXT_CONFIG) plt.tight_layout() # retrieve and draw WMS image ax_bounds = plt.gca().bbox.bounds width, height = int(round(ax_bounds[2])), int(round(ax_bounds[3])) req = requests.get(url, auth=tuple(config["WMS_login"][url]), params={ "version": "1.3.0", "request": "GetMap", "format": "image/png", "exceptions": "XML", "crs": "VERT:LOGP", "layers": layer, "styles": style, "dim_init_time": init_time, "time": time, "width": width, "height": height, "path": ",".join(f"{wp[0]:.2f},{wp[1]:.2f}" for wp in wps), "bbox": bbox }) if req.headers['Content-Type'] == "text/xml": print(flight, section, vertical, filename, init_time, time) print(url, layer, style) print("WMS Error:") print(req.text) exit(1) image_io = io.BytesIO(req.content) img = PIL.Image.open(image_io) imgax = fig.add_axes(ax.get_position(), frameon=True, xticks=[], yticks=[], label="ax2", zorder=0) imgax.imshow(img, interpolation="nearest", aspect="auto", origin="upper") imgax.set_xlim(0, img.size[0] - 1) imgax.set_ylim(img.size[1] - 1, 0) plt.savefig(f"{flight}_{layer}.png")
def __init__(self, token, op_id, user, operation_name, operations, conn, parent=None, mscolab_server_url=config_loader(dataset="default_MSCOLAB")): """ token: access token op_id: operation id conn: connection to send/receive socket messages """ super(MSColabAdminWindow, self).__init__(parent) self.setupUi(self) self.mscolab_server_url = mscolab_server_url self.token = token self.op_id = op_id self.user = user self.operation_name = operation_name self.operations = operations self.conn = conn self.addUsers = [] self.modifyUsers = [] # Button click handlers self.addUsersBtn.clicked.connect(self.add_selected_users) self.modifyUsersBtn.clicked.connect(self.modify_selected_users) self.deleteUsersBtn.clicked.connect(self.delete_selected_users) self.importPermissionsBtn.clicked.connect(self.import_permissions) self.selectAllAddBtn.clicked.connect( lambda: self.select_all(self.addUsersTable)) self.deselectAllAddBtn.clicked.connect( lambda: self.deselect_all(self.addUsersTable)) self.selectAllModifyBtn.clicked.connect( lambda: self.select_all(self.modifyUsersTable)) self.deselectAllModifyBtn.clicked.connect( lambda: self.deselect_all(self.modifyUsersTable)) index = self.addUsersPermission.findText("collaborator", QtCore.Qt.MatchFixedString) if index >= 0: self.addUsersPermission.setCurrentIndex(index) # Search filter self.addUsersSearch.textChanged.connect( lambda text: self.search_user_filter(text, self.addUsersTable)) self.modifyUsersSearch.textChanged.connect( lambda text: self.search_user_filter(text, self.modifyUsersTable)) self.modifyUsersPermissionFilter.currentTextChanged.connect( self.apply_permission_filter) # Setting handlers for connection manager self.conn.signal_operation_permissions_updated.connect( self.handle_permissions_updated) self.set_label_text() self.load_import_operations() self.load_users_without_permission() self.load_users_with_permission()