diff --git a/src/blackgui/components/maininfoareacomponent.h b/src/blackgui/components/maininfoareacomponent.h
index 397c3206e..cd45abe23 100644
--- a/src/blackgui/components/maininfoareacomponent.h
+++ b/src/blackgui/components/maininfoareacomponent.h
@@ -39,6 +39,7 @@ namespace BlackGui
class CSimulatorComponent;
class CTextMessageComponent;
class CUserComponent;
+ class CWeatherComponent;
//! Main info area
diff --git a/src/blackgui/components/maininfoareacomponent.ui b/src/blackgui/components/maininfoareacomponent.ui
index ceef1d75a..b54263601 100644
--- a/src/blackgui/components/maininfoareacomponent.ui
+++ b/src/blackgui/components/maininfoareacomponent.ui
@@ -6,8 +6,8 @@
0
0
- 1530
- 71
+ 1629
+ 79
@@ -565,8 +565,8 @@
- 80
- 50
+ 157
+ 78
@@ -613,13 +613,28 @@
0
+
+ 0
+
+
+ 0
+
+
+ 2
+
+
+ 0
+
+
+ 0
+
-
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
+
+
+
+ 0
+ 0
+
@@ -708,7 +723,7 @@
- 80
+ 91
70
@@ -928,6 +943,12 @@
blackgui/components/mappingcomponent.h
1
+
+ BlackGui::Components::CWeatherComponent
+ QTabWidget
+ blackgui/components/weathercomponent.h
+ 1
+
diff --git a/src/blackgui/components/weathercomponent.cpp b/src/blackgui/components/weathercomponent.cpp
new file mode 100644
index 000000000..09ea862d5
--- /dev/null
+++ b/src/blackgui/components/weathercomponent.cpp
@@ -0,0 +1,242 @@
+/* Copyright (C) 2016
+ * swift project Community / Contributors
+ *
+ * This file is part of swift Project. It is subject to the license terms in the LICENSE file found in the top-level
+ * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
+ * including this file, may be copied, modified, propagated, or distributed except according to the terms
+ * contained in the LICENSE file.
+ */
+
+#include "blackgui/components/weathercomponent.h"
+#include "blackgui/infoarea.h"
+#include "blackgui/views/viewbase.h"
+#include "blackgui/guiapplication.h"
+#include "blackcore/contextapplication.h"
+#include "blackcore/contextsimulator.h"
+#include "blackcore/contextownaircraft.h"
+#include "blackmisc/pq/length.h"
+#include "blackmisc/logmessage.h"
+#include "blackmisc/weather/weathergrid.h"
+#include "ui_weathercomponent.h"
+
+#include
+#include
+#include
+
+using namespace BlackCore;
+using namespace BlackGui;
+using namespace BlackGui::Models;
+using namespace BlackGui::Views;
+using namespace BlackMisc;
+using namespace BlackMisc::Geo;
+using namespace BlackMisc::PhysicalQuantities;
+using namespace BlackMisc::Weather;
+
+namespace BlackGui
+{
+ namespace Components
+ {
+ CWeatherComponent::CWeatherComponent(QWidget *parent) :
+ QWidget(parent),
+ CIdentifiable(this),
+ ui(new Ui::CWeatherComponent)
+ {
+ ui->setupUi(this);
+ m_weatherScenarios = CWeatherGrid::getAllScenarios();
+
+ for (const auto &scenario : m_weatherScenarios)
+ {
+ ui->cb_weatherScenario->addItem(scenario.getName(), QVariant::fromValue(scenario));
+ }
+ auto scenario = m_weatherScenarioSetting.get();
+ ui->cb_weatherScenario->setCurrentIndex(scenario.getIndex());
+
+ setupConnections();
+ setupInputValidators();
+ setupCompleter();
+
+ // Set interval to 5 min
+ m_weatherUpdateTimer.setInterval(1000 * 60 * 5);
+
+ // Call this method deferred in order to have the component fully initialized, e.g. object name set by the parent
+ QTimer::singleShot(0, this, &CWeatherComponent::updateWeatherInformation);
+ }
+
+ CWeatherComponent::~CWeatherComponent()
+ { }
+
+ bool CWeatherComponent::setParentDockWidgetInfoArea(CDockWidgetInfoArea *parentDockableWidget)
+ {
+ CEnableForDockWidgetInfoArea::setParentDockWidgetInfoArea(parentDockableWidget);
+ bool c = connect(this->getParentInfoArea(), &CInfoArea::changedInfoAreaTabBarIndex, this, &CWeatherComponent::ps_infoAreaTabBarChanged);
+ Q_ASSERT_X(c, Q_FUNC_INFO, "failed connect");
+ Q_ASSERT_X(parentDockableWidget, Q_FUNC_INFO, "missing parent");
+ return c && parentDockableWidget;
+ }
+
+ void CWeatherComponent::ps_infoAreaTabBarChanged(int index)
+ {
+ // ignore in those cases
+ if (!this->isVisibleWidget()) { return; }
+ if (this->isParentDockWidgetFloating()) { return; }
+
+ // here I know I am the selected widget, update, but keep GUI responsive (-> timer)
+ //QTimer::singleShot(1000, this, &CWeatherComponent::update);
+ Q_UNUSED(index);
+ }
+
+ void CWeatherComponent::toggleUseOwnAircraftPosition(bool checked)
+ {
+ m_lastOwnAircraftPosition = {};
+ if (checked)
+ {
+ ui->le_LatOrIcao->setReadOnly(true);
+ ui->le_Lon->setReadOnly(true);
+ m_weatherUpdateTimer.start();
+ updateWeatherInformation();
+ }
+ else
+ {
+ m_weatherUpdateTimer.stop();
+ ui->le_LatOrIcao->setReadOnly(false);
+ ui->le_LatOrIcao->setText({});
+ ui->le_Lon->setReadOnly(false);
+ ui->le_Lon->setText({});
+ updateWeatherInformation();
+ }
+ }
+
+ void CWeatherComponent::setWeatherScenario(int index)
+ {
+ if (index == -1) { return; }
+ m_lastOwnAircraftPosition = {};
+ auto scenario = m_weatherScenarios[index];
+ m_weatherScenarioSetting.set(scenario);
+ updateWeatherInformation();
+
+ }
+
+ void CWeatherComponent::updateWeatherInformation()
+ {
+ setWeatherGrid({});
+ const bool useOwnAcftPosition = ui->cb_UseOwnAcftPosition->isChecked();
+ BlackMisc::Geo::CCoordinateGeodetic position;
+ Q_ASSERT(ui->cb_weatherScenario->currentData().canConvert());
+ CWeatherScenario scenario = ui->cb_weatherScenario->currentData().value();
+
+ if (useOwnAcftPosition)
+ {
+ Q_ASSERT(sGui->getIContextOwnAircraft());
+ position = sGui->getIContextOwnAircraft()->getOwnAircraft().getPosition();
+ if(position == CCoordinateGeodetic())
+ {
+ ui->le_LatOrIcao->setText("N/A");
+ ui->le_Lon->setText("N/A");
+ return;
+ }
+ ui->le_LatOrIcao->setText(position.latitude().toQString());
+ ui->le_Lon->setText(position.longitude().toQString());
+
+
+ }
+ else
+ {
+ QString latitudeOrIcao = ui->le_LatOrIcao->text();
+ QString longitude = ui->le_Lon->text();
+ const QRegularExpression reIcao("^[a-zA-Z]{4}$", QRegularExpression::CaseInsensitiveOption);
+ auto icaoMatch = reIcao.match(latitudeOrIcao);
+ if (icaoMatch.hasMatch())
+ {
+ CLogMessage(this).warning("Requested weather for ICAO station %1. Unfortunately ICAO support is not yet implemented.") << latitudeOrIcao;
+ return;
+ }
+
+ const QRegularExpression reDecimalNumber("^-?\\d{1,2}[,.]?\\d+$", QRegularExpression::CaseInsensitiveOption);
+ auto latitudeMatch = reDecimalNumber.match(latitudeOrIcao);
+ auto longitudeMatch = reDecimalNumber.match(longitude);
+ if (!latitudeMatch.hasMatch() || !longitudeMatch.hasMatch())
+ {
+ CLogMessage(this).warning("Invalid position - Latitude: %1, Longitude: %2") << latitudeOrIcao << longitude;
+ return;
+ }
+
+ // Replace ',' with '.'. Both are allowed as input, but QString::toDouble() only accepts '.'
+ latitudeOrIcao = latitudeOrIcao.replace(',', '.');
+ longitude = longitude.replace(',', '.');
+ position = { latitudeOrIcao.toDouble(), longitude.toDouble(), 0 };
+ }
+
+ if (CWeatherScenario::isRealWeatherScenario(scenario))
+ {
+ if (!useOwnAcftPosition ||
+ calculateGreatCircleDistance(position, m_lastOwnAircraftPosition).value(CLengthUnit::km()) > 20 )
+ {
+ requestWeatherGrid(position);
+ m_lastOwnAircraftPosition = position;
+ }
+ }
+ else
+ {
+ setWeatherGrid(CWeatherGrid::getByScenario(scenario));
+ }
+ }
+
+ void CWeatherComponent::weatherGridReceived(const CWeatherGrid &weatherGrid, const CIdentifier &identifier)
+ {
+ if(!isMyIdentifier(identifier)) { return; }
+ ui->lb_Status->setText({});
+ setWeatherGrid(weatherGrid);
+ }
+
+ void CWeatherComponent::setupConnections()
+ {
+ // UI connections
+ connect(ui->cb_weatherScenario, static_cast(&QComboBox::currentIndexChanged),
+ this, &CWeatherComponent::setWeatherScenario);
+ connect(ui->le_LatOrIcao, &QLineEdit::returnPressed, this, &CWeatherComponent::updateWeatherInformation);
+ connect(ui->le_Lon, &QLineEdit::returnPressed, this, &CWeatherComponent::updateWeatherInformation);
+ connect(ui->cb_UseOwnAcftPosition, &QCheckBox::toggled, this, &CWeatherComponent::toggleUseOwnAircraftPosition);
+ connect(&m_weatherUpdateTimer, &QTimer::timeout, this, &CWeatherComponent::updateWeatherInformation);
+
+ // Context connections
+ Q_ASSERT(sGui->getIContextSimulator());
+ connect(sGui->getIContextSimulator(), &IContextSimulator::weatherGridReceived, this, &CWeatherComponent::weatherGridReceived);
+ }
+
+ void CWeatherComponent::setupInputValidators()
+ {
+ QRegExp reIcaoOrLatitude("^[a-zA-Z]{4}|-?\\d{1,2}[,.]?\\d+$", Qt::CaseInsensitive);
+ ui->le_LatOrIcao->setValidator(new QRegExpValidator(reIcaoOrLatitude, this));
+ QRegExp reLongitude("^-?\\d{1,2}[,.]?\\d+$", Qt::CaseInsensitive);
+ ui->le_Lon->setValidator(new QRegExpValidator(reLongitude, this));
+ }
+
+ void CWeatherComponent::setupCompleter()
+ {
+ // Temporary list of ICAO airports. Replace with final list, once available
+ QStringList airports = { "EDDM", "LSZH", "EDMO", "EGLL" };
+ QCompleter *c = new QCompleter(airports, this);
+ c->setCaseSensitivity(Qt::CaseInsensitive);
+ c->setCompletionMode(QCompleter::PopupCompletion);
+ c->setMaxVisibleItems(5);
+ ui->le_LatOrIcao->setCompleter(c);
+ }
+
+ void CWeatherComponent::setWeatherGrid(const CWeatherGrid &weatherGrid)
+ {
+ auto gridPoint = weatherGrid.frontOrDefault();
+ ui->tvp_TemperatureLayers->updateContainer(gridPoint.getTemperatureLayers());
+ ui->tvp_CloudLayers->updateContainer(gridPoint.getCloudLayers());
+ ui->tvp_WindLayers->updateContainer(gridPoint.getWindLayers());
+ }
+
+ void CWeatherComponent::requestWeatherGrid(const CCoordinateGeodetic &position)
+ {
+ ui->lb_Status->setText("Loading...");
+ CWeatherGrid weatherGrid { { {}, position } };
+ auto ident = identifier();
+ sGui->getIContextSimulator()->requestWeatherGrid(weatherGrid, ident);
+ }
+
+ } // namespace
+} // namespace
diff --git a/src/blackgui/components/weathercomponent.h b/src/blackgui/components/weathercomponent.h
new file mode 100644
index 000000000..e1dd939eb
--- /dev/null
+++ b/src/blackgui/components/weathercomponent.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2016
+ * swift project Community / Contributors
+ *
+ * This file is part of swift Project. It is subject to the license terms in the LICENSE file found in the top-level
+ * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
+ * including this file, may be copied, modified, propagated, or distributed except according to the terms
+ * contained in the LICENSE file.
+ */
+
+//! \file
+
+#ifndef BLACKGUI_WEATHERCOMPONENT_H
+#define BLACKGUI_WEATHERCOMPONENT_H
+
+#include "blackgui/blackguiexport.h"
+#include "blackgui/components/enablefordockwidgetinfoarea.h"
+#include "blackgui/components/updatetimer.h"
+#include "blackcore/settings/simulator.h"
+#include "blackmisc/geo/coordinategeodetic.h"
+#include "blackmisc/identifiable.h"
+#include "blackmisc/weather/weatherscenario.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace BlackMisc { namespace Weather { class CWeatherGrid; } }
+namespace Ui { class CWeatherComponent; }
+
+namespace BlackGui
+{
+ class CDockWidgetInfoArea;
+
+ namespace Components
+ {
+ //! Weather component
+ class BLACKGUI_EXPORT CWeatherComponent :
+ public QWidget,
+ public CEnableForDockWidgetInfoArea,
+ public BlackMisc::CIdentifiable
+ {
+ Q_OBJECT
+
+ public:
+ //! Constructor
+ explicit CWeatherComponent(QWidget *parent = nullptr);
+
+ //! Destructor
+ virtual ~CWeatherComponent();
+
+ //! \copydoc CEnableForDockWidgetInfoArea::setParentDockWidgetInfoArea
+ virtual bool setParentDockWidgetInfoArea(BlackGui::CDockWidgetInfoArea *parentDockableWidget) override;
+
+ private slots:
+ void ps_infoAreaTabBarChanged(int index);
+
+ private:
+ void toggleUseOwnAircraftPosition(bool checked);
+ void setWeatherScenario(int index);
+
+ void updateWeatherInformation();
+ void weatherGridReceived(const BlackMisc::Weather::CWeatherGrid &weatherGrid, const BlackMisc::CIdentifier &identifier);
+
+ void setupConnections();
+ void setupInputValidators();
+ void setupCompleter();
+
+ void setWeatherGrid(const BlackMisc::Weather::CWeatherGrid &weatherGrid);
+ void requestWeatherGrid(const BlackMisc::Geo::CCoordinateGeodetic &position);
+
+ QScopedPointer ui;
+ QVector m_weatherScenarios;
+ QTimer m_weatherUpdateTimer { this };
+ BlackMisc::Geo::CCoordinateGeodetic m_lastOwnAircraftPosition;
+ BlackMisc::CSetting m_weatherScenarioSetting { this };
+ };
+ } // namespace
+} // namespace
+#endif // guard
diff --git a/src/blackgui/components/weathercomponent.ui b/src/blackgui/components/weathercomponent.ui
new file mode 100644
index 000000000..23758b345
--- /dev/null
+++ b/src/blackgui/components/weathercomponent.ui
@@ -0,0 +1,248 @@
+
+
+ CWeatherComponent
+
+
+
+ 0
+ 0
+ 400
+ 556
+
+
+
+ Weather
+
+
+ 0
+
+
+
+ 0
+
+
+ 7
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Weather Control
+
+
+
+ 1
+
+
+ 7
+
+
+ 1
+
+
+ 0
+
+
+ 1
+
+
-
+
+
+
+
+
+ -
+
+
+ Weather Display
+
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
-
+
+
+ Use Own Aircraft Position
+
+
+ true
+
+
+
+ -
+
+
+
+ 85
+ 0
+
+
+
+
+ 100
+ 16777215
+
+
+
+ Lat or ICAO
+
+
+
+ -
+
+
+
+ 85
+ 0
+
+
+
+ Lon
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ Temperature
+
+
+
+ 0
+
+
+ 0
+
+
+ 1
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ Clouds
+
+
+
+ 0
+
+
+ 0
+
+
+ 1
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+
+
+
+ Winds
+
+
+
+ 0
+
+
+ 0
+
+
+ 1
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+ BlackGui::Views::CTemperatureLayerView
+ QTableView
+ blackgui/views/temperaturelayerview.h
+
+
+ BlackGui::Views::CCloudLayerView
+ QTableView
+ blackgui/views/cloudlayerview.h
+
+
+ BlackGui::Views::CWindLayerView
+ QTableView
+ blackgui/views/windlayerview.h
+
+
+
+
+