File indexing completed on 2024-05-05 03:50:42

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2012 Rene Kuettner <rene@bitkanal.net>
0004 //
0005 
0006 #include "EclipsesPlugin.h"
0007 
0008 #include "MarbleWidget.h"
0009 #include "MarbleColors.h"
0010 #include "MarbleDebug.h"
0011 #include "MarbleModel.h"
0012 #include "MarbleClock.h"
0013 #include "ViewportParams.h"
0014 #include "GeoPainter.h"
0015 
0016 #include "EclipsesModel.h"
0017 #include "EclipsesItem.h"
0018 #include "EclipsesBrowserDialog.h"
0019 
0020 #include "ui_EclipsesConfigDialog.h"
0021 #include "ui_EclipsesReminderDialog.h"
0022 
0023 #include <QMenu>
0024 #include <QPushButton>
0025 
0026 namespace Marble
0027 {
0028 
0029 EclipsesPlugin::EclipsesPlugin()
0030     : RenderPlugin( nullptr ),
0031       m_isInitialized( false ),
0032       m_marbleWidget( nullptr ),
0033       m_model( nullptr ),
0034       m_eclipsesActionGroup( nullptr ),
0035       m_eclipsesMenuAction( nullptr ),
0036       m_eclipsesListMenu( nullptr ),
0037       m_menuYear( 0 ),
0038       m_configDialog( nullptr ),
0039       m_configWidget( nullptr ),
0040       m_browserDialog( nullptr ),
0041       m_reminderDialog( nullptr ),
0042       m_reminderWidget( nullptr )
0043 {
0044 }
0045 
0046 EclipsesPlugin::EclipsesPlugin( const MarbleModel *marbleModel )
0047     : RenderPlugin( marbleModel ),
0048      m_isInitialized( false ),
0049      m_marbleWidget( nullptr ),
0050      m_model( nullptr ),
0051      m_eclipsesActionGroup( nullptr ),
0052      m_eclipsesMenuAction( nullptr ),
0053      m_eclipsesListMenu( nullptr ),
0054      m_menuYear( 0 ),
0055      m_configDialog( nullptr ),
0056      m_configWidget( nullptr ),
0057      m_browserDialog( nullptr ),
0058      m_reminderDialog( nullptr ),
0059      m_reminderWidget( nullptr )
0060 {
0061     connect( this, SIGNAL(settingsChanged(QString)),
0062                    SLOT(updateSettings()) );
0063 }
0064 
0065 EclipsesPlugin::~EclipsesPlugin()
0066 {
0067     if( m_isInitialized ) {
0068         delete m_model;
0069         delete m_eclipsesActionGroup;
0070         delete m_eclipsesListMenu;
0071         delete m_configDialog;
0072         delete m_configWidget;
0073         delete m_browserDialog;
0074         delete m_reminderDialog;
0075         delete m_reminderWidget;
0076     }
0077 }
0078 
0079 QStringList EclipsesPlugin::backendTypes() const
0080 {
0081     return QStringList(QStringLiteral("eclipses"));
0082 }
0083 
0084 QString EclipsesPlugin::renderPolicy() const
0085 {
0086     return QStringLiteral("ALWAYS");
0087 }
0088 
0089 QStringList EclipsesPlugin::renderPosition() const
0090 {
0091     return QStringList(QStringLiteral("ORBIT"));
0092 }
0093 
0094 QString EclipsesPlugin::name() const
0095 {
0096     return tr( "Eclipses" );
0097 }
0098 
0099 QString EclipsesPlugin::nameId() const
0100 {
0101     return QStringLiteral("eclipses");
0102 }
0103 
0104 QString EclipsesPlugin::guiString() const
0105 {
0106     return tr( "E&clipses" );
0107 }
0108 
0109 QString EclipsesPlugin::version() const
0110 {
0111     return QStringLiteral("1.0");
0112 }
0113 
0114 QString EclipsesPlugin::description() const
0115 {
0116     return tr( "This plugin visualizes solar eclipses." );
0117 }
0118 
0119 QString EclipsesPlugin::copyrightYears() const
0120 {
0121     return QStringLiteral("2013");
0122 }
0123 
0124 QVector<PluginAuthor> EclipsesPlugin::pluginAuthors() const
0125 {
0126     return QVector<PluginAuthor>()
0127             << PluginAuthor(QStringLiteral("Rene Kuettner"), QStringLiteral("rene@bitkanal.net"))
0128             << PluginAuthor(QStringLiteral("Gerhard Holtkamp"), QString());
0129 }
0130 
0131 QIcon EclipsesPlugin::icon() const
0132 {
0133     return QIcon(QStringLiteral(":res/eclipses.png"));
0134 }
0135 
0136 RenderPlugin::RenderType EclipsesPlugin::renderType() const
0137 {
0138     return RenderPlugin::ThemeRenderType;
0139     //return UnknownRenderType;
0140 }
0141 
0142 QList<QActionGroup*>* EclipsesPlugin::actionGroups() const
0143 {
0144     return const_cast<QList<QActionGroup*>*>( &m_actionGroups );
0145 }
0146 
0147 QDialog* EclipsesPlugin::configDialog()
0148 {
0149     Q_ASSERT( m_isInitialized );
0150     return m_configDialog;
0151 }
0152 
0153 void EclipsesPlugin::initialize()
0154 {
0155     if( isInitialized() ) {
0156         return;
0157     }
0158 
0159     // initialize dialogs
0160     delete m_configDialog;
0161     m_configDialog = new QDialog();
0162     delete m_configWidget;
0163     m_configWidget = new Ui::EclipsesConfigDialog();
0164     m_configWidget->setupUi( m_configDialog );
0165 
0166     connect( m_configDialog, SIGNAL(accepted()),
0167              this, SLOT(writeSettings()) );
0168     connect( m_configDialog, SIGNAL(rejected()),
0169              this, SLOT(readSettings()) );
0170     connect( m_configWidget->buttonBox->button( QDialogButtonBox::Reset ),
0171              SIGNAL(clicked()), this, SLOT(readSettings()) );
0172     connect( m_configWidget->buttonBox->button( QDialogButtonBox::Apply ),
0173              SIGNAL(clicked()), this, SLOT(writeSettings()) );
0174     connect( m_configWidget->buttonBox->button( QDialogButtonBox::Apply ),
0175              SIGNAL(clicked()), this, SLOT(updateEclipses()) );
0176 
0177     m_browserDialog = new EclipsesBrowserDialog( marbleModel() );
0178     connect( m_browserDialog, SIGNAL(buttonShowClicked(int,int)),
0179              this, SLOT(showEclipse(int,int)) );
0180     connect( m_browserDialog, SIGNAL(buttonSettingsClicked()),
0181              m_configDialog, SLOT(show()) );
0182 
0183     delete m_reminderDialog;
0184     m_reminderDialog = new QDialog();
0185     delete m_reminderWidget;
0186     m_reminderWidget = new Ui::EclipsesReminderDialog();
0187     m_reminderWidget->setupUi( m_reminderDialog );
0188 
0189     // initialize menu entries
0190     m_eclipsesActionGroup = new QActionGroup( this );
0191     m_actionGroups.append( m_eclipsesActionGroup );
0192 
0193     m_eclipsesListMenu = new QMenu();
0194     m_eclipsesActionGroup->addAction( m_eclipsesListMenu->menuAction() );
0195     connect( m_eclipsesListMenu, SIGNAL(triggered(QAction*)),
0196              this, SLOT(showEclipseFromMenu(QAction*)) );
0197 
0198     m_eclipsesMenuAction = new QAction(
0199             tr("Browse Ecli&pses..."), m_eclipsesActionGroup );
0200     m_eclipsesMenuAction->setIcon(QIcon(QStringLiteral(":res/eclipses.png")));
0201     m_eclipsesActionGroup->addAction( m_eclipsesMenuAction );
0202     connect( m_eclipsesMenuAction, SIGNAL(triggered()),
0203              m_browserDialog, SLOT(show()) );
0204 
0205     // initialize eclipses model
0206     m_model = new EclipsesModel( marbleModel() );
0207 
0208     connect( marbleModel()->clock(), SIGNAL(timeChanged()),
0209              this, SLOT(updateEclipses()) );
0210 
0211     m_isInitialized = true;
0212 
0213     readSettings();
0214     updateEclipses();
0215     updateMenuItemState();
0216     updateSettings();
0217 }
0218 
0219 bool EclipsesPlugin::isInitialized() const
0220 {
0221     return m_isInitialized;
0222 }
0223 
0224 bool EclipsesPlugin::eventFilter( QObject *object, QEvent *e )
0225 {
0226     // delayed initialization of pointer to marble widget
0227     MarbleWidget *widget = dynamic_cast<MarbleWidget*> (object);
0228     if ( widget && m_marbleWidget != widget ) {
0229         connect( widget, SIGNAL(themeChanged(QString)),
0230                  this, SLOT(updateMenuItemState()) );
0231         m_marbleWidget = widget;
0232     }
0233 
0234     return RenderPlugin::eventFilter(object, e);
0235 }
0236 
0237 bool EclipsesPlugin::render( GeoPainter *painter,
0238                              ViewportParams *viewport,
0239                              const QString &renderPos,
0240                              GeoSceneLayer *layer )
0241 {
0242     Q_UNUSED( viewport );
0243     Q_UNUSED( renderPos );
0244     Q_UNUSED( layer );
0245 
0246     if (marbleModel()->planetId() == QLatin1String("earth")) {
0247         for( EclipsesItem *item: m_model->items() ) {
0248             if( item->takesPlaceAt( marbleModel()->clock()->dateTime() ) ) {
0249                 return renderItem( painter, item );
0250             }
0251         }
0252     }
0253 
0254     return true;
0255 }
0256 
0257 bool EclipsesPlugin::renderItem( GeoPainter *painter, EclipsesItem *item ) const
0258 {
0259     int phase = item->phase();
0260 
0261     // Draw full penumbra shadow cone
0262     if( m_configWidget->checkBoxShowFullPenumbra->isChecked() ) {
0263         painter->setPen( Oxygen::aluminumGray1 );
0264         QColor sunBoundingBrush ( Oxygen::aluminumGray6 );
0265         sunBoundingBrush.setAlpha( 48 );
0266         painter->setBrush( sunBoundingBrush );
0267         painter->drawPolygon( item->shadowConePenumbra() );
0268     }
0269 
0270     // Draw 60% penumbra shadow cone
0271     if( m_configWidget->checkBoxShow60MagPenumbra->isChecked() ) {
0272         painter->setPen( Oxygen::aluminumGray2 );
0273         QColor penumbraBrush ( Oxygen::aluminumGray6 );
0274         penumbraBrush.setAlpha( 96 );
0275         painter->setBrush( penumbraBrush );
0276         painter->drawPolygon( item->shadowCone60MagPenumbra() );
0277     }
0278 
0279     // Draw southern boundary of the penumbra
0280     if( m_configWidget->checkBoxShowSouthernPenumbra->isChecked() ) {
0281         QColor southernBoundaryColor(Oxygen::brickRed1);
0282         southernBoundaryColor.setAlpha(128);
0283         QPen southernBoundary(southernBoundaryColor);
0284         southernBoundary.setWidth(3);
0285         painter->setPen( southernBoundary );
0286         painter->drawPolyline( item->southernPenumbra() );
0287         painter->setPen( Oxygen::brickRed5 );
0288         painter->drawPolyline( item->southernPenumbra() );
0289     }
0290 
0291     // Draw northern boundary of the penumbra
0292     if( m_configWidget->checkBoxShowNorthernPenumbra->isChecked() ) {
0293         QColor northernBoundaryColor(Oxygen::brickRed1);
0294         northernBoundaryColor.setAlpha(128);
0295         QPen northernBoundary(northernBoundaryColor);
0296         northernBoundary.setWidth(3);
0297         painter->setPen( northernBoundary );
0298         painter->drawPolyline( item->northernPenumbra() );
0299         painter->setPen( Oxygen::brickRed5 );
0300         painter->drawPolyline( item->northernPenumbra() );
0301     }
0302 
0303     // Draw Sunrise / Sunset Boundaries
0304     if( m_configWidget->checkBoxShowSunBoundaries->isChecked() ) {
0305         painter->setPen( Oxygen::hotOrange6 );
0306         const QList<GeoDataLinearRing> boundaries = item->sunBoundaries();
0307         QList<GeoDataLinearRing>::const_iterator i = boundaries.constBegin();
0308         QColor sunBoundingBrush ( Oxygen::hotOrange5 );
0309         sunBoundingBrush.setAlpha( 64 );
0310         painter->setBrush( sunBoundingBrush );
0311         for( ; i != boundaries.constEnd(); ++i ) {
0312             painter->drawPolygon( *i );
0313         }
0314     }
0315 
0316     // total or annular eclipse
0317     if( m_configWidget->checkBoxShowUmbra->isChecked() && phase > 3 )
0318     {
0319         painter->setPen( Oxygen::aluminumGray4 );
0320         QColor sunBoundingBrush ( Oxygen::aluminumGray6 );
0321         sunBoundingBrush.setAlpha( 128 );
0322         painter->setBrush( sunBoundingBrush );
0323         painter->drawPolygon( item->umbra() );
0324 
0325         // draw shadow cone
0326         painter->setPen( Qt::black );
0327         QColor shadowConeBrush ( Oxygen::aluminumGray6 );
0328         shadowConeBrush.setAlpha( 128 );
0329         painter->setBrush( shadowConeBrush );
0330         painter->drawPolygon( item->shadowConeUmbra() );
0331     }
0332 
0333     // plot central line
0334     if( m_configWidget->checkBoxShowCentralLine->isChecked() && phase > 3 ) {
0335         painter->setPen( Qt::black );
0336         painter->drawPolyline( item->centralLine() );
0337     }
0338 
0339     // mark point of maximum eclipse
0340     if( m_configWidget->checkBoxShowMaximum->isChecked() ) {
0341         painter->setPen( Qt::white );
0342         QColor sunBoundingBrush ( Qt::white );
0343         sunBoundingBrush.setAlpha( 128 );
0344         painter->setBrush( sunBoundingBrush );
0345 
0346         painter->drawEllipse( item->maxLocation(), 15, 15 );
0347         painter->setPen( Oxygen::brickRed4 );
0348         painter->drawText( item->maxLocation(), tr( "Maximum of Eclipse" ) );
0349     }
0350 
0351     return true;
0352 }
0353 
0354 QHash<QString, QVariant> EclipsesPlugin::settings() const
0355 {
0356     return RenderPlugin::settings();
0357 }
0358 
0359 void EclipsesPlugin::setSettings( const QHash<QString, QVariant> &settings )
0360 {
0361     RenderPlugin::setSettings( settings );
0362     m_settings = settings;
0363     emit settingsChanged( nameId() );
0364 }
0365 
0366 void EclipsesPlugin::readSettings()
0367 {
0368     m_configWidget->checkBoxEnableLunarEclipses->setChecked(
0369             m_settings.value(QStringLiteral("enableLunarEclipses"), false).toBool());
0370     m_configWidget->checkBoxShowMaximum->setChecked(
0371             m_settings.value(QStringLiteral("showMaximum"), true).toBool());
0372     m_configWidget->checkBoxShowUmbra->setChecked(
0373             m_settings.value(QStringLiteral("showUmbra"), true).toBool());
0374     m_configWidget->checkBoxShowSouthernPenumbra->setChecked(
0375             m_settings.value(QStringLiteral("showSouthernPenumbra"), true).toBool());
0376     m_configWidget->checkBoxShowNorthernPenumbra->setChecked(
0377             m_settings.value(QStringLiteral("showNorthernPenumbra"), true).toBool());
0378     m_configWidget->checkBoxShowCentralLine->setChecked(
0379             m_settings.value(QStringLiteral("showCentralLine"), true).toBool());
0380     m_configWidget->checkBoxShowFullPenumbra->setChecked(
0381             m_settings.value(QStringLiteral("showFullPenumbra"), true).toBool());
0382     m_configWidget->checkBoxShow60MagPenumbra->setChecked(
0383             m_settings.value(QStringLiteral("show60MagPenumbra"), false).toBool());
0384     m_configWidget->checkBoxShowSunBoundaries->setChecked(
0385             m_settings.value(QStringLiteral("showSunBoundaries"), true).toBool());
0386 }
0387 
0388 void EclipsesPlugin::writeSettings()
0389 {
0390     m_settings.insert(QStringLiteral("enableLunarEclipses"),
0391             m_configWidget->checkBoxEnableLunarEclipses->isChecked() );
0392     m_settings.insert(QStringLiteral("showMaximum"),
0393             m_configWidget->checkBoxShowMaximum->isChecked() );
0394     m_settings.insert(QStringLiteral("showUmbra"),
0395             m_configWidget->checkBoxShowUmbra->isChecked() );
0396     m_settings.insert(QStringLiteral("showSouthernPenumbra"),
0397             m_configWidget->checkBoxShowSouthernPenumbra->isChecked() );
0398     m_settings.insert(QStringLiteral("showNorthernPenumbra"),
0399             m_configWidget->checkBoxShowNorthernPenumbra->isChecked() );
0400     m_settings.insert(QStringLiteral("showCentralLine"),
0401             m_configWidget->checkBoxShowCentralLine->isChecked() );
0402     m_settings.insert(QStringLiteral("showFullPenumbra"),
0403             m_configWidget->checkBoxShowFullPenumbra->isChecked() );
0404     m_settings.insert(QStringLiteral("show60MagPenumbra"),
0405             m_configWidget->checkBoxShow60MagPenumbra->isChecked() );
0406     m_settings.insert(QStringLiteral("showSunBoundaries"),
0407             m_configWidget->checkBoxShowSunBoundaries->isChecked() );
0408 
0409     emit settingsChanged( nameId() );
0410 }
0411 
0412 void EclipsesPlugin::updateSettings()
0413 {
0414     if (!isInitialized()) {
0415         return;
0416     }
0417 
0418     m_browserDialog->setWithLunarEclipses(
0419             m_settings.value(QStringLiteral("enableLunarEclipses")).toBool());
0420     if( m_model->withLunarEclipses() !=
0421             m_settings.value(QStringLiteral("enableLunarEclipses")).toBool()) {
0422         updateEclipses();
0423     }
0424 }
0425 
0426 void EclipsesPlugin::updateEclipses()
0427 {
0428     // mDebug() << "Updating eclipses....";
0429     const int year = marbleModel()->clock()->dateTime().date().year();
0430     const bool lun = m_settings.value(QStringLiteral("enableLunarEclipses")).toBool();
0431 
0432     if( ( m_menuYear != year ) || ( m_model->withLunarEclipses() != lun ) ) {
0433 
0434         // remove old menus
0435         for( QAction *action: m_eclipsesListMenu->actions() ) {
0436             m_eclipsesListMenu->removeAction( action );
0437             delete action;
0438         }
0439 
0440         // update year and create menus for this year's eclipse events
0441         if( m_model->year() != year ) {
0442             m_model->setYear( year );
0443         }
0444         m_menuYear = year;
0445 
0446         // enable/disable lunar eclipses if necessary
0447         if( m_model->withLunarEclipses() != lun ) {
0448             m_model->setWithLunarEclipses( lun );
0449         }
0450 
0451         m_eclipsesListMenu->setTitle( tr("Eclipses in %1").arg( year ) );
0452 
0453         for( EclipsesItem *item: m_model->items() ) {
0454             QAction *action = m_eclipsesListMenu->addAction(
0455                         item->dateMaximum().date().toString() );
0456             action->setData( QVariant( 1000 * item->dateMaximum().date().year() +  item->index() ) );
0457             action->setIcon( item->icon() );
0458         }
0459 
0460         emit actionGroupsChanged();
0461     }
0462 }
0463 
0464 void EclipsesPlugin::updateMenuItemState()
0465 {
0466     if( !isInitialized() ) {
0467         return;
0468     }
0469 
0470     // eclipses are only supported for earth based observers at the moment
0471     // so we disable the menu items for other celestial bodies
0472 
0473     const bool active = (marbleModel()->planetId() == QLatin1String("earth"));
0474 
0475     m_eclipsesListMenu->setEnabled( active );
0476     m_eclipsesMenuAction->setEnabled( active );
0477 }
0478 
0479 void EclipsesPlugin::showEclipse( int year, int index )
0480 {
0481     if( m_model->year() != year ) {
0482         m_model->setYear( year );
0483     }
0484 
0485     EclipsesItem *item = m_model->eclipseWithIndex( index );
0486     Q_ASSERT( item );
0487 
0488     if( item ) {
0489         m_marbleWidget->model()->clock()->setDateTime( item->dateMaximum() );
0490         m_marbleWidget->centerOn( item->maxLocation() );
0491     }
0492 }
0493 
0494 void EclipsesPlugin::showEclipseFromMenu( QAction *action )
0495 {
0496     Q_ASSERT( action->data().isValid() );
0497     int year = action->data().toInt() / 1000;
0498     int index = action->data().toInt() - 1000 * year;
0499 
0500     showEclipse( year, index );
0501 }
0502 
0503 } // namespace Marble
0504 
0505 #include "moc_EclipsesPlugin.cpp"
0506