Kstars

obslistwizard.cpp
1 /*
2  SPDX-FileCopyrightText: 2005 Jason Harris <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "obslistwizard.h"
8 #include "Options.h"
9 
10 #include "geolocation.h"
11 #include "kstarsdata.h"
12 #include "dialogs/locationdialog.h"
13 #include "skycomponents/constellationboundarylines.h"
14 #include "skycomponents/catalogscomponent.h"
15 #include "skycomponents/skymapcomposite.h"
16 #include "catalogobject.h"
17 #include "catalogsdb.h"
18 
19 ObsListWizardUI::ObsListWizardUI(QWidget *p) : QFrame(p)
20 {
21  setupUi(this);
22 }
23 
24 ObsListWizard::ObsListWizard(QWidget *ksparent) : QDialog(ksparent)
25 {
26 #ifdef Q_OS_OSX
28 #endif
29  olw = new ObsListWizardUI(this);
30  QVBoxLayout *mainLayout = new QVBoxLayout;
31  mainLayout->addWidget(olw);
32  setLayout(mainLayout);
33 
34  setWindowTitle(i18nc("@title:window", "Observing List Wizard"));
35 
37  nextB = new QPushButton(i18n("&Next >"));
38  nextB->setDefault(true);
39  backB = new QPushButton(i18n("< &Back"));
40  backB->setEnabled(false);
41 
42  buttonBox->addButton(backB, QDialogButtonBox::ActionRole);
43  buttonBox->addButton(nextB, QDialogButtonBox::ActionRole);
44  mainLayout->addWidget(buttonBox);
45 
46  connect(nextB, SIGNAL(clicked()), this, SLOT(slotNextPage()));
47  connect(backB, SIGNAL(clicked()), this, SLOT(slotPrevPage()));
48  connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotApplyFilters()));
49  connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
50  connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
51 
52  connect(olw->AllButton, SIGNAL(clicked()), this, SLOT(slotAllButton()));
53  connect(olw->NoneButton, SIGNAL(clicked()), this, SLOT(slotNoneButton()));
54  connect(olw->DeepSkyButton, SIGNAL(clicked()), this, SLOT(slotDeepSkyButton()));
55  connect(olw->SolarSystemButton, SIGNAL(clicked()), this, SLOT(slotSolarSystemButton()));
56  connect(olw->LocationButton, SIGNAL(clicked()), this, SLOT(slotChangeLocation()));
57 
58  //Update the count of objects when the user asks for it
59  connect(olw->updateButton, SIGNAL(clicked()), this, SLOT(slotUpdateObjectCount()));
60 
61  // Enable the update count button when certain elements are changed
62  connect(olw->TypeList, &QListWidget::itemSelectionChanged, this, &ObsListWizard::slotObjectCountDirty);
63  connect(olw->ConstellationList, &QListWidget::itemSelectionChanged, this, &ObsListWizard::slotObjectCountDirty);
64  connect(olw->RAMin, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
65  connect(olw->RAMax, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
66  connect(olw->DecMin, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
67  connect(olw->DecMax, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
68  connect(olw->RA, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
69  connect(olw->Dec, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion);
70  connect(olw->Radius, &QLineEdit::editingFinished, this, &ObsListWizard::slotObjectCountDirty);
71  connect(olw->Date, &QDateEdit::dateChanged, this, &ObsListWizard::slotObjectCountDirty);
72  connect(olw->Mag, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
73  &ObsListWizard::slotObjectCountDirty);
74  connect(olw->IncludeNoMag, &QPushButton::clicked, this, &ObsListWizard::slotObjectCountDirty);
75  connect(olw->timeTo, &QTimeEdit::timeChanged, this, &ObsListWizard::slotObjectCountDirty);
76  connect(olw->timeFrom, &QTimeEdit::timeChanged, this, &ObsListWizard::slotObjectCountDirty);
77  connect(olw->minAlt, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
78  &ObsListWizard::slotObjectCountDirty);
79  connect(olw->maxAlt, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
80  &ObsListWizard::slotObjectCountDirty);
81 
82  olw->coverage->setValue(Options::obsListCoverage());
83  connect(olw->coverage, static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), [&](double value)
84  {
85  Options::setObsListCoverage(value);
86  slotObjectCountDirty();
87  });
88 
89  connect(olw->SelectByDate, SIGNAL(clicked()), this, SLOT(slotToggleDateWidgets()));
90  connect(olw->SelectByMagnitude, SIGNAL(clicked()), this, SLOT(slotToggleMagWidgets()));
91 
92  geo = KStarsData::Instance()->geo();
93  olw->LocationButton->setText(geo->fullName());
94  olw->Date->setDate(KStarsDateTime::currentDateTime().date());
95  olw->timeFrom->setTime(QTime(18, 0));
96  olw->timeTo->setTime(QTime(23, 59));
97 
98  initialize();
99 }
100 
101 void ObsListWizard::initialize()
102 {
103  KStarsData *data = KStarsData::Instance();
104  olw->olwStack->setCurrentIndex(0);
105 
106  //Populate the list of constellations
107  foreach (SkyObject *p, data->skyComposite()->constellationNames())
108  olw->ConstellationList->addItem(p->name());
109 
110  //unSelect all object types
111  olw->TypeList->clearSelection();
112 
113  olw->Mag->setMinimum(-5.0);
114  olw->Mag->setMaximum(20.0);
115  olw->Mag->setValue(6.0);
116 
117  olw->RA->setUnits(dmsBox::HOURS);
118  olw->RAMin->setUnits(dmsBox::HOURS);
119  olw->RAMax->setUnits(dmsBox::HOURS);
120 
121  //Initialize object counts
122  ObjectCount = 0; //number of objects in observing list
123  StarCount = data->skyComposite()->stars().size(); //total number of stars
124  PlanetCount = 10; //Sun, Moon, 8 planets
125  AsteroidCount = data->skyComposite()->asteroids().size(); //total number of asteroids
126  CometCount = data->skyComposite()->comets().size(); //total number of comets
127  //DeepSkyObjects
128  OpenClusterCount = 0;
129  GlobClusterCount = 0;
130  GasNebCount = 0;
131  PlanNebCount = 0;
132  GalaxyCount = 0;
133 
134  CatalogsDB::DBManager manager{ CatalogsDB::dso_db_path() };
135 
136  const auto &stats{ manager.get_master_statistics() };
137  if (!stats.first)
138  return;
139 
140  for (const auto &element : stats.second.object_counts)
141  {
142  auto cnt = element.second;
143  switch (element.first)
144  {
145  case SkyObject::GALAXY:
146  GalaxyCount += cnt;
147  break;
148  case SkyObject::STAR:
149  case SkyObject::CATALOG_STAR:
150  StarCount += cnt;
151  break;
152  case SkyObject::OPEN_CLUSTER:
153  OpenClusterCount += cnt;
154  break;
155  case SkyObject::GLOBULAR_CLUSTER:
156  GlobClusterCount += cnt;
157  break;
158  case SkyObject::GASEOUS_NEBULA:
159  case SkyObject::SUPERNOVA_REMNANT:
160  GasNebCount += cnt;
161  break;
162  case SkyObject::PLANETARY_NEBULA:
163  PlanNebCount += cnt;
164  break;
165  default:
166  break;
167  }
168  }
169 }
170 
171 bool ObsListWizard::isItemSelected(const QString &name, QListWidget *listWidget, bool *ok)
172 {
173  /*QList<QListWidgetItem *> items = listWidget->findItems(name, Qt::MatchContains);
174  if (ok)
175  *ok = items.size();
176  return items.size() && listWidget->isItemSelected(items[0]);
177  ;*/
178  foreach(QListWidgetItem *item, listWidget->selectedItems())
179  {
180  if (item->text().compare(name, Qt::CaseInsensitive) == 0)
181  {
182  if (ok)
183  *ok = true;
184  return true;
185  }
186  }
187 
188  if (ok)
189  *ok = false;
190  return false;
191 }
192 
193 void ObsListWizard::setItemSelected(const QString &name, QListWidget *listWidget, bool value, bool *ok)
194 {
195  QList<QListWidgetItem *> items = listWidget->findItems(name, Qt::MatchContains);
196  if (ok)
197  *ok = items.size();
198  if (items.size())
199  items[0]->setSelected(value);
200 }
201 
202 //Advance to the next page in the stack. However, on page 2 the user
203 //selects what regional filter they want to use, and this determines
204 //what the page following page 2 should be:
205 // + Constellation(s): the next page index is 3
206 // + Rectangular region: the next page index is 4
207 // + Circular region: the next page index is 5
208 // + No region selected (a.k.a. "all over the sky"): the next page index is 6
209 //
210 //Also, if the current page index is 3 or 4, then the next page should be 6.
211 //
212 //NOTE: the page indexes are hard-coded here, which isn't ideal. However,
213 //There's no easy way to access the pointers of widgets in the stack
214 //if you didn't save them at the start.
215 void ObsListWizard::slotNextPage()
216 {
217  int NextPage = olw->olwStack->currentIndex() + 1;
218 
219  if (olw->olwStack->currentIndex() == 2)
220  {
221  //On the Region select page. Determine what
222  //the next page index should be.
223  //No need to handle "by constellation, it's already currentIndex + 1.
224  if (isItemSelected(i18n("in a rectangular region"), olw->RegionList))
225  NextPage = 4;
226  if (isItemSelected(i18n("in a circular region"), olw->RegionList))
227  NextPage = 5;
228  if (isItemSelected(i18n("all over the sky"), olw->RegionList))
229  NextPage = 6;
230  }
231 
232  if (olw->olwStack->currentIndex() == 3 || olw->olwStack->currentIndex() == 4)
233  NextPage = 6;
234 
235  olw->olwStack->setCurrentIndex(NextPage);
236 
237  if (olw->olwStack->currentIndex() == olw->olwStack->count() - 1)
238  nextB->setEnabled(false);
239 
240  backB->setEnabled(true);
241 }
242 
243 //Advance to the previous page in the stack. However, because the
244 //path through the wizard branches depending on the user's choice of
245 //Region filter, the previous page is not always currentPage-1.
246 //Specifically, if the current page index is 4, 5, or 6, then the Previous
247 //page index should be 2 rather than currentIndex-1.
248 void ObsListWizard::slotPrevPage()
249 {
250  int PrevPage = olw->olwStack->currentIndex() - 1;
251 
252  if (olw->olwStack->currentIndex() == 4 || olw->olwStack->currentIndex() == 5 || olw->olwStack->currentIndex() == 6)
253  PrevPage = 2;
254 
255  olw->olwStack->setCurrentIndex(PrevPage);
256 
257  if (olw->olwStack->currentIndex() == 0)
258  backB->setEnabled(false);
259 
260  nextB->setEnabled(true);
261 }
262 
263 void ObsListWizard::slotAllButton()
264 {
265  for (int i = 0; i < olw->TypeList->count(); ++i)
266  olw->TypeList->item(i)->setSelected(true);
267 }
268 
269 void ObsListWizard::slotNoneButton()
270 {
271  olw->TypeList->clearSelection();
272 }
273 
274 void ObsListWizard::slotDeepSkyButton()
275 {
276  olw->TypeList->clearSelection();
277  setItemSelected(i18n("Open clusters"), olw->TypeList, true);
278  setItemSelected(i18n("Globular clusters"), olw->TypeList, true);
279  setItemSelected(i18n("Gaseous nebulae"), olw->TypeList, true);
280  setItemSelected(i18n("Planetary nebulae"), olw->TypeList, true);
281  setItemSelected(i18n("Galaxies"), olw->TypeList, true);
282 }
283 
284 void ObsListWizard::slotSolarSystemButton()
285 {
286  olw->TypeList->clearSelection();
287  setItemSelected(i18n("Sun, moon, planets"), olw->TypeList, true);
288  setItemSelected(i18n("Comets"), olw->TypeList, true);
289  setItemSelected(i18n("Asteroids"), olw->TypeList, true);
290 }
291 
292 void ObsListWizard::slotChangeLocation()
293 {
294  QPointer<LocationDialog> ld = new LocationDialog(this);
295 
296  if (ld->exec() == QDialog::Accepted)
297  {
298  //set geographic location
299  if (ld->selectedCity())
300  {
301  geo = ld->selectedCity();
302  olw->LocationButton->setText(geo->fullName());
303  }
304  }
305  delete ld;
306 }
307 
308 void ObsListWizard::slotToggleDateWidgets()
309 {
310  olw->Date->setEnabled(olw->SelectByDate->isChecked());
311  olw->LocationButton->setEnabled(olw->SelectByDate->isChecked());
312  olw->timeTo->setEnabled(olw->SelectByDate->isChecked());
313  olw->timeFrom->setEnabled(olw->SelectByDate->isChecked());
314  olw->minAlt->setEnabled(olw->SelectByDate->isChecked());
315  olw->maxAlt->setEnabled(olw->SelectByDate->isChecked());
316 
317  // slotUpdateObjectCount();
318  slotObjectCountDirty();
319 }
320 
321 void ObsListWizard::slotToggleMagWidgets()
322 {
323  olw->Mag->setEnabled(olw->SelectByMagnitude->isChecked());
324  olw->IncludeNoMag->setEnabled(olw->SelectByMagnitude->isChecked());
325  slotObjectCountDirty();
326  // slotUpdateObjectCount();
327 }
328 
329 void ObsListWizard::slotParseRegion()
330 {
331  if (sender()->objectName() == "RAMin" || sender()->objectName() == "RAMax" || sender()->objectName() == "DecMin" ||
332  sender()->objectName() == "DecMax")
333  {
334  if (!olw->RAMin->isEmpty() && !olw->RAMax->isEmpty() && !olw->DecMin->isEmpty() && !olw->DecMax->isEmpty())
335  {
336  bool rectOk = false;
337  xRect1 = 0.0;
338  xRect2 = 0.0;
339  yRect1 = 0.0;
340  yRect2 = 0.0;
341 
342  xRect1 = olw->RAMin->createDms(&rectOk).Hours();
343  if (rectOk)
344  xRect2 = olw->RAMax->createDms(&rectOk).Hours();
345  if (rectOk)
346  yRect1 = olw->DecMin->createDms(&rectOk).Degrees();
347  if (rectOk)
348  yRect2 = olw->DecMax->createDms(&rectOk).Degrees();
349  if (xRect2 == 0.0)
350  xRect2 = 24.0;
351 
352  if (!rectOk)
353  {
354  // qWarning() << i18n( "Illegal rectangle specified, no region selection possible." ) ;
355  return;
356  }
357 
358  //Make sure yRect1 < yRect2.
359  if (yRect1 > yRect2)
360  {
361  double temp = yRect2;
362  yRect2 = yRect1;
363  yRect1 = temp;
364  }
365 
366  //If xRect1 > xRect2, we may need to swap the two values, or subtract 24h from xRect1.
367  if (xRect1 > xRect2)
368  {
369  if (xRect1 - xRect2 > 12.0) //the user probably wants a region that straddles 0h
370  {
371  xRect1 -= 24.0;
372  }
373  else //the user probably wants xRect2 to be the lower limit
374  {
375  double temp = xRect2;
376  xRect2 = xRect1;
377  xRect1 = temp;
378  }
379  }
380 
381  // slotUpdateObjectCount();
382  slotObjectCountDirty();
383  }
384  }
385  else if (!olw->RA->isEmpty() && !olw->Dec->isEmpty() && !olw->Radius->isEmpty())
386  {
387  bool circOk;
388  dms ra = olw->RA->createDms(&circOk);
389  dms dc;
390  if (circOk)
391  dc = olw->Dec->createDms(&circOk);
392  if (circOk)
393  {
394  pCirc.set(ra, dc);
395  rCirc = olw->Radius->createDms(&circOk).Degrees();
396  }
397  else
398  {
399  qWarning() << i18n("Illegal circle specified, no region selection possible.");
400  return;
401  }
402  // slotUpdateObjectCount();
403  slotObjectCountDirty();
404  }
405 }
406 
407 void ObsListWizard::slotObjectCountDirty()
408 {
409  olw->updateButton->setDisabled(false);
410 }
411 
412 void ObsListWizard::slotUpdateObjectCount()
413 {
415  ObjectCount = 0;
416  if (isItemSelected(i18n("Stars"), olw->TypeList))
417  ObjectCount += StarCount;
418  if (isItemSelected(i18n("Sun, moon, planets"), olw->TypeList))
419  ObjectCount += PlanetCount;
420  if (isItemSelected(i18n("Comets"), olw->TypeList))
421  ObjectCount += CometCount;
422  if (isItemSelected(i18n("Asteroids"), olw->TypeList))
423  ObjectCount += AsteroidCount;
424  if (isItemSelected(i18n("Galaxies"), olw->TypeList))
425  ObjectCount += GalaxyCount;
426  if (isItemSelected(i18n("Open clusters"), olw->TypeList))
427  ObjectCount += OpenClusterCount;
428  if (isItemSelected(i18n("Globular clusters"), olw->TypeList))
429  ObjectCount += GlobClusterCount;
430  if (isItemSelected(i18n("Gaseous nebulae"), olw->TypeList))
431  ObjectCount += GasNebCount;
432  if (isItemSelected(i18n("Planetary nebulae"), olw->TypeList))
433  ObjectCount += PlanNebCount;
434 
435  applyFilters(false); //false = only adjust counts, do not build list
437  olw->updateButton->setDisabled(true);
438 }
439 
440 void ObsListWizard::applyFilters(bool doBuildList)
441 {
442  bool filterPass = true;
443  KStarsData *data = KStarsData::Instance();
444  if (doBuildList)
445  obsList().clear();
446 
447  //We don't need to call applyRegionFilter() if no region filter is selected, *and*
448  //we are just counting items (i.e., doBuildList is false)
449  bool needRegion = true;
450  if (!doBuildList && isItemSelected(i18n("all over the sky"), olw->RegionList))
451  needRegion = false;
452 
453  double maglimit = 100.;
454  if (olw->SelectByMagnitude->isChecked())
455  maglimit = olw->Mag->value();
456 
457  //Stars
458  if (isItemSelected(i18n("Stars"), olw->TypeList))
459  {
460  const QList<SkyObject *> &starList = data->skyComposite()->stars();
461  int starIndex(starList.size());
462  if (maglimit < 100.)
463  {
464  //Stars are sorted by mag, so use binary search algo to find index of faintest mag
465  int low(0), high(starList.size() - 1), middle(high);
466  while (low < high)
467  {
468  middle = (low + high) / 2;
469  if (maglimit == starList.at(middle)->mag())
470  break;
471  if (maglimit < starList.at(middle)->mag())
472  high = middle - 1;
473  if (maglimit > starList.at(middle)->mag())
474  low = middle + 1;
475  }
476  //now, the star at "middle" has the right mag, but we want the *last* star that has this mag.
477  for (starIndex = middle + 1; starIndex < starList.size(); ++starIndex)
478  {
479  if (starList.at(starIndex)->mag() > maglimit)
480  break;
481  }
482  }
483 
484  //DEBUG
485  qDebug() << Q_FUNC_INFO << QString("starIndex for mag %1: %2").arg(maglimit).arg(starIndex);
486 
487  if (!doBuildList)
488  {
489  //reduce StarCount by appropriate amount
490  ObjectCount -= StarCount;
491  ObjectCount += starIndex;
492  }
493  for (int i = 0; i < starIndex; ++i)
494  {
495  SkyObject *o = (SkyObject *)(starList[i]);
496 
497  // JM 2012-10-22: Skip unnamed stars
498  if (o->name() == "star")
499  {
500  if (!doBuildList)
501  --ObjectCount;
502  continue;
503  }
504 
505  if (needRegion)
506  filterPass = applyRegionFilter(o, doBuildList, !doBuildList);
507  //Filter objects visible from geo at Date if region filter passes
508  if (olw->SelectByDate->isChecked() && filterPass)
509  applyObservableFilter(o, doBuildList, !doBuildList);
510  }
511  }
512 
513  //Sun, Moon, Planets
514  if (isItemSelected(i18n("Sun, moon, planets"), olw->TypeList))
515  {
516  if (maglimit < data->skyComposite()->findByName(i18n("Sun"))->mag())
517  {
518  if (!doBuildList)
519  --ObjectCount;
520  filterPass = false;
521  }
522  else
523  filterPass = true;
524 
525  if (needRegion && filterPass)
526  filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Sun")), doBuildList);
527  if (olw->SelectByDate->isChecked() && filterPass)
528  applyObservableFilter(data->skyComposite()->findByName(i18n("Sun")), doBuildList);
529 
530  if (maglimit < data->skyComposite()->findByName(i18n("Moon"))->mag())
531  {
532  if (!doBuildList)
533  --ObjectCount;
534  filterPass = false;
535  }
536  else
537  filterPass = true;
538 
539  if (needRegion && filterPass)
540  filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Moon")), doBuildList);
541  if (olw->SelectByDate->isChecked() && filterPass)
542  applyObservableFilter(data->skyComposite()->findByName(i18n("Moon")), doBuildList);
543 
544  if (maglimit < data->skyComposite()->findByName(i18n("Mercury"))->mag())
545  {
546  if (!doBuildList)
547  --ObjectCount;
548  filterPass = false;
549  }
550  else
551  filterPass = true;
552  if (needRegion && filterPass)
553  filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Mercury")), doBuildList);
554  if (olw->SelectByDate->isChecked() && filterPass)
555  applyObservableFilter(data->skyComposite()->findByName(i18n("Mercury")), doBuildList);
556 
557  if (maglimit < data->skyComposite()->findByName(i18n("Venus"))->mag())
558  {
559  if (!doBuildList)
560  --ObjectCount;
561  filterPass = false;
562  }
563  else
564  filterPass = true;
565 
566  if (needRegion && filterPass)
567  filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Venus")), doBuildList);
568  if (olw->SelectByDate->isChecked() && filterPass)
569  applyObservableFilter(data->skyComposite()->findByName(i18n("Venus")), doBuildList);
570 
571  if (maglimit < data->skyComposite()->findByName(i18n("Mars"))->mag())
572  {
573  if (!doBuildList)
574  --ObjectCount;
575  filterPass = false;
576  }
577  else
578  filterPass = true;
579  if (needRegion && filterPass)
580  filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Mars")), doBuildList);
581  if (olw->SelectByDate->isChecked() && filterPass)
582  applyObservableFilter(data->skyComposite()->findByName(i18n("Mars")), doBuildList);
583 
584  if (maglimit < data->skyComposite()->findByName(i18n("Jupiter"))->mag())
585  {
586  if (!doBuildList)
587  --ObjectCount;
588  filterPass = false;
589  }
590  else
591  filterPass = true;
592  if (needRegion && filterPass)
593  filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Jupiter")), doBuildList);
594  if (olw->SelectByDate->isChecked() && filterPass)
595  applyObservableFilter(data->skyComposite()->findByName(i18n("Jupiter")), doBuildList);
596 
597  if (maglimit < data->skyComposite()->findByName(i18n("Saturn"))->mag())
598  {
599  if (!doBuildList)
600  --ObjectCount;
601  filterPass = false;
602  }
603  else
604  filterPass = true;
605 
606  if (needRegion && filterPass)
607  filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Saturn")), doBuildList);
608  if (olw->SelectByDate->isChecked() && filterPass)
609  applyObservableFilter(data->skyComposite()->findByName(i18n("Saturn")), doBuildList);
610 
611  if (maglimit < data->skyComposite()->findByName(i18n("Uranus"))->mag())
612  {
613  if (!doBuildList)
614  --ObjectCount;
615  filterPass = false;
616  }
617  else
618  filterPass = true;
619 
620  if (needRegion && filterPass)
621  filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Uranus")), doBuildList);
622  if (olw->SelectByDate->isChecked() && filterPass)
623  applyObservableFilter(data->skyComposite()->findByName(i18n("Uranus")), doBuildList);
624 
625  if (maglimit < data->skyComposite()->findByName(i18n("Neptune"))->mag())
626  {
627  if (!doBuildList)
628  --ObjectCount;
629  filterPass = false;
630  }
631  else
632  filterPass = true;
633 
634  if (needRegion && filterPass)
635  filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Neptune")), doBuildList);
636  if (olw->SelectByDate->isChecked() && filterPass)
637  applyObservableFilter(data->skyComposite()->findByName(i18n("Neptune")), doBuildList);
638 
639  // if (maglimit < data->skyComposite()->findByName(i18nc("Asteroid name (optional)", "Pluto"))->mag())
640  // {
641  // if (!doBuildList)
642  // --ObjectCount;
643  // filterPass = false;
644  // }
645  // else
646  // filterPass = true;
647 
648  // if (needRegion && filterPass)
649  // filterPass = applyRegionFilter(data->skyComposite()->findByName(i18nc("Asteroid name (optional)", "Pluto")), doBuildList);
650  // if (olw->SelectByDate->isChecked() && filterPass)
651  // applyObservableFilter(data->skyComposite()->findByName(i18nc("Asteroid name (optional)", "Pluto")), doBuildList);
652  }
653 
654  //Deep sky objects
655  bool dso =
656  (isItemSelected(i18n("Open clusters"), olw->TypeList) ||
657  isItemSelected(i18n("Globular clusters"), olw->TypeList) ||
658  isItemSelected(i18n("Gaseous nebulae"), olw->TypeList) ||
659  isItemSelected(i18n("Planetary nebulae"), olw->TypeList) || isItemSelected(i18n("Galaxies"), olw->TypeList));
660 
661  if (dso)
662  {
663  //Don't need to do anything if we are just counting objects and not
664  //filtering by region or magnitude
665  if (needRegion || olw->SelectByMagnitude->isChecked() || olw->SelectByDate->isChecked())
666  {
667 
668  CatalogsDB::DBManager manager{ CatalogsDB::dso_db_path() };
669 
670  for(auto& o : manager.get_objects(olw->SelectByMagnitude->isChecked() ? maglimit : 99))
671  {
672  //Skip unselected object types
673  bool typeSelected = false;
674  // if ( (o->type() == SkyObject::STAR || o->type() == SkyObject::CATALOG_STAR) &&
675  // isItemSelected( i18n( "Stars" ), olw->TypeList ) )
676  // typeSelected = true;
677  switch (o.type())
678  {
679  case SkyObject::OPEN_CLUSTER:
680  if (isItemSelected(i18n("Open clusters"), olw->TypeList))
681  typeSelected = true;
682  break;
683 
684  case SkyObject::GLOBULAR_CLUSTER:
685  if (isItemSelected(i18n("Globular clusters"), olw->TypeList))
686  typeSelected = true;
687  break;
688 
689  case SkyObject::GASEOUS_NEBULA:
690  case SkyObject::SUPERNOVA_REMNANT:
691  if (isItemSelected(i18n("Gaseous nebulae"), olw->TypeList))
692  typeSelected = true;
693  break;
694 
695  case SkyObject::PLANETARY_NEBULA:
696  if (isItemSelected(i18n("Planetary nebulae"), olw->TypeList))
697  typeSelected = true;
698  break;
699  case SkyObject::GALAXY:
700  if (isItemSelected(i18n("Galaxies"), olw->TypeList))
701  typeSelected = true;
702  break;
703  }
704 
705  if (!typeSelected)
706  continue;
707 
708  if (olw->SelectByMagnitude->isChecked())
709  {
710  if (o.mag() > 90.)
711  {
712  if (olw->IncludeNoMag->isChecked())
713  {
714  auto *obj = &o;
715  if(doBuildList)
716  obj = &data->skyComposite()->catalogsComponent()
717  ->insertStaticObject(o);
718 
719  if (needRegion)
720  filterPass = applyRegionFilter(obj, doBuildList);
721  if (olw->SelectByDate->isChecked() && filterPass)
722  applyObservableFilter(obj, doBuildList);
723  }
724  else if (!doBuildList)
725  --ObjectCount;
726  }
727  else
728  {
729  if (o.mag() <= maglimit)
730  {
731  auto *obj = &o;
732  if(doBuildList)
733  obj = &data->skyComposite()->catalogsComponent()
734  ->insertStaticObject(o);
735 
736  if (needRegion)
737  filterPass = applyRegionFilter(obj, doBuildList);
738  if (olw->SelectByDate->isChecked() && filterPass)
739  applyObservableFilter(obj, doBuildList);
740  }
741  else if (!doBuildList)
742  --ObjectCount;
743  }
744  }
745  else
746  {
747  auto *obj = &o;
748  if(doBuildList)
749  obj = &data->skyComposite()->catalogsComponent()
750  ->insertStaticObject(o);
751 
752  if (needRegion)
753  filterPass = applyRegionFilter(obj, doBuildList);
754  if (olw->SelectByDate->isChecked() && filterPass)
755  applyObservableFilter(obj, doBuildList);
756  }
757  }
758  }
759  }
760 
761  //Comets
762  if (isItemSelected(i18n("Comets"), olw->TypeList))
763  {
764  foreach (SkyObject *o, data->skyComposite()->comets())
765  {
766  if (olw->SelectByMagnitude->isChecked())
767  {
768  if (o->mag() > 90.)
769  {
770  if (olw->IncludeNoMag->isChecked())
771  {
772  if (needRegion)
773  filterPass = applyRegionFilter(o, doBuildList);
774  if (olw->SelectByDate->isChecked() && filterPass)
775  applyObservableFilter(o, doBuildList);
776  }
777  else if (!doBuildList)
778  --ObjectCount;
779  }
780  else
781  {
782  if (o->mag() <= maglimit)
783  {
784  if (needRegion)
785  filterPass = applyRegionFilter(o, doBuildList);
786  if (olw->SelectByDate->isChecked() && filterPass)
787  applyObservableFilter(o, doBuildList);
788  }
789  else if (!doBuildList)
790  --ObjectCount;
791  }
792  }
793  else
794  {
795  if (needRegion)
796  filterPass = applyRegionFilter(o, doBuildList);
797  if (olw->SelectByDate->isChecked() && filterPass)
798  applyObservableFilter(o, doBuildList);
799  }
800  }
801  }
802 
803  //Asteroids
804  if (isItemSelected(i18n("Asteroids"), olw->TypeList))
805  {
806  foreach (SkyObject *o, data->skyComposite()->asteroids())
807  {
808  if (olw->SelectByMagnitude->isChecked())
809  {
810  if (o->mag() > 90.)
811  {
812  if (olw->IncludeNoMag->isChecked())
813  {
814  if (needRegion)
815  filterPass = applyRegionFilter(o, doBuildList);
816  if (olw->SelectByDate->isChecked() && filterPass)
817  applyObservableFilter(o, doBuildList);
818  }
819  else if (!doBuildList)
820  --ObjectCount;
821  }
822  else
823  {
824  if (o->mag() <= maglimit)
825  {
826  if (needRegion)
827  filterPass = applyRegionFilter(o, doBuildList);
828  if (olw->SelectByDate->isChecked() && filterPass)
829  applyObservableFilter(o, doBuildList);
830  }
831  else if (!doBuildList)
832  --ObjectCount;
833  }
834  }
835  else
836  {
837  if (needRegion)
838  filterPass = applyRegionFilter(o, doBuildList);
839  if (olw->SelectByDate->isChecked() && filterPass)
840  applyObservableFilter(o, doBuildList);
841  }
842  }
843  }
844 
845  //Update the object count label
846  if (doBuildList)
847  ObjectCount = obsList().size();
848 
849  olw->CountLabel->setText(i18np("Your observing list currently has 1 object",
850  "Your observing list currently has %1 objects", ObjectCount));
851 }
852 
853 bool ObsListWizard::applyRegionFilter(SkyObject *o, bool doBuildList, bool doAdjustCount)
854 {
855  //select by constellation
856  if (isItemSelected(i18n("by constellation"), olw->RegionList))
857  {
858  QString c = KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(o);
859 
860  if (isItemSelected(c, olw->ConstellationList))
861  {
862  if (doBuildList)
863  obsList().append(o);
864 
865  return true;
866  }
867  else if (doAdjustCount)
868  {
869  --ObjectCount;
870  return false;
871  }
872  else
873  return false;
874  }
875 
876  //select by rectangular region
877  else if (isItemSelected(i18n("in a rectangular region"), olw->RegionList))
878  {
879  double ra = o->ra().Hours();
880  double dec = o->dec().Degrees();
881  bool addObject = false;
882  if (dec >= yRect1 && dec <= yRect2)
883  {
884  if (xRect1 < 0.0)
885  {
886  addObject = ra >= xRect1 + 24.0 || ra <= xRect2;
887  }
888  else
889  {
890  addObject = ra >= xRect1 && ra <= xRect2;
891  }
892  }
893 
894  if (addObject)
895  {
896  if (doBuildList)
897  obsList().append(o);
898 
899  return true;
900  }
901 
902  else
903  {
904  if (doAdjustCount)
905  --ObjectCount;
906 
907  return false;
908  }
909  }
910 
911  //select by circular region
912  //make sure circ region data are valid
913  else if (isItemSelected(i18n("in a circular region"), olw->RegionList))
914  {
915  if (o->angularDistanceTo(&pCirc).Degrees() < rCirc)
916  {
917  if (doBuildList)
918  obsList().append(o);
919 
920  return true;
921  }
922  else if (doAdjustCount)
923  {
924  --ObjectCount;
925  return false;
926  }
927  else
928  return false;
929  }
930 
931  //No region filter, just add the object
932  else if (doBuildList)
933  {
934  obsList().append(o);
935  }
936 
937  return true;
938 }
939 
940 bool ObsListWizard::applyObservableFilter(SkyObject *o, bool doBuildList, bool doAdjustCount)
941 {
942  SkyPoint p = *o;
943 
944  //Check altitude of object every hour from 18:00 to midnight
945  //If it's ever above 15 degrees, flag it as visible
946  KStarsDateTime Evening(olw->Date->date(), QTime(18, 0, 0), Qt::LocalTime);
947  KStarsDateTime Midnight(olw->Date->date().addDays(1), QTime(0, 0, 0), Qt::LocalTime);
948  double minAlt = 15, maxAlt = 90;
949 
950  // Or use user-selected values, if they're valid
951  if (olw->timeFrom->time().isValid() && olw->timeTo->time().isValid())
952  {
953  Evening.setTime(olw->timeFrom->time());
954  Midnight.setTime(olw->timeTo->time());
955 
956  // If time from < timeTo (e.g. 06:00 PM to 9:00 PM)
957  // then we stay on the same day.
958  if (olw->timeFrom->time() < olw->timeTo->time())
959  {
960  Midnight.setDate(olw->Date->date());
961  }
962  // Otherwise we advance by one day
963  else
964  {
965  Midnight.setDate(olw->Date->date().addDays(1));
966  }
967  }
968 
969  minAlt = olw->minAlt->value();
970  maxAlt = olw->maxAlt->value();
971 
972  // This is the "relaxed" search mode
973  // where if the object obeys the restrictions in 50% of the time of the range
974  // then it qualifies as "visible"
975  double totalCount = 0, visibleCount = 0;
976  for (KStarsDateTime t = Evening; t < Midnight; t = t.addSecs(3600.0))
977  {
978  dms LST = geo->GSTtoLST(t.gst());
979  p.EquatorialToHorizontal(&LST, geo->lat());
980  totalCount++;
981  if (p.alt().Degrees() >= minAlt && p.alt().Degrees() <= maxAlt)
982  visibleCount++;
983  }
984 
985  // If the object is within the min/max alt at least coverage % of the time range
986  // then consider it visible
987  if (visibleCount / totalCount >= olw->coverage->value() / 100.0)
988  return true;
989 
990  if (doAdjustCount)
991  --ObjectCount;
992  if (doBuildList)
993  obsList().takeAt(obsList().indexOf(o));
994 
995  return false;
996 
997  // This is the strict mode where ANY object that does not meet the min & max
998  // altitude at ANY time would be removed from the list.
999 #if 0
1000  for (KStarsDateTime t = Evening; t < Midnight; t = t.addSecs(3600.0))
1001  {
1002  dms LST = geo->GSTtoLST(t.gst());
1003  p.EquatorialToHorizontal(&LST, geo->lat());
1004  if (p.alt().Degrees() < minAlt || p.alt().Degrees() > maxAlt)
1005  {
1006  if (doAdjustCount)
1007  --ObjectCount;
1008  if (doBuildList)
1009  obsList().takeAt(obsList().indexOf(o));
1010  return false;
1011  }
1012  }
1013  return true;
1014 #endif
1015 
1016 }
const dms & alt() const
Definition: skypoint.h:281
QString text() const const
static KStarsDateTime currentDateTime()
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
CaseInsensitive
LocalTime
Stores dms coordinates for a point in the sky. for converting between coordinate systems.
Definition: skypoint.h:44
StandardShortcut findByName(const QString &name)
void clicked(bool checked)
void editingFinished()
virtual QString name(void) const
Definition: skyobject.h:145
float mag() const
Definition: skyobject.h:206
Manages the catalog database and provides an interface to provide an interface to query and modify th...
Definition: catalogsdb.h:181
void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates,...
Definition: skypoint.cpp:77
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void valueChanged(double d)
KStarsDateTime addSecs(double s) const
void timeChanged(const QTime &time)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void dateChanged(const QDate &date)
int type(void) const
Definition: skyobject.h:188
void initialize(StandardShortcut id)
int size() const const
SkyObject * findByName(const QString &name, bool exact=true) override
Search the children of this SkyMapComposite for a SkyObject whose name matches the argument.
QString i18n(const char *text, const TYPE &arg...)
void setWindowFlags(Qt::WindowFlags type)
const CachingDms & dec() const
Definition: skypoint.h:269
CatalogObject & insertStaticObject(const CatalogObject &obj)
Insert an object obj into m_static_objects and return a reference to the newly inserted object.
Horizontal
GeoLocation * geo()
Definition: kstarsdata.h:229
WaitCursor
void setWindowTitle(const QString &)
const T & at(int i) const const
void addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role)
QTextStream & dec(QTextStream &stream)
GeoCoordinates geo(const QVariant &location)
QList< QListWidgetItem * > selectedItems() const const
void setupUi(QWidget *widget)
dms angularDistanceTo(const SkyPoint *sp, double *const positionAngle=nullptr) const
Computes the angular distance between two SkyObjects.
Definition: skypoint.cpp:899
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
SkyMapComposite * skyComposite()
Definition: kstarsdata.h:165
An angle, stored as degrees, but expressible in many ways.
Definition: dms.h:37
const CachingDms & ra() const
Definition: skypoint.h:263
void setOverrideCursor(const QCursor &cursor)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const double & Degrees() const
Definition: dms.h:141
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void itemSelectionChanged()
int compare(const QString &other, Qt::CaseSensitivity cs) const const
void restoreOverrideCursor()
MatchContains
void setLayout(QLayout *layout)
Information about an object in the sky.
Definition: skyobject.h:41
double Hours() const
Definition: dms.h:168
QList< QListWidgetItem * > findItems(const QString &text, Qt::MatchFlags flags) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri Aug 12 2022 04:00:56 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.