File indexing completed on 2024-05-05 07:43:41
0001 /* 0002 SPDX-FileCopyrightText: 2008 Akarsh Simha <akarshsimha@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 0006 0007 Much of the code here is taken from Pablo de Vicente's 0008 modcalcplanets.cpp 0009 0010 */ 0011 0012 #include "conjunctions.h" 0013 0014 #include "geolocation.h" 0015 #include "ksconjunct.h" 0016 #include "kstars.h" 0017 #include "ksnotification.h" 0018 #include "kstarsdata.h" 0019 #include "skymap.h" 0020 #include "dialogs/finddialog.h" 0021 #include "dialogs/locationdialog.h" 0022 #include "skycomponents/skymapcomposite.h" 0023 #include "skyobjects/kscomet.h" 0024 #include "skyobjects/kspluto.h" 0025 #include "ksplanetbase.h" 0026 0027 #include <QFileDialog> 0028 #include <QProgressDialog> 0029 #include <QStandardItemModel> 0030 #include <QtConcurrent> 0031 0032 ConjunctionsTool::ConjunctionsTool(QWidget *parentSplit) : QFrame(parentSplit) 0033 { 0034 setupUi(this); 0035 0036 KStarsData *kd = KStarsData::Instance(); 0037 KStarsDateTime dtStart(KStarsDateTime::currentDateTime()); 0038 KStarsDateTime dtStop(dtStart.djd() + 365.24); // TODO: Refine 0039 0040 //startDate -> setDateTime( dtStart.dateTime() ); 0041 //stopDate -> setDateTime( dtStop.dateTime() ); 0042 //TODO Check if this change works 0043 startDate->setDateTime(dtStart); 0044 stopDate->setDateTime(dtStop); 0045 0046 geoPlace = kd->geo(); 0047 LocationButton->setText(geoPlace->fullName()); 0048 0049 0050 pNames[KSPlanetBase::MERCURY] = i18n("Mercury"); 0051 pNames[KSPlanetBase::VENUS] = i18n("Venus"); 0052 pNames[KSPlanetBase::MARS] = i18n("Mars"); 0053 pNames[KSPlanetBase::JUPITER] = i18n("Jupiter"); 0054 pNames[KSPlanetBase::SATURN] = i18n("Saturn"); 0055 pNames[KSPlanetBase::URANUS] = i18n("Uranus"); 0056 pNames[KSPlanetBase::NEPTUNE] = i18n("Neptune"); 0057 //pNames[KSPlanetBase::PLUTO] = i18nc("Asteroid name (optional)", "Pluto"); 0058 pNames[KSPlanetBase::SUN] = i18n("Sun"); 0059 pNames[KSPlanetBase::MOON] = i18n("Moon"); 0060 0061 // Initialize the Maximum Separation box to 1 degree 0062 maxSeparationBox->setUnits(dmsBox::DEGREES); 0063 maxSeparationBox->show(1.0_deg); 0064 0065 //FilterEdit->showClearButton = true; 0066 ClearFilterButton->setIcon(QIcon::fromTheme("edit-clear")); 0067 0068 // signals and slots connections 0069 connect(LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation())); 0070 connect(Obj1FindButton, SIGNAL(clicked()), this, SLOT(slotFindObject())); 0071 0072 // Mode Change 0073 connect(ModeSelector, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ConjunctionsTool::setMode); 0074 0075 //connect(ComputeButton, SIGNAL(clicked()), this, SLOT(slotCompute())); 0076 connect(ComputeButton, &QPushButton::clicked, [this]() 0077 { 0078 QtConcurrent::run(this, &ConjunctionsTool::slotCompute); 0079 }); 0080 connect(FilterTypeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(slotFilterType(int))); 0081 connect(ClearButton, SIGNAL(clicked()), this, SLOT(slotClear())); 0082 connect(ExportButton, SIGNAL(clicked()), this, SLOT(slotExport())); 0083 connect(OutputList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotGoto())); 0084 connect(ClearFilterButton, SIGNAL(clicked()), FilterEdit, SLOT(clear())); 0085 connect(FilterEdit, SIGNAL(textChanged(QString)), this, SLOT(slotFilterReg(QString))); 0086 0087 m_Model = new QStandardItemModel(0, 5, this); 0088 0089 setMode(ModeSelector->currentIndex()); 0090 0091 // Init filter type combobox 0092 FilterTypeComboBox->clear(); 0093 FilterTypeComboBox->addItem(i18n("Single Object")); 0094 FilterTypeComboBox->addItem(i18n("Any")); 0095 FilterTypeComboBox->addItem(i18n("Stars")); 0096 FilterTypeComboBox->addItem(i18n("Solar System")); 0097 FilterTypeComboBox->addItem(i18n("Planets")); 0098 FilterTypeComboBox->addItem(i18n("Comets")); 0099 FilterTypeComboBox->addItem(i18n("Asteroids")); 0100 FilterTypeComboBox->addItem(i18n("Open Clusters")); 0101 FilterTypeComboBox->addItem(i18n("Globular Clusters")); 0102 FilterTypeComboBox->addItem(i18n("Gaseous Nebulae")); 0103 FilterTypeComboBox->addItem(i18n("Planetary Nebulae")); 0104 FilterTypeComboBox->addItem(i18n("Galaxies")); 0105 0106 Obj2ComboBox->clear(); 0107 for (int i = 0; i < KSPlanetBase::UNKNOWN_PLANET; ++i) 0108 { 0109 // Obj1ComboBox->insertItem( i, pNames[i] ); 0110 Obj2ComboBox->insertItem(i, pNames[i]); 0111 } 0112 0113 maxSeparationBox->setEnabled(true); 0114 0115 //Set up the Table Views 0116 m_Model->setHorizontalHeaderLabels(QStringList() << i18n("Conjunction/Opposition") << i18n("Date & Time (UT)") 0117 << i18n("Object 1") << i18n("Object 2") << i18n("Separation")); 0118 m_SortModel = new QSortFilterProxyModel(this); 0119 m_SortModel->setSourceModel(m_Model); 0120 OutputList->setModel(m_SortModel); 0121 OutputList->setSortingEnabled(true); 0122 OutputList->horizontalHeader()->setStretchLastSection(true); 0123 OutputList->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); 0124 OutputList->horizontalHeader()->resizeSection(2, 100); 0125 OutputList->horizontalHeader()->resizeSection(3, 100); 0126 OutputList->horizontalHeader()->resizeSection(4, 120); //is it bad way to fix default size of columns ? 0127 0128 show(); 0129 } 0130 0131 void ConjunctionsTool::slotGoto() 0132 { 0133 int index = m_SortModel->mapToSource(OutputList->currentIndex()).row(); // Get the number of the line 0134 long double jd = outputJDList.value(index); 0135 KStarsDateTime dt; 0136 KStars *ks = KStars::Instance(); 0137 KStarsData *data = KStarsData::Instance(); 0138 SkyMap *map = ks->map(); 0139 0140 // Show conjunction 0141 data->setLocation(*geoPlace); 0142 dt.setDJD(jd); 0143 data->changeDateTime(dt); 0144 map->setClickedObject(data->skyComposite()->findByName(m_Model->data(m_Model->index(index, 2)).toString())); 0145 map->setClickedPoint(map->clickedObject()); 0146 map->slotCenter(); 0147 } 0148 0149 void ConjunctionsTool::slotFindObject() 0150 { 0151 if (FindDialog::Instance()->exec() == QDialog::Accepted) 0152 { 0153 if (!FindDialog::Instance()->targetObject()) 0154 return; 0155 Object1 = SkyObject_s(FindDialog::Instance()->targetObject()->clone()); 0156 if (Object1 != nullptr) 0157 Obj1FindButton->setText(Object1->name()); 0158 } 0159 } 0160 0161 void ConjunctionsTool::setMode(int new_mode) 0162 { 0163 // unlikely to happen 0164 if(new_mode == -1 || new_mode > 2) 0165 { 0166 ModeSelector->setCurrentIndex(0); 0167 return; 0168 } 0169 0170 mode = static_cast<MODE>(new_mode); 0171 } 0172 0173 void ConjunctionsTool::slotLocation() 0174 { 0175 QPointer<LocationDialog> ld(new LocationDialog(this)); 0176 if (ld->exec() == QDialog::Accepted && ld) 0177 { 0178 geoPlace = ld->selectedCity(); 0179 LocationButton->setText(geoPlace->fullName()); 0180 } 0181 delete ld; 0182 } 0183 0184 void ConjunctionsTool::slotFilterType(int) 0185 { 0186 // Disable find button if the user select an object type 0187 if (FilterTypeComboBox->currentIndex() == 0) 0188 Obj1FindButton->setEnabled(true); 0189 else 0190 Obj1FindButton->setEnabled(false); 0191 } 0192 0193 void ConjunctionsTool::slotClear() 0194 { 0195 m_Model->setRowCount(0); 0196 outputJDList.clear(); 0197 m_index = 0; 0198 } 0199 0200 void ConjunctionsTool::slotExport() 0201 { 0202 int i, j; 0203 QByteArray line; 0204 0205 //QFile file( KFileDialog::getSaveFileName( QDir::homePath(), "*|All files", this, "Save Conjunctions" ) ); 0206 QFile file(QFileDialog::getSaveFileName(nullptr, i18nc("@title:window", "Save Conjunctions"), QDir::homePath(), "*|All files")); 0207 0208 file.open(QIODevice::WriteOnly | QIODevice::Text); 0209 0210 for (i = 0; i < m_Model->rowCount(); ++i) 0211 { 0212 for (j = 0; j < m_Model->columnCount(); ++j) 0213 { 0214 line.append(m_Model->data(m_Model->index(i, j)).toByteArray()); 0215 if (j < m_Model->columnCount() - 1) 0216 line.append(";"); 0217 else 0218 line.append("\n"); 0219 } 0220 file.write(line); 0221 line.clear(); 0222 } 0223 0224 file.close(); 0225 } 0226 0227 void ConjunctionsTool::slotFilterReg(const QString &filter) 0228 { 0229 m_SortModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive, QRegExp::RegExp)); 0230 m_SortModel->setFilterKeyColumn(-1); 0231 } 0232 0233 void ConjunctionsTool::slotCompute(void) 0234 { 0235 KStarsDateTime dtStart(startDate->dateTime()); // Start date 0236 KStarsDateTime dtStop(stopDate->dateTime()); // Stop date 0237 long double startJD = dtStart.djd(); // Start julian day 0238 long double stopJD = dtStop.djd(); // Stop julian day 0239 bool opposition = false; // true=opposition, false=conjunction 0240 if (mode == OPPOSITION) 0241 opposition = true; 0242 QStringList objects; // List of sky object used as Object1 0243 KStarsData *data = KStarsData::Instance(); 0244 int progress = 0; 0245 0246 // Check if we have a valid angle in maxSeparationBox 0247 dms maxSeparation(0.0); 0248 bool ok; 0249 maxSeparation = maxSeparationBox->createDms(&ok); 0250 0251 if (!ok) 0252 { 0253 KSNotification::sorry(i18n("Maximum separation entered is not a valid angle. Use the What's this help feature " 0254 "for information on how to enter a valid angle")); 0255 return; 0256 } 0257 0258 // Check if Object1 and Object2 are set 0259 if (FilterTypeComboBox->currentIndex() == 0 && Object1 == nullptr) 0260 { 0261 KSNotification::sorry(i18n("Please select an object to check conjunctions with, by clicking on the \'Find Object\' button.")); 0262 return; 0263 } 0264 Object2.reset(KSPlanetBase::createPlanet(Obj2ComboBox->currentIndex())); 0265 if (FilterTypeComboBox->currentIndex() == 0 && Object1->name() == Object2->name()) 0266 { 0267 // FIXME: Must free the created Objects 0268 KSNotification::sorry(i18n("Please select two different objects to check conjunctions with.")); 0269 return; 0270 } 0271 0272 // Init KSConjunct object 0273 KSConjunct ksc; 0274 connect(&ksc, SIGNAL(madeProgress(int)), this, SLOT(showProgress(int))); 0275 ksc.setGeoLocation(geoPlace); 0276 0277 switch (FilterTypeComboBox->currentIndex()) 0278 { 0279 case 1: // All object types 0280 foreach (int type, data->skyComposite()->objectNames().keys()) 0281 objects += data->skyComposite()->objectNames(type); 0282 break; 0283 case 2: // Stars 0284 objects += data->skyComposite()->objectNames(SkyObject::STAR); 0285 objects += data->skyComposite()->objectNames(SkyObject::CATALOG_STAR); 0286 break; 0287 case 3: // Solar system 0288 objects += data->skyComposite()->objectNames(SkyObject::PLANET); 0289 objects += data->skyComposite()->objectNames(SkyObject::COMET); 0290 objects += data->skyComposite()->objectNames(SkyObject::ASTEROID); 0291 objects += data->skyComposite()->objectNames(SkyObject::MOON); 0292 objects += i18n("Sun"); 0293 // Remove Object2 planet 0294 objects.removeAll(Object2->name()); 0295 break; 0296 case 4: // Planet 0297 objects += data->skyComposite()->objectNames(SkyObject::PLANET); 0298 // Remove Object2 planet 0299 objects.removeAll(Object2->name()); 0300 break; 0301 case 5: // Comet 0302 objects += data->skyComposite()->objectNames(SkyObject::COMET); 0303 break; 0304 case 6: // Asteroid 0305 objects += data->skyComposite()->objectNames(SkyObject::ASTEROID); 0306 break; 0307 case 7: // Open Clusters 0308 objects = data->skyComposite()->objectNames(SkyObject::OPEN_CLUSTER); 0309 break; 0310 case 8: // Open Clusters 0311 objects = data->skyComposite()->objectNames(SkyObject::GLOBULAR_CLUSTER); 0312 break; 0313 case 9: // Gaseous nebulae 0314 objects = data->skyComposite()->objectNames(SkyObject::GASEOUS_NEBULA); 0315 break; 0316 case 10: // Planetary nebula 0317 objects = data->skyComposite()->objectNames(SkyObject::PLANETARY_NEBULA); 0318 break; 0319 case 11: // Galaxies 0320 objects = data->skyComposite()->objectNames(SkyObject::GALAXY); 0321 break; 0322 } 0323 0324 // Remove all Jupiter and Saturn moons 0325 // KStars crash if we compute a conjunction between a planet and one of this moon 0326 if (FilterTypeComboBox->currentIndex() == 1 || FilterTypeComboBox->currentIndex() == 3 || 0327 FilterTypeComboBox->currentIndex() == 6) 0328 { 0329 objects.removeAll("Io"); 0330 objects.removeAll("Europa"); 0331 objects.removeAll("Ganymede"); 0332 objects.removeAll("Callisto"); 0333 objects.removeAll("Mimas"); 0334 objects.removeAll("Enceladus"); 0335 objects.removeAll("Tethys"); 0336 objects.removeAll("Dione"); 0337 objects.removeAll("Rhea"); 0338 objects.removeAll("Titan"); 0339 objects.removeAll("Hyperion"); 0340 objects.removeAll("Iapetus"); 0341 } 0342 0343 ksc.setMaxSeparation(maxSeparation); 0344 ksc.setObject2(Object2); 0345 ksc.setOpposition(opposition); 0346 0347 if (FilterTypeComboBox->currentIndex() != 0) 0348 { 0349 // Show a progress dialog while processing 0350 QProgressDialog progressDlg(i18n("Compute conjunction..."), i18n("Abort"), 0, objects.count(), this); 0351 progressDlg.setWindowTitle(i18nc("@title:window", "Conjunction")); 0352 progressDlg.setWindowModality(Qt::WindowModal); 0353 progressDlg.setValue(0); 0354 0355 for (auto &object : objects) 0356 { 0357 // If the user click on the 'cancel' button 0358 if (progressDlg.wasCanceled()) 0359 break; 0360 0361 // Update progress dialog 0362 ++progress; 0363 progressDlg.setValue(progress); 0364 progressDlg.setLabelText(i18n("Compute conjunction between %1 and %2", Object2->name(), object)); 0365 0366 // Compute conjuction 0367 Object1 = std::shared_ptr<SkyObject>(data->skyComposite()->findByName(object)->clone()); 0368 ksc.setObject1(Object1); 0369 showConjunctions(ksc.findClosestApproach(startJD, stopJD), 0370 object, Object2->name()); 0371 } 0372 0373 progressDlg.setValue(objects.count()); 0374 } 0375 else 0376 { 0377 // Change cursor while we search for conjunction 0378 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 0379 0380 ComputeStack->setCurrentIndex(1); 0381 0382 ksc.setObject1(Object1); 0383 showConjunctions(ksc.findClosestApproach(startJD, stopJD), 0384 Object1->name(), Object2->name()); 0385 ComputeStack->setCurrentIndex(0); 0386 0387 // Restore cursor 0388 QApplication::restoreOverrideCursor(); 0389 } 0390 0391 Object2.reset(); 0392 } 0393 0394 void ConjunctionsTool::showProgress(int n) 0395 { 0396 progress->setValue(n); 0397 } 0398 0399 void ConjunctionsTool::showConjunctions(const QMap<long double, dms> &conjunctionlist, const QString &object1, 0400 const QString &object2) 0401 { 0402 KStarsDateTime dt; 0403 QList<QStandardItem *> itemList; 0404 0405 for (auto it = conjunctionlist.constBegin(); it != conjunctionlist.constEnd(); ++it) 0406 { 0407 dt.setDJD(it.key()); 0408 QStandardItem *typeItem; 0409 0410 if (mode == CONJUNCTION) 0411 typeItem = new QStandardItem(i18n("Conjunction")); 0412 else 0413 typeItem = new QStandardItem(i18n("Opposition")); 0414 0415 itemList << typeItem 0416 //FIXME TODO is this ISO date? is there a ready format to use? 0417 //<< new QStandardItem( QLocale().toString( dt.dateTime(), "YYYY-MM-DDTHH:mm:SS" ) ) 0418 //<< new QStandardItem( QLocale().toString( dt, Qt::ISODate) ) 0419 << new QStandardItem(dt.toString(Qt::ISODate)) << new QStandardItem(object1) 0420 << new QStandardItem(object2) << new QStandardItem(it.value().toDMSString()); 0421 m_Model->appendRow(itemList); 0422 itemList.clear(); 0423 0424 outputJDList.insert(m_index, it.key()); 0425 ++m_index; 0426 } 0427 } 0428 0429 void ConjunctionsTool::setUpConjunctionOpposition() 0430 { 0431 0432 }