Skip to content

Commit

Permalink
Merge branch 'develop' into i899
Browse files Browse the repository at this point in the history
  • Loading branch information
ReimarBauer authored May 29, 2021
2 parents 7727b6b + af5d754 commit 6dc9e4f
Show file tree
Hide file tree
Showing 48 changed files with 2,060 additions and 141 deletions.
29 changes: 29 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
Changelog
=========

Version 4.0.0
-------------

This release brings a new view mode of 1-D samples along the flight path.
Also in sideview waypoint symbols and corresponding vertical lines can be switched off.
A lot of UI improvements, e.g. import and export filters, topview history and home button implemented
for steorographic projections

HINT:
~~~~~

For using the 1-D samples along the flight path you have to add a configuration to your
mss_wms_settings.py. Similiar as to the other layers add:

.. code-block:: python
register_linear_layers = None
if mpl_lsec_styles is not None:
register_linear_layers = [
# ECMWF standard 1D section styles.
(mpl_lsec_styles.LS_DefaultStyle, "air_temperature", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "divergence_of_wind", ["ecmwf_EUR_LL015"]),
]
All changes:
https://github.com/Open-MSS/MSS/milestone/52?closed=1


Version 3.0.4
-------------

Expand Down
6 changes: 3 additions & 3 deletions docs/samples/config/mss/performance_simple.json.sample
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "DUMMY",
"takeoff_weight": 91000,
"fuel": 35000,
"empty_weight": 56000,
"climb": [[0.00, 0.0, 0.0, 0.0, 0.0]],
"descent": [[0.00, 0.0, 0.0, 0.0, 0.0]],
"cruise": [[0.00, 0.00, 400, 2900.00]],
"ceiling": [410],
"_comment1": "_comment fields are just for self-documentation!",
"_comment2": "takeoff_weight: weight(lbs)",
"_comment3": "fuel: weight(lbs)",
"_comment2": "takeoff_weight: maximum weight for takeoff (lbs)",
"_comment3": "empty_weight: aircraft weight without fuel (lbs)",
"_comment4": "climb/descent: weight(lbs), altitude(ft), duration(min), distance(nm), fuel(lbs)",
"_comment5": "cruise: weight(lbs), altitude(ft), total air speed(nm/h), fuel flow(lbs/h)"
"_comment6": "ceiling: polynomial coeficients for relating weight (lbs) with peak flightlevel (hft). Leftmost coefficient is the intercept."
Expand Down
9 changes: 5 additions & 4 deletions docs/samples/config/wms/mss_chem_plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from mslib.mswms.mpl_hsec import MPLBasemapHorizontalSectionStyle
from mslib.mswms.mpl_vsec import AbstractVerticalSectionStyle
from mslib.mswms.utils import get_style_parameters, get_cbar_label_format
from mslib.utils import convert_to
from mslib import thermolib


Expand Down Expand Up @@ -167,7 +168,6 @@ class fnord(HS_MSSChemStyle):
for vert in ["ml"]:
for stdname, props in MSSChemTargets.items():
name, qty, units, scale = props
# ToDo string substitution
key = "HS_MSSChemStyle_" + vert.upper() + "_" + name + "_" + qty + "_pcontours"
globals()[key] = make_msschem_hs_class(
stdname, name, vert, units, scale, add_data=[(vert, "air_pressure", None)],
Expand Down Expand Up @@ -220,14 +220,15 @@ def _prepare_datafields(self):
# anyways, so we do a poor-man's on-the-fly conversion here.
if 'air_pressure' not in self.data:
self.data["air_pressure"] = np.empty_like(self.data[self.dataname])
flightlevel = 3.28083989501 / 100. * self.driver.vert_data[::-self.driver.vert_order, np.newaxis]
self.data['air_pressure'][:] = thermolib.flightlevel2pressure_a(flightlevel)
self.data['air_pressure'][:] = thermolib.flightlevel2pressure_a(convert_to(
self.driver.vert_data[::-self.driver.vert_order, np.newaxis],
self.driver.vert_units, "hft"))

def _plot_style(self):
ax = self.ax
curtain_cc = self.data[self.dataname] * self.unit_scale
curtain_cc = np.ma.masked_invalid(curtain_cc)
curtain_p = self.data["air_pressure"] # TODO* 100
curtain_p = self.data["air_pressure"]

numlevel = curtain_p.shape[0]
numpoints = len(self.lats)
Expand Down
25 changes: 25 additions & 0 deletions docs/samples/config/wms/mss_wms_settings.py.chem_plots
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import sys
import mslib.mswms.dataaccess
from mslib.mswms import mpl_hsec_styles
from mslib.mswms import mpl_vsec_styles
from mslib.mswms import mpl_lsec_styles
import mslib.mswms
import mss_chem_plots

Expand Down Expand Up @@ -138,3 +139,27 @@ if mpl_vsec_styles is not None:
(mpl_vsec_styles.VS_TemperatureStyle_01, ["ecmwf_EUR_LL015"])
(mss_chem_plots.VS_MSSChemStyle_PL_NH3_nfrac, ["ecmwf_EUR_LL015"]),
]


#
# Registration of linear layers.
#
# The same as above, but for 1D sections.
register_linear_layers = None
if mpl_lsec_styles is not None:
register_linear_layers = [
# ECMWF standard 1D section styles.
(mpl_lsec_styles.LS_DefaultStyle, "air_temperature", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "divergence_of_wind", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "mole_fraction_of_ozone_in_air", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "air_potential_temperature", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "geopotential_height", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "specific_humidity", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "cloud_area_fraction_in_atmosphere_layer", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "specific_cloud_ice_water_content", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "specific_cloud_liquid_water_content", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "ertel_potential_vorticity", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_RelativeHumdityStyle_01, ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_HorizontalVelocityStyle_01, ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_VerticalVelocityStyle_01, ["ecmwf_EUR_LL015"])
]
25 changes: 25 additions & 0 deletions docs/samples/config/wms/mss_wms_settings.py.demodata
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import sys
import mslib.mswms.dataaccess
from mslib.mswms import mpl_hsec_styles
from mslib.mswms import mpl_vsec_styles
from mslib.mswms import mpl_lsec_styles
import mslib.mswms


Expand Down Expand Up @@ -134,3 +135,27 @@ if mpl_vsec_styles is not None:
(mpl_vsec_styles.VS_SpecificHumdityStyle_01, ["ecmwf_EUR_LL015"]),
(mpl_vsec_styles.VS_TemperatureStyle_01, ["ecmwf_EUR_LL015"])
]


#
# Registration of linear layers.
#
# The same as above, but for 1D sections.
register_linear_layers = None
if mpl_lsec_styles is not None:
register_linear_layers = [
# ECMWF standard 1D section styles.
(mpl_lsec_styles.LS_DefaultStyle, "air_temperature", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "divergence_of_wind", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "mole_fraction_of_ozone_in_air", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "air_potential_temperature", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "geopotential_height", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "specific_humidity", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "cloud_area_fraction_in_atmosphere_layer", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "specific_cloud_ice_water_content", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "specific_cloud_liquid_water_content", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "ertel_potential_vorticity", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_RelativeHumdityStyle_01, ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_HorizontalVelocityStyle_01, ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_VerticalVelocityStyle_01, ["ecmwf_EUR_LL015"])
]
27 changes: 27 additions & 0 deletions docs/samples/config/wms/mss_wms_settings.py.sample
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ os.chdir(MSSCONFIGPATH)
import mslib.mswms.dataaccess
from mslib.mswms import mpl_hsec_styles
from mslib.mswms import mpl_vsec_styles
from mslib.mswms import mpl_lsec_styles
import mslib.mswms


Expand Down Expand Up @@ -205,6 +206,32 @@ register_vertical_layers = [
]


#
# Registration of linear layers.
#

# The same as above, but for 1D sections.

register_linear_layers = None
if mpl_lsec_styles is not None:
register_linear_layers = [
# ECMWF standard 1D section styles.
(mpl_lsec_styles.LS_DefaultStyle, "air_temperature", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "divergence_of_wind", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "mole_fraction_of_ozone_in_air", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "air_potential_temperature", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "geopotential_height", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "specific_humidity", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "cloud_area_fraction_in_atmosphere_layer", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "specific_cloud_ice_water_content", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "specific_cloud_liquid_water_content", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_DefaultStyle, "ertel_potential_vorticity", ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_RelativeHumdityStyle_01, ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_HorizontalVelocityStyle_01, ["ecmwf_EUR_LL015"]),
(mpl_lsec_styles.LS_VerticalVelocityStyle_01, ["ecmwf_EUR_LL015"])
]


#
# Server settings. ###
#
Expand Down
5 changes: 5 additions & 0 deletions mslib/msui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class MissionSupportSystemDefaultConfig(object):
# layout of different views, with immutable they can't resized
layout = {"topview": (963, 702),
"sideview": (913, 557),
"linearview": (913, 557),
"tableview": (1236, 424),
"immutable": False}

Expand Down Expand Up @@ -103,6 +104,10 @@ class MissionSupportSystemDefaultConfig(object):
"http://localhost:8081/"
]

default_LSEC_WMS = [
"http://localhost:8081/"
]

# URLs of default mscolab servers
default_MSCOLAB = [
"http://localhost:8083",
Expand Down
134 changes: 134 additions & 0 deletions mslib/msui/_tests/test_linearview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
"""
mslib.msui._tests.test_linearview
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This module provides pytest functions to tests msui.linearview
This file is part of mss.
:copyright: Copyright 2021 May Bär
:copyright: Copyright 2021 by the mss team, see AUTHORS.
:license: APACHE-2.0, see LICENSE for details.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import mock
import os
import pytest
import shutil
import sys
import multiprocessing
import tempfile
from mslib.mswms.mswms import application
from PyQt5 import QtWidgets, QtTest, QtCore
from mslib.msui import flighttrack as ft
import mslib.msui.linearview as tv
from mslib._tests.utils import wait_until_signal

PORTS = list(range(8106, 8107))


class Test_MSSLinearViewWindow(object):
def setup(self):
self.application = QtWidgets.QApplication(sys.argv)
initial_waypoints = [ft.Waypoint(40., 25., 300), ft.Waypoint(60., -10., 400), ft.Waypoint(40., 10, 300)]

waypoints_model = ft.WaypointsTableModel("")
waypoints_model.insertRows(
0, rows=len(initial_waypoints), waypoints=initial_waypoints)

self.window = tv.MSSLinearViewWindow(model=waypoints_model)
self.window.show()
QtWidgets.QApplication.processEvents()
QtTest.QTest.qWaitForWindowExposed(self.window)
QtWidgets.QApplication.processEvents()

def teardown(self):
self.window.hide()
QtWidgets.QApplication.processEvents()
self.application.quit()
QtWidgets.QApplication.processEvents()

@mock.patch("PyQt5.QtWidgets.QMessageBox")
def test_open_wms(self, mockbox):
self.window.cbTools.currentIndexChanged.emit(1)
QtWidgets.QApplication.processEvents()
assert mockbox.critical.call_count == 0

@mock.patch("PyQt5.QtWidgets.QMessageBox")
def test_mouse_over(self, mockbox):
# Test mouse over
QtTest.QTest.mouseMove(self.window.mpl.canvas, QtCore.QPoint(782, 266), -1)
QtWidgets.QApplication.processEvents()
QtTest.QTest.mouseMove(self.window.mpl.canvas, QtCore.QPoint(20, 20), -1)
QtWidgets.QApplication.processEvents()


@pytest.mark.skipif(os.name == "nt",
reason="multiprocessing needs currently start_method fork")
class Test_LinearViewWMS(object):
def setup(self):
self.application = QtWidgets.QApplication(sys.argv)
self.port = PORTS.pop()
self.tempdir = tempfile.mkdtemp()
if not os.path.exists(self.tempdir):
os.mkdir(self.tempdir)
self.thread = multiprocessing.Process(
target=application.run,
args=("127.0.0.1", self.port))
self.thread.start()

initial_waypoints = [ft.Waypoint(40., 25., 0), ft.Waypoint(60., -10., 0), ft.Waypoint(40., 10, 0)]
waypoints_model = ft.WaypointsTableModel("")
waypoints_model.insertRows(
0, rows=len(initial_waypoints), waypoints=initial_waypoints)
self.window = tv.MSSLinearViewWindow(model=waypoints_model)
self.window.show()
QtWidgets.QApplication.processEvents()
QtTest.QTest.qWait(2000)
QtTest.QTest.qWaitForWindowExposed(self.window)
QtWidgets.QApplication.processEvents()
self.window.cbTools.currentIndexChanged.emit(1)
QtWidgets.QApplication.processEvents()
self.wms_control = self.window.docks[0].widget()
self.wms_control.multilayers.cbWMS_URL.setEditText("")

def teardown(self):
self.window.hide()
QtWidgets.QApplication.processEvents()
self.application.quit()
QtWidgets.QApplication.processEvents()
shutil.rmtree(self.tempdir)
self.thread.terminate()

def query_server(self, url):
QtWidgets.QApplication.processEvents()
QtTest.QTest.keyClicks(self.wms_control.multilayers.cbWMS_URL, url)
QtWidgets.QApplication.processEvents()
QtTest.QTest.mouseClick(self.wms_control.multilayers.btGetCapabilities, QtCore.Qt.LeftButton)
QtWidgets.QApplication.processEvents()
wait_until_signal(self.wms_control.cpdlg.canceled)

@mock.patch("PyQt5.QtWidgets.QMessageBox")
def test_server_getmap(self, mockbox):
"""
assert that a getmap call to a WMS server displays an image
"""
self.query_server(f"http://127.0.0.1:{self.port}")
QtTest.QTest.mouseClick(self.wms_control.btGetMap, QtCore.Qt.LeftButton)
QtWidgets.QApplication.processEvents()
wait_until_signal(self.wms_control.image_displayed)
assert mockbox.critical.call_count == 0
2 changes: 1 addition & 1 deletion mslib/msui/_tests/test_wms_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from mslib._tests.utils import wait_until_signal


PORTS = list(range(8106, 8120))
PORTS = list(range(8107, 8121))


class HSecViewMockup(mock.Mock):
Expand Down
7 changes: 5 additions & 2 deletions mslib/msui/aircrafts.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
AIRCRAFT_DUMMY = {
"name": "DUMMY",
"takeoff_weight": 90000,
"fuel": 35000,
"empty_weight": 55000,
"climb": [[0.00, 0.0, 0.0, 0.0, 0.0]],
"descent": [[0.00, 0.0, 0.0, 0.0, 0.0]],
"cruise": [[0.00, 0.00, 400, 2900.00]],
Expand All @@ -50,7 +50,10 @@ class SimpleAircraft(object):
def __init__(self, data):
self.name = data["name"]
self.takeoff_weight = data.get("takeoff_weight", 0)
self.fuel = data.get("fuel", 0)
if "fuel" in data and "empty_weight" not in data:
self.empty_weight = self.takeoff_weight - data["fuel"]
else:
self.empty_weight = data.get("empty_weight", 0)
self._climb = self._setup(data["climb"])
self._descent = self._setup(data["descent"])
self._cruise = self._setup(data["cruise"])
Expand Down
2 changes: 1 addition & 1 deletion mslib/msui/flighttrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ def get_duration_fuel(flightlevel0, flightlevel1, distance, weight, lastleg):
wp1.utc_time = self.performance_settings["takeoff_time"].toPyDateTime()
wp1.weight = self.performance_settings["takeoff_weight"]
wp1.leg_fuel = 0
wp1.rem_fuel = self.performance_settings["fuel"]
wp1.rem_fuel = self.performance_settings["takeoff_weight"] - self.performance_settings["empty_weight"]
wp1.ascent_rate = 0
else:
wp0 = waypoints[pos - 1]
Expand Down
Loading

0 comments on commit 6dc9e4f

Please sign in to comment.