Kstars

altvstime.cpp
1 /*
2  SPDX-FileCopyrightText: 2002-2003 Pablo de Vicente <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "altvstime.h"
8 
9 #include "avtplotwidget.h"
10 #include "dms.h"
11 #include "ksalmanac.h"
12 #include "kstarsdata.h"
13 #include "kstarsdatetime.h"
14 #include "ksnumbers.h"
15 #include "simclock.h"
16 #include "kssun.h"
17 #include "dialogs/finddialog.h"
18 #include "dialogs/locationdialog.h"
19 #include "geolocation.h"
20 #include "skyobjects/skypoint.h"
21 #include "skyobjects/skyobject.h"
22 #include "skyobjects/starobject.h"
23 
24 #include <KLocalizedString>
25 #include <kplotwidget.h>
26 
27 #include <QVBoxLayout>
28 #include <QFrame>
29 #include <QDialog>
30 #include <QPainter>
31 #include <QtPrintSupport/QPrinter>
32 #include <QtPrintSupport/QPrintDialog>
33 
34 #include "kstars_debug.h"
35 
36 AltVsTimeUI::AltVsTimeUI(QWidget *p) : QFrame(p)
37 {
38  setupUi(this);
39 }
40 
42 {
43 #ifdef Q_OS_OSX
45 #endif
46 
47  setWindowTitle(i18nc("@title:window", "Altitude vs. Time"));
48 
49  setModal(false);
50 
51  QVBoxLayout *topLayout = new QVBoxLayout;
52  setLayout(topLayout);
53  topLayout->setContentsMargins(0, 0, 0, 0);
54  avtUI = new AltVsTimeUI(this);
55 
56  // Layers for setting up the plot's priority: the current curve should be above the other curves.
57  // The Rise/Set/Transit markers should be on top, with highest priority.
58  avtUI->View->addLayer("currentCurveLayer", avtUI->View->layer("main"), QCustomPlot::limAbove);
59  avtUI->View->addLayer("markersLayer", avtUI->View->layer("currentCurveLayer"), QCustomPlot::limAbove);
60 
61  // Set up the Graph Window:
62  avtUI->View->setBackground(QBrush(QColor(0, 0, 0)));
63  avtUI->View->xAxis->grid()->setVisible(false);
64  avtUI->View->yAxis->grid()->setVisible(false);
65  QColor axisColor(Qt::white);
66  QPen axisPen(axisColor, 1);
67  avtUI->View->xAxis->setBasePen(axisPen);
68  avtUI->View->xAxis->setTickPen(axisPen);
69  avtUI->View->xAxis->setSubTickPen(axisPen);
70  avtUI->View->xAxis->setTickLabelColor(axisColor);
71  avtUI->View->xAxis->setLabelColor(axisColor);
72 
73  avtUI->View->xAxis2->setBasePen(axisPen);
74  avtUI->View->xAxis2->setTickPen(axisPen);
75  avtUI->View->xAxis2->setSubTickPen(axisPen);
76  avtUI->View->xAxis2->setTickLabelColor(axisColor);
77  avtUI->View->xAxis2->setLabelColor(axisColor);
78 
79  avtUI->View->yAxis->setBasePen(axisPen);
80  avtUI->View->yAxis->setTickPen(axisPen);
81  avtUI->View->yAxis->setSubTickPen(axisPen);
82  avtUI->View->yAxis->setTickLabelColor(axisColor);
83  avtUI->View->yAxis->setLabelColor(axisColor);
84 
85  avtUI->View->yAxis2->setBasePen(axisPen);
86  avtUI->View->yAxis2->setTickPen(axisPen);
87  avtUI->View->yAxis2->setSubTickPen(axisPen);
88  avtUI->View->yAxis2->setTickLabelColor(axisColor);
89  avtUI->View->yAxis2->setLabelColor(axisColor);
90 
91  // give the axis some labels:
92  avtUI->View->xAxis2->setLabel(i18n("Local Sidereal Time"));
93  avtUI->View->xAxis2->setVisible(true);
94  avtUI->View->yAxis2->setVisible(true);
95  avtUI->View->yAxis2->setTickLength(0, 0);
96  avtUI->View->xAxis->setLabel(i18n("Local Time"));
97  avtUI->View->yAxis->setLabel(i18n("Altitude"));
98  avtUI->View->xAxis->setRange(43200, 129600);
99  avtUI->View->xAxis2->setRange(61200, 147600);
100 
101  // configure the bottom axis to show time instead of number:
103  xAxisTimeTicker->setTimeFormat("%h:%m");
104  xAxisTimeTicker->setTickCount(12);
105  xAxisTimeTicker->setTickStepStrategy(QCPAxisTicker::tssReadability);
106  xAxisTimeTicker->setTickOrigin(Qt::UTC);
107  avtUI->View->xAxis->setTicker(xAxisTimeTicker);
108 
109  // configure the top axis to show time instead of number:
111  xAxis2TimeTicker->setTimeFormat("%h:%m");
112  xAxis2TimeTicker->setTickCount(12);
113  xAxis2TimeTicker->setTickStepStrategy(QCPAxisTicker::tssReadability);
114  xAxis2TimeTicker->setTickOrigin(Qt::UTC);
115  avtUI->View->xAxis2->setTicker(xAxis2TimeTicker);
116 
117  // set up the Zoom/Pan features for the Top X Axis
118  avtUI->View->axisRect()->setRangeDragAxes(avtUI->View->xAxis2, avtUI->View->yAxis);
119  avtUI->View->axisRect()->setRangeZoomAxes(avtUI->View->xAxis2, avtUI->View->yAxis);
120 
121  // set up the margins, for a nice view
122  avtUI->View->axisRect()->setAutoMargins(QCP::msBottom | QCP::msLeft | QCP::msTop);
123  avtUI->View->axisRect()->setMargins(QMargins(0, 0, 7, 0));
124 
125  // set up the interaction set:
126  avtUI->View->setInteraction(QCP::iRangeZoom, true);
127  avtUI->View->setInteraction(QCP::iRangeDrag, true);
128 
129  // set up the selection tolerance for checking if a certain graph is or not selected:
130  avtUI->View->setSelectionTolerance(5);
131 
132  // draw the gradient:
133  drawGradient();
134 
135  // set up the background image:
136  background = new QCPItemPixmap(avtUI->View);
137  background->setPixmap(*gradient);
138  background->topLeft->setType(QCPItemPosition::ptPlotCoords);
139  background->bottomRight->setType(QCPItemPosition::ptPlotCoords);
140  background->setScaled(true, Qt::IgnoreAspectRatio);
141  background->setLayer("background");
142  background->setVisible(true);
143 
144  avtUI->raBox->setUnits(dmsBox::HOURS);
145  avtUI->decBox->setUnits(dmsBox::DEGREES);
146 
147  //FIXME:
148  //Doesn't make sense to manually adjust long/lat unless we can modify TZ also
149  avtUI->longBox->setReadOnly(true);
150  avtUI->latBox->setReadOnly(true);
151 
152  topLayout->addWidget(avtUI);
153 
155  topLayout->addWidget(buttonBox);
156  connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
157 
158  QPushButton *printB = new QPushButton(QIcon::fromTheme("document-print"), i18n("&Print..."));
159  printB->setToolTip(i18n("Print the Altitude vs. time plot"));
160  buttonBox->addButton(printB, QDialogButtonBox::ActionRole);
161  connect(printB, SIGNAL(clicked()), this, SLOT(slotPrint()));
162 
163  geo = KStarsData::Instance()->geo();
164 
165  DayOffset = 0;
166  // set up the initial minimum and maximum altitude
167  minAlt = 0;
168  maxAlt = 0;
169  showCurrentDate();
170  if (getDate().time().hour() > 12)
171  DayOffset = 1;
172 
173  avtUI->longBox->show(geo->lng());
174  avtUI->latBox->show(geo->lat());
175 
177  setLSTLimits();
178  setDawnDusk();
179 
180  connect(avtUI->View->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(onYRangeChanged(QCPRange)));
181  connect(avtUI->View->xAxis2, SIGNAL(rangeChanged(QCPRange)), this, SLOT(onXRangeChanged(QCPRange)));
182  connect(avtUI->View, SIGNAL(plottableClick(QCPAbstractPlottable*, int, QMouseEvent*)), this,
184  connect(avtUI->View, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseOverLine(QMouseEvent*)));
185 
186  connect(avtUI->browseButton, SIGNAL(clicked()), this, SLOT(slotBrowseObject()));
187  connect(avtUI->cityButton, SIGNAL(clicked()), this, SLOT(slotChooseCity()));
188  connect(avtUI->updateButton, SIGNAL(clicked()), this, SLOT(slotUpdateDateLoc()));
189  connect(avtUI->clearButton, SIGNAL(clicked()), this, SLOT(slotClear()));
190  connect(avtUI->addButton, SIGNAL(clicked()), this, SLOT(slotAddSource()));
191  connect(avtUI->nameBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource()));
192  connect(avtUI->raBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource()));
193  connect(avtUI->decBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource()));
194  connect(avtUI->clearFieldsButton, SIGNAL(clicked()), this, SLOT(slotClearBoxes()));
195  connect(avtUI->longBox, SIGNAL(returnPressed()), this, SLOT(slotAdvanceFocus()));
196  connect(avtUI->latBox, SIGNAL(returnPressed()), this, SLOT(slotAdvanceFocus()));
197  connect(avtUI->PlotList, SIGNAL(currentRowChanged(int)), this, SLOT(slotHighlight(int)));
198  connect(avtUI->computeButton, SIGNAL(clicked()), this, SLOT(slotComputeAltitudeByTime()));
199  connect(avtUI->riseButton, SIGNAL(clicked()), this, SLOT(slotMarkRiseTime()));
200  connect(avtUI->setButton, SIGNAL(clicked()), this, SLOT(slotMarkSetTime()));
201  connect(avtUI->transitButton, SIGNAL(clicked()), this, SLOT(slotMarkTransitTime()));
202 
203  // Set up the Rise/Set/Transit buttons' icons:
204 
205  QPixmap redButton(100, 100);
206  redButton.fill(Qt::transparent);
207  QPainter p;
208  p.begin(&redButton);
210  QPen pen(Qt::red, 2);
211  p.setPen(pen);
212  QBrush brush(Qt::red);
213  p.setBrush(brush);
214  p.drawEllipse(15, 15, 80, 80);
215  p.end();
216 
217  QPixmap blueButton(100, 100);
218  blueButton.fill(Qt::transparent);
219  QPainter p1;
220  p1.begin(&blueButton);
222  QPen pen1(Qt::blue, 2);
223  p1.setPen(pen1);
224  QBrush brush1(Qt::blue);
225  p1.setBrush(brush1);
226  p1.drawEllipse(15, 15, 80, 80);
227  p1.end();
228 
229  QPixmap greenButton(100, 100);
230  greenButton.fill(Qt::transparent);
231  QPainter p2;
232  p2.begin(&greenButton);
234  QPen pen2(Qt::green, 2);
235  p2.setPen(pen2);
236  QBrush brush2(Qt::green);
237  p2.setBrush(brush2);
238  p2.drawEllipse(15, 15, 80, 80);
239  p2.end();
240 
241  avtUI->riseButton->setIcon(QIcon(redButton));
242  avtUI->setButton->setIcon(QIcon(blueButton));
243  avtUI->transitButton->setIcon(QIcon(greenButton));
244 
245  setMouseTracking(true);
246 }
247 
249 {
250  //WARNING: need to delete deleteList items!
251 }
253 {
254 
255  SkyObject *obj = KStarsData::Instance()->objectNamed(avtUI->nameBox->text());
256  if (!obj)
257  {
258  QString name = FindDialog::processSearchText(avtUI->nameBox->text());
259  obj = KStarsData::Instance()->objectNamed(name);
260  }
261  if (obj)
262  {
263  //An object with the current name exists. If the object is not already
264  //in the avt list, add it.
265  bool found = false;
266  foreach (SkyObject *o, pList)
267  {
268  //if ( o->name() == obj->name() ) {
269  if (getObjectName(o, false) == getObjectName(obj, false))
270  {
271  found = true;
272  break;
273  }
274  }
275  if (!found)
276  processObject(obj);
277  }
278  else
279  {
280  //Object with the current name doesn't exist. It's possible that the
281  //user is trying to add a custom object. Assume this is the case if
282  //the RA and Dec fields are filled in.
283 
284  if (!avtUI->nameBox->text().isEmpty() && !avtUI->raBox->text().isEmpty() && !avtUI->decBox->text().isEmpty())
285  {
286  bool okRA, okDec;
287  dms newRA = avtUI->raBox->createDms(&okRA);
288  dms newDec = avtUI->decBox->createDms(&okDec);
289  if (!okRA || !okDec)
290  return;
291 
292  //If the epochName is blank (or any non-double), we assume J2000
293  //Otherwise, precess to J2000.
294  KStarsDateTime dt;
295  dt.setFromEpoch(getEpoch(avtUI->epochName->text()));
296  long double jd = dt.djd();
297  if (jd != J2000)
298  {
299  SkyPoint ptest(newRA, newDec);
300  //ptest.precessFromAnyEpoch(jd, J2000);
301  ptest.catalogueCoord(jd);
302  newRA.setH(ptest.ra().Hours());
303  newDec.setD(ptest.dec().Degrees());
304  }
305 
306  //make sure the coords do not already exist from another object
307  bool found = false;
308  foreach (SkyObject *p, pList)
309  {
310  //within an arcsecond?
311  if (fabs(newRA.Degrees() - p->ra().Degrees()) < 0.0003 &&
312  fabs(newDec.Degrees() - p->dec().Degrees()) < 0.0003)
313  {
314  found = true;
315  break;
316  }
317  }
318  if (!found)
319  {
320  SkyObject *obj = new SkyObject(8, newRA, newDec, 1.0, avtUI->nameBox->text());
321  deleteList.append(obj); //this object will be deleted when window is destroyed
322  processObject(obj);
323  }
324  }
325 
326  //If the Ra and Dec boxes are filled, but the name field is empty,
327  //move input focus to nameBox. If either coordinate box is empty,
328  //move focus there
329  if (avtUI->nameBox->text().isEmpty())
330  {
331  avtUI->nameBox->QWidget::setFocus();
332  }
333  if (avtUI->raBox->text().isEmpty())
334  {
335  avtUI->raBox->QWidget::setFocus();
336  }
337  else
338  {
339  if (avtUI->decBox->text().isEmpty())
340  avtUI->decBox->QWidget::setFocus();
341  }
342  }
343 
344  avtUI->View->update();
345 }
346 
347 //Use find dialog to choose an object
349 {
350  if (FindDialog::Instance()->exec() == QDialog::Accepted)
351  {
352  SkyObject *o = FindDialog::Instance()->targetObject();
353  processObject(o);
354  }
355 
356  avtUI->View->update();
357  avtUI->View->replot();
358 }
359 
360 void AltVsTime::processObject(SkyObject *o, bool forceAdd)
361 {
362  if (!o)
363  return;
364 
365  KSNumbers *num = new KSNumbers(getDate().djd());
366  KSNumbers *oldNum = nullptr;
367 
368  //If the object is in the solar system, recompute its position for the given epochLabel
369  KStarsData *data = KStarsData::Instance();
370  if (o->isSolarSystem())
371  {
372  oldNum = new KSNumbers(data->ut().djd());
373  o->updateCoords(num, true, geo->lat(), data->lst(), true);
374  }
375 
376  //precess coords to target epoch
377  o->updateCoordsNow(num);
378 
379  // vector used for computing the points needed for drawing the graph
380  QVector<double> y(100), t(100);
381 
382  //If this point is not in list already, add it to list
383  bool found(false);
384  foreach (SkyObject *p, pList)
385  {
386  if (o->ra().Degrees() == p->ra().Degrees() && o->dec().Degrees() == p->dec().Degrees())
387  {
388  found = true;
389  break;
390  }
391  }
392  if (found && !forceAdd)
393  {
394  qCWarning(KSTARS) << "This point is already displayed; It will not be duplicated.";
395  }
396  else
397  {
398  pList.append(o);
399 
400  // make sure existing curves are thin and red:
401 
402  for (int i = 0; i < avtUI->View->graphCount(); i++)
403  {
404  if (avtUI->View->graph(i)->pen().color() == Qt::white)
405  {
406  avtUI->View->graph(i)->setPen(QPen(Qt::red, 2));
407  }
408  }
409 
410  // SET up the curve's name
411  avtUI->View->addGraph()->setName(o->name());
412 
413  // compute the current graph:
414  // time range: 24h
415 
416  int offset = 3;
417  for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
418  {
419  y[i] = findAltitude(o, h);
420  if (y[i] > maxAlt)
421  maxAlt = y[i];
422  if (y[i] < minAlt)
423  minAlt = y[i];
424  t[i] = i * 900 + 43200;
425  avtUI->View->graph(avtUI->View->graphCount() - 1)->addData(t[i], y[i]);
426  }
427  avtUI->View->graph(avtUI->View->graphCount() - 1)->setPen(QPen(Qt::white, 3));
428 
429  // Go into initial state: without Zoom/Pan
430  avtUI->View->xAxis->setRange(43200, 129600);
431  avtUI->View->xAxis2->setRange(61200, 147600);
432  if (abs(minAlt) > maxAlt)
433  maxAlt = abs(minAlt);
434  else
435  minAlt = -maxAlt;
436 
437  avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);
438 
439  // Update background coordinates:
440  background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
441  background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);
442 
443  avtUI->View->replot();
444 
445  avtUI->PlotList->addItem(getObjectName(o));
446  avtUI->PlotList->setCurrentRow(avtUI->PlotList->count() - 1);
447  avtUI->raBox->show(o->ra());
448  avtUI->decBox->show(o->dec());
449  avtUI->nameBox->setText(getObjectName(o));
450 
451  //Set epochName to epoch shown in date tab
452  avtUI->epochName->setText(QString().setNum(getDate().epoch()));
453  }
454  //qCDebug() << "Currently, there are " << avtUI->View->graphCount() << " objects displayed.";
455 
456  //restore original position
457  if (o->isSolarSystem())
458  {
459  o->updateCoords(oldNum, true, data->geo()->lat(), data->lst(), true);
460  delete oldNum;
461  }
462  o->EquatorialToHorizontal(data->lst(), data->geo()->lat());
463  delete num;
464 }
465 
466 double AltVsTime::findAltitude(SkyPoint *p, double hour)
467 {
468  hour += 24.0 * DayOffset;
469 
470  //getDate converts the user-entered local time to UT
471  KStarsDateTime ut = getDate().addSecs(hour * 3600.0);
472 
473  CachingDms LST = geo->GSTtoLST(ut.gst());
474  p->EquatorialToHorizontal(&LST, geo->lat());
475  return p->alt().Degrees();
476 }
477 
479 {
480  if (row < 0)
481  return;
482 
483  int rowIndex = 0;
484  //highlight the curve of the selected object
485  for (int i = 0; i < avtUI->View->graphCount(); i++)
486  {
487  if (i == row)
488  rowIndex = row;
489  else
490  {
491  avtUI->View->graph(i)->setPen(QPen(Qt::red, 2));
492  avtUI->View->graph(i)->setLayer("main");
493  }
494  }
495  avtUI->View->graph(rowIndex)->setPen(QPen(Qt::white, 3));
496  avtUI->View->graph(rowIndex)->setLayer("currentCurveLayer");
497  avtUI->View->update();
498  avtUI->View->replot();
499 
500  if (row >= 0 && row < pList.size())
501  {
502  SkyObject *p = pList.at(row);
503  avtUI->raBox->show(p->ra());
504  avtUI->decBox->show(p->dec());
505  avtUI->nameBox->setText(avtUI->PlotList->currentItem()->text());
506  }
507 
508  SkyObject *selectedObject = KStarsData::Instance()->objectNamed(avtUI->nameBox->text());
509  const KStarsDateTime &ut = KStarsData::Instance()->ut();
510  if (selectedObject)
511  {
512  QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
513  if (rt.isValid() == false)
514  {
515  avtUI->riseButton->setEnabled(false);
516  avtUI->setButton->setEnabled(false);
517  }
518  else
519  {
520  avtUI->riseButton->setEnabled(true);
521  avtUI->setButton->setEnabled(true);
522  }
523  }
524 }
525 
527 {
528  QCPRange aux = avtUI->View->xAxis2->range();
529  avtUI->View->xAxis->setRange(aux -= 18000);
530  avtUI->View->xAxis2->setRange(range.bounded(61200, 147600));
531  // if ZOOM is detected then remove the gold lines that indicate current position:
532  if (avtUI->View->xAxis->range().size() != 86400)
533  {
534  // Refresh the background:
535  background->setScaled(false);
536  background->setScaled(true, Qt::IgnoreAspectRatio);
537  background->setPixmap(*gradient);
538 
539  avtUI->View->update();
540  avtUI->View->replot();
541  }
542 }
543 
545 {
546  int offset = 3;
547  avtUI->View->yAxis->setRange(range.bounded(minAlt - offset, maxAlt + offset));
548 }
549 
550 void AltVsTime::plotMousePress(QCPAbstractPlottable *abstractPlottable, int dataIndex, QMouseEvent *event)
551 {
552  //Do we need this?
553  Q_UNUSED(dataIndex);
554 
555  if (event->button() == Qt::RightButton)
556  {
557  QCPAbstractPlottable *plottable = abstractPlottable;
558  if (plottable)
559  {
560  double x = avtUI->View->xAxis->pixelToCoord(event->localPos().x());
561  double y = avtUI->View->yAxis->pixelToCoord(event->localPos().y());
562 
563  QCPGraph *graph = qobject_cast<QCPGraph *>(plottable);
564 
565  if (graph)
566  {
567  double yValue = y;
568  double xValue = x;
569 
570  // Compute time value:
571  QTime localTime(0, 0, 0, 0);
572  QTime localSiderealTime(5, 0, 0, 0);
573 
574  localTime = localTime.addSecs(int(xValue));
575  localSiderealTime = localSiderealTime.addSecs(int(xValue));
576 
578  QToolTip::showText(event->globalPos(),
579  i18n("<table>"
580  "<tr>"
581  "<th colspan=\"2\">%1</th>"
582  "</tr>"
583  "<tr>"
584  "<td>LST: </td>"
585  "<td>%3</td>"
586  "</tr>"
587  "<tr>"
588  "<td>LT: </td>"
589  "<td>%2</td>"
590  "</tr>"
591  "<tr>"
592  "<td>Altitude: </td>"
593  "<td>%4</td>"
594  "</tr>"
595  "</table>",
596  graph->name().isEmpty() ? "???" : graph->name(),
597  localTime.toString(),
598  localSiderealTime.toString(),
599  QString::number(yValue, 'f', 2) + ' ' + QChar(176)),
600  avtUI->View, avtUI->View->rect());
601  }
602  }
603  }
604 }
605 
606 //move input focus to the next logical widget
608 {
609  if (sender()->objectName() == QString("nameBox"))
610  avtUI->addButton->setFocus();
611  if (sender()->objectName() == QString("raBox"))
612  avtUI->decBox->setFocus();
613  if (sender()->objectName() == QString("decbox"))
614  avtUI->addButton->setFocus();
615  if (sender()->objectName() == QString("longBox"))
616  avtUI->latBox->setFocus();
617  if (sender()->objectName() == QString("latBox"))
618  avtUI->updateButton->setFocus();
619 }
620 
622 {
623  pList.clear();
624  //Need to delete the pointers in deleteList
625  while (!deleteList.isEmpty())
626  delete deleteList.takeFirst();
627 
628  avtUI->PlotList->clear();
629  avtUI->nameBox->clear();
630  avtUI->raBox->clear();
631  avtUI->decBox->clear();
632  avtUI->epochName->clear();
633  // remove all graphs from the plot:
634  avtUI->View->clearGraphs();
635  // we remove all the dots (rise/set/transit) from the chart
636  // without removing the background image
637  int indexItem = 0, noItems = avtUI->View->itemCount();
638  QCPAbstractItem *item;
639  QCPItemPixmap *background;
640  // remove every item at a time:
641  while (noItems > 1 && indexItem < noItems)
642  {
643  // test if the current item is the background:
644  item = avtUI->View->item(indexItem);
645  background = qobject_cast<QCPItemPixmap *>(item);
646  if (background)
647  indexItem++;
648  else
649  {
650  // if not, then remove this item:
651  avtUI->View->removeItem(indexItem);
652  noItems--;
653  }
654  }
655  // update & replot the chart:
656  avtUI->View->update();
657  avtUI->View->replot();
658 }
659 
661 {
662  avtUI->nameBox->clear();
663  avtUI->raBox->clear();
664  avtUI->decBox->clear();
665  avtUI->epochName->clear();
666 }
667 
669 {
670  SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
671  if (selectedObject == nullptr)
672  {
673  if (avtUI->PlotList->currentItem())
674  qCWarning(KSTARS) << "slotComputeAltitudeByTime: Unable to find" << avtUI->PlotList->currentItem()->text();
675  else
676  {
677  qCWarning(KSTARS) << "slotComputeAltitudeByTime: Unable to find item";
678  }
679  return;
680  }
681 
682  // Get Local Date & Time
683  KStarsDateTime lt = KStarsDateTime(avtUI->DateWidget->date(), avtUI->timeSpin->time(), Qt::LocalTime);
684  // Convert to UT
685  KStarsDateTime ut = geo->LTtoUT(lt);
686  // Get LST from GST
687  CachingDms LST = geo->GSTtoLST(ut.gst());
688  SkyObject *tempObject = selectedObject->clone();
689  // Update coords
690  KSNumbers num(ut.djd());
691  tempObject->updateCoords(&num, true, geo->lat(), &LST);
692  // Find Horizontal coordinates from LST & Latitude
693  selectedObject->EquatorialToHorizontal(&LST, geo->lat());
694 
695  // Set altitude
696  avtUI->altitudeBox->setText(selectedObject->altRefracted().toDMSString(true));
697 
698  delete (tempObject);
699 }
700 
702 {
703  const KStarsDateTime &ut = KStarsData::Instance()->ut();
704  SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
705  if (selectedObject == nullptr)
706  {
707  qCWarning(KSTARS) << "Mark Rise Time: Unable to find" << avtUI->PlotList->currentItem()->text();
708  return;
709  }
710 
711  QCPItemTracer *riseTimeTracer;
712  // check if at least one graph exists in the plot
713  if (avtUI->View->graphCount() > 0)
714  {
715  double time = 0;
716  double hours, minutes;
717 
718  QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow());
719 
720  QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
721  // mark the Rise time with a solid red circle
722  if (rt.isValid() && selectedGraph)
723  {
724  hours = rt.hour();
725  minutes = rt.minute();
726  if (hours < 12)
727  hours += 24;
728  time = hours * 3600 + minutes * 60;
729  riseTimeTracer = new QCPItemTracer(avtUI->View);
730  riseTimeTracer->setLayer("markersLayer");
731  riseTimeTracer->setGraph(selectedGraph);
732  riseTimeTracer->setInterpolating(true);
733  riseTimeTracer->setStyle(QCPItemTracer::tsCircle);
734  riseTimeTracer->setPen(QPen(Qt::red));
735  riseTimeTracer->setBrush(Qt::red);
736  riseTimeTracer->setSize(10);
737  riseTimeTracer->setGraphKey(time);
738  riseTimeTracer->setVisible(true);
739  avtUI->View->update();
740  avtUI->View->replot();
741  }
742  }
743 }
744 
746 {
747  const KStarsDateTime &ut = KStarsData::Instance()->ut();
748  SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
749  if (selectedObject == nullptr)
750  {
751  qCWarning(KSTARS) << "Mark Set Time: Unable to find" << avtUI->PlotList->currentItem()->text();
752  return;
753  }
754  QCPItemTracer *setTimeTracer;
755  // check if at least one graph exists in the plot
756  if (avtUI->View->graphCount() > 0)
757  {
758  double time = 0;
759  double hours, minutes;
760 
761  QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow());
762 
763  QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
764  //If set time is before rise time, use set time for tomorrow
765  QTime st = selectedObject->riseSetTime(ut, geo, false); //false = use set time
766  if (st < rt)
767  st = selectedObject->riseSetTime(ut.addDays(1), geo, false); //false = use set time
768  // mark the Set time with a solid blue circle
769  if (rt.isValid())
770  {
771  hours = st.hour();
772  minutes = st.minute();
773  if (hours < 12)
774  hours += 24;
775  time = hours * 3600 + minutes * 60;
776  setTimeTracer = new QCPItemTracer(avtUI->View);
777  setTimeTracer->setLayer("markersLayer");
778  setTimeTracer->setGraph(selectedGraph);
779  setTimeTracer->setInterpolating(true);
780  setTimeTracer->setStyle(QCPItemTracer::tsCircle);
781  setTimeTracer->setPen(QPen(Qt::blue));
782  setTimeTracer->setBrush(Qt::blue);
783  setTimeTracer->setSize(10);
784  setTimeTracer->setGraphKey(time);
785  setTimeTracer->setVisible(true);
786  avtUI->View->update();
787  avtUI->View->replot();
788  }
789  }
790 }
791 
793 {
794  const KStarsDateTime &ut = KStarsData::Instance()->ut();
795  SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
796  if (selectedObject == nullptr)
797  {
798  qCWarning(KSTARS) << "Mark Transit Time: Unable to find" << avtUI->PlotList->currentItem()->text();
799  return;
800  }
801  QCPItemTracer *transitTimeTracer;
802  // check if at least one graph exists in the plot
803  if (avtUI->View->graphCount() > 0)
804  {
805  double time = 0;
806  double hours, minutes;
807 
808  QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow());
809 
810  QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
811  //If transit time is before rise time, use transit time for tomorrow
812  QTime tt = selectedObject->transitTime(ut, geo);
813 
814  if (tt < rt)
815  tt = selectedObject->transitTime(ut.addDays(1), geo);
816  // mark the Transit time with a solid green circle
817  hours = tt.hour();
818  minutes = tt.minute();
819  if (hours < 12)
820  hours += 24;
821  time = hours * 3600 + minutes * 60;
822  transitTimeTracer = new QCPItemTracer(avtUI->View);
823  transitTimeTracer->setLayer("markersLayer");
824  transitTimeTracer->setGraph(selectedGraph);
825  transitTimeTracer->setInterpolating(true);
826  transitTimeTracer->setStyle(QCPItemTracer::tsCircle);
827  transitTimeTracer->setPen(QPen(Qt::green));
828  transitTimeTracer->setBrush(Qt::green);
829  transitTimeTracer->setSize(10);
830  transitTimeTracer->setGraphKey(time);
831  transitTimeTracer->setVisible(true);
832  avtUI->View->update();
833  avtUI->View->replot();
834  }
835 }
836 
838 {
839  //Determine the time of sunset and sunrise for the desired date and location
840  //expressed as doubles, the fraction of a full day.
841 
842  /* KSAlmanac ksal(getDate(), geo); */
843 
844  /* ... */
845 }
846 
847 //FIXME
848 /*
849 void AltVsTime::mouseOverLine(QMouseEvent *event){
850  // Get the mouse position's coordinates relative to axes:
851  double x = avtUI->View->xAxis->pixelToCoord(event->pos().x());
852  double y = avtUI->View->yAxis->pixelToCoord(event->pos().y());
853  // Save the actual values:
854  double yValue = y;
855  double xValue = x;
856  // The offset used for the Y axis: top/bottom
857  int offset = 3;
858  // Compute the Y axis maximum value:
859  int yAxisMaxValue = maxAlt + offset;
860  // Compute the X axis minimum and maximum values:
861  int xAxisMinValue = 43200;
862  int xAxisMaxValue = 129600;
863  // Ignore the upper and left margins:
864  y = yAxisMaxValue - y;
865  x -= xAxisMinValue;
866  // We make a copy to gradient background in order to have one set of lines at a time:
867  // Otherwise, the chart would have been covered by lines
868  QPixmap copy = gradient->copy(gradient->rect());
869  // If ZOOM is not active, then draw the gold lines that indicate current mouse pisition:
870  if(avtUI->View->xAxis->range().size() == 86400){
871  QPainter p;
872 
873  p.begin(&copy);
874  p.setPen( QPen( QBrush("gold"), 2, Qt::SolidLine ) );
875 
876  // Get the gradient background's width and height:
877  int pW = gradient->rect().width();
878  int pH = gradient->rect().height();
879 
880  // Compute the real coordinates within the chart:
881  y = (y*pH/2)/yAxisMaxValue;
882  x = (x*pW)/(xAxisMaxValue-xAxisMinValue);
883 
884  // Draw the horizontal line (altitude):
885  p.drawLine( QLineF( 0.5, y, avtUI->View->rect().width()-0.5,y ) );
886  // Draw the altitude value:
887  p.setPen( QPen( QBrush("gold"), 3, Qt::SolidLine ) );
888  p.drawText( 25, y + 15, QString::number(yValue,'f',2) + QChar(176) );
889  p.setPen( QPen( QBrush("gold"), 1, Qt::SolidLine ) );
890  // Draw the vertical line (time):
891  p.drawLine( QLineF( x, 0.5, x, avtUI->View->rect().height()-0.5 ) );
892  // Compute and draw the time value:
893  QTime localTime(0,0,0,0);
894  localTime = localTime.addSecs(int(xValue));
895  p.save();
896  p.translate( x + 10, pH - 20 );
897  p.rotate(-90);
898  p.setPen( QPen( QBrush("gold"), 3, Qt::SolidLine ) );
899  p.drawText( 5, 5, QLocale().toString( localTime, QLocale::ShortFormat ) ); // short format necessary to avoid false time-zone labeling
900  p.restore();
901  p.end();
902  }
903  // Refresh the background:
904  background->setScaled(false);
905  background->setScaled(true, Qt::IgnoreAspectRatio);
906  background->setPixmap(copy);
907 
908  avtUI->View->update();
909  avtUI->View->replot();
910 }
911 */
912 
914 {
915  double x = avtUI->View->xAxis->pixelToCoord(event->localPos().x());
916  double y = avtUI->View->yAxis->pixelToCoord(event->localPos().y());
917  QCPAbstractPlottable *abstractGraph = avtUI->View->plottableAt(event->pos(), false);
918  QCPGraph *graph = qobject_cast<QCPGraph *>(abstractGraph);
919 
920  if (x > avtUI->View->xAxis->range().lower && x < avtUI->View->xAxis->range().upper)
921  if (y > avtUI->View->yAxis->range().lower && y < avtUI->View->yAxis->range().upper)
922  {
923  if (graph)
924  {
925  double yValue = y;
926  double xValue = x;
927 
928  // Compute time value:
929  QTime localTime(0, 0, 0, 0);
930  QTime localSiderealTime(5, 0, 0, 0);
931 
932  localTime = localTime.addSecs(int(xValue));
933  localSiderealTime = localSiderealTime.addSecs(int(xValue));
934 
936  QToolTip::showText(event->globalPos(),
937  i18n("<table>"
938  "<tr>"
939  "<th colspan=\"2\">%1</th>"
940  "</tr>"
941  "<tr>"
942  "<td>LST: </td>"
943  "<td>%3</td>"
944  "</tr>"
945  "<tr>"
946  "<td>LT: </td>"
947  "<td>%2</td>"
948  "</tr>"
949  "<tr>"
950  "<td>Altitude: </td>"
951  "<td>%4</td>"
952  "</tr>"
953  "</table>",
954  graph->name().isEmpty() ? "???" : graph->name(),
955  localTime.toString(), localSiderealTime.toString(),
956  QString::number(yValue, 'f', 2) + ' ' + QChar(176)),
957  avtUI->View, avtUI->View->rect());
958  }
959  else
961  }
962 
963  avtUI->View->update();
964  avtUI->View->replot();
965 }
966 
968 {
969  KStarsData *data = KStarsData::Instance();
970  KStarsDateTime today = getDate();
971  KSNumbers *num = new KSNumbers(today.djd());
972  KSNumbers *oldNum = nullptr;
973  CachingDms LST = geo->GSTtoLST(today.gst());
974 
975  //First determine time of sunset and sunrise
976  computeSunRiseSetTimes();
977  // Determine dawn/dusk time and min/max sun elevation
978  setDawnDusk();
979 
980  for (int i = 0; i < pList.count(); ++i)
981  {
982  SkyObject *o = pList.at(i);
983  if (o)
984  {
985  //If the object is in the solar system, recompute its position for the given date
986  if (o->isSolarSystem())
987  {
988  oldNum = new KSNumbers(data->ut().djd());
989  o->updateCoords(num, true, geo->lat(), &LST, true);
990  }
991 
992  //precess coords to target epoch
993  o->updateCoordsNow(num);
994 
995  //update pList entry
996  pList.replace(i, o);
997 
998  // We are creating a new data set (time, altitude) for the new date:
999  QVector<double> time_dataSet, altitude_dataSet;
1000  double point_altitudeValue, point_timeValue;
1001  // compute the new graph values:
1002  // time range: 24h
1003  int offset = 3;
1004  for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
1005  {
1006  point_altitudeValue = findAltitude(o, h);
1007  altitude_dataSet.push_back(point_altitudeValue);
1008  if (point_altitudeValue > maxAlt)
1009  maxAlt = point_altitudeValue;
1010  if (point_altitudeValue < minAlt)
1011  minAlt = point_altitudeValue;
1012  point_timeValue = i * 900 + 43200;
1013  time_dataSet.push_back(point_timeValue);
1014  }
1015 
1016  // Replace graph data set:
1017  avtUI->View->graph(i)->setData(time_dataSet, altitude_dataSet);
1018 
1019  // Go into initial state: without Zoom/Pan
1020  avtUI->View->xAxis->setRange(43200, 129600);
1021  avtUI->View->xAxis2->setRange(61200, 147600);
1022 
1023  // Center the altitude axis in 0 value:
1024  if (abs(minAlt) > maxAlt)
1025  maxAlt = abs(minAlt);
1026  else
1027  minAlt = -maxAlt;
1028  avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);
1029 
1030  // Update background coordinates:
1031  background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
1032  background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);
1033 
1034  // Redraw the plot:
1035  avtUI->View->replot();
1036 
1037  //restore original position
1038  if (o->isSolarSystem())
1039  {
1040  o->updateCoords(oldNum, true, data->geo()->lat(), data->lst());
1041  delete oldNum;
1042  oldNum = nullptr;
1043  }
1044  o->EquatorialToHorizontal(data->lst(), data->geo()->lat());
1045  }
1046  else //assume unfound object is a custom object
1047  {
1048  pList.at(i)->updateCoordsNow(num); //precess to desired epoch
1049 
1050  // We are creating a new data set (time, altitude) for the new date:
1051  QVector<double> time_dataSet, altitude_dataSet;
1052  double point_altitudeValue, point_timeValue;
1053  // compute the new graph values:
1054  // time range: 24h
1055  int offset = 3;
1056  for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
1057  {
1058  point_altitudeValue = findAltitude(pList.at(i), h);
1059  altitude_dataSet.push_back(point_altitudeValue);
1060  if (point_altitudeValue > maxAlt)
1061  maxAlt = point_altitudeValue;
1062  if (point_altitudeValue < minAlt)
1063  minAlt = point_altitudeValue;
1064  point_timeValue = i * 900 + 43200;
1065  time_dataSet.push_back(point_timeValue);
1066  }
1067 
1068  // Replace graph data set:
1069  avtUI->View->graph(i)->setData(time_dataSet, altitude_dataSet);
1070 
1071  // Go into initial state: without Zoom/Pan
1072  avtUI->View->xAxis->setRange(43200, 129600);
1073  avtUI->View->xAxis2->setRange(61200, 147600);
1074 
1075  // Center the altitude axis in 0 value:
1076  if (abs(minAlt) > maxAlt)
1077  maxAlt = abs(minAlt);
1078  else
1079  minAlt = -maxAlt;
1080  avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);
1081 
1082  // Update background coordinates:
1083  background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
1084  background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);
1085 
1086  // Redraw the plot:
1087  avtUI->View->replot();
1088  }
1089  }
1090 
1091  if (getDate().time().hour() > 12)
1092  DayOffset = 1;
1093  else
1094  DayOffset = 0;
1095 
1096  setLSTLimits();
1097  slotHighlight(avtUI->PlotList->currentRow());
1098  avtUI->View->update();
1099 
1100  delete num;
1101 }
1102 
1104 {
1105  QPointer<LocationDialog> ld = new LocationDialog(this);
1106  if (ld->exec() == QDialog::Accepted)
1107  {
1108  GeoLocation *newGeo = ld->selectedCity();
1109  if (newGeo)
1110  {
1111  geo = newGeo;
1112  avtUI->latBox->show(geo->lat());
1113  avtUI->longBox->show(geo->lng());
1114  }
1115  }
1116  delete ld;
1117 }
1118 
1119 // FIXME: should we remove this method?
1121 {
1122  /*
1123  //UT at noon on target date
1124  KStarsDateTime ut = getDate().addSecs(((double)DayOffset + 0.5)*86400.);
1125 
1126  dms lst = geo->GSTtoLST(ut.gst());
1127  double h1 = lst.Hours();
1128  if(h1 > 12.0)
1129  h1 -= 24.0;
1130  double h2 = h1 + 24.0;
1131  avtUI->View->setSecondaryLimits(h1, h2, -90.0, 90.0);
1132  */
1133 }
1134 
1136 {
1138  if (dt.time() > QTime(12, 0, 0))
1139  dt = dt.addDays(1);
1140  avtUI->DateWidget->setDate(dt.date());
1141 }
1142 
1143 void AltVsTime::drawGradient()
1144 {
1145  // Things needed for Gradient:
1147  GeoLocation *geoLoc = KStarsData::Instance()->geo();
1148  QDateTime midnight = QDateTime(dtt.date(), QTime());
1149  KStarsDateTime const utt = geoLoc->LTtoUT(KStarsDateTime(midnight));
1150 
1151  // Variables needed for Gradient:
1152  double SunRise, SunSet, Dawn, Dusk, SunMinAlt, SunMaxAlt;
1153  double MoonRise, MoonSet, MoonIllum;
1154 
1155  KSAlmanac ksal(utt, geoLoc);
1156 
1157  // Get the values:
1158  SunRise = ksal.getSunRise();
1159  SunSet = ksal.getSunSet();
1160  SunMaxAlt = ksal.getSunMaxAlt();
1161  SunMinAlt = ksal.getSunMinAlt();
1162  MoonRise = ksal.getMoonRise();
1163  MoonSet = ksal.getMoonSet();
1164  MoonIllum = ksal.getMoonIllum();
1165  Dawn = ksal.getDawnAstronomicalTwilight();
1166  Dusk = ksal.getDuskAstronomicalTwilight();
1167 
1168  gradient = new QPixmap(avtUI->View->rect().width(), avtUI->View->rect().height());
1169 
1170  QPainter p;
1171 
1172  p.begin(gradient);
1173  KPlotWidget *kPW = new KPlotWidget;
1175  p.fillRect(gradient->rect(), kPW->backgroundColor());
1176 
1177  p.setClipRect(gradient->rect());
1178  p.setClipping(true);
1179 
1180  int pW = gradient->rect().width();
1181  int pH = gradient->rect().height();
1182 
1183  QColor SkyColor(0, 100, 200);
1184  // TODO
1185  // if( Options::darkAppColors() )
1186  // SkyColor = QColor( 200, 0, 0 ); // use something red, visible through a red filter
1187 
1188  // Draw gradient representing lunar interference in the sky
1189  if (MoonIllum > 0.01) // do this only if Moon illumination is reasonable so it's important
1190  {
1191  int moonrise = int(pW * (0.5 + MoonRise));
1192  int moonset = int(pW * (MoonSet - 0.5));
1193  if (moonset < 0)
1194  moonset += pW;
1195  if (moonrise > pW)
1196  moonrise -= pW;
1197  int moonalpha = int(10 + MoonIllum * 130);
1198  int fadewidth =
1199  pW *
1200  0.01; // pW * fraction of day to fade the moon brightness over (0.01 corresponds to roughly 15 minutes, 0.007 to 10 minutes), both before and after actual set.
1201  QColor MoonColor(255, 255, 255, moonalpha);
1202 
1203  if (moonset < moonrise)
1204  {
1205  QLinearGradient grad =
1206  QLinearGradient(QPointF(moonset - fadewidth, 0.0), QPointF(moonset + fadewidth, 0.0));
1207  grad.setColorAt(0, MoonColor);
1208  grad.setColorAt(1, Qt::transparent);
1209  p.fillRect(QRectF(0.0, 0.0, moonset + fadewidth, pH),
1210  grad); // gradient should be padded until moonset - fadewidth (see QLinearGradient docs)
1211  grad.setStart(QPointF(moonrise + fadewidth, 0.0));
1212  grad.setFinalStop(QPointF(moonrise - fadewidth, 0.0));
1213  p.fillRect(QRectF(moonrise - fadewidth, 0.0, pW - moonrise + fadewidth, pH), grad);
1214  }
1215  else
1216  {
1217  qreal opacity = p.opacity();
1218  p.setOpacity(opacity / 4);
1219  p.fillRect(QRectF(moonrise + fadewidth, 0.0, moonset - moonrise - 2 * fadewidth, pH), MoonColor);
1220  QLinearGradient grad =
1221  QLinearGradient(QPointF(moonrise + fadewidth, 0.0), QPointF(moonrise - fadewidth, 0.0));
1222  grad.setColorAt(0, MoonColor);
1223  grad.setColorAt(1, Qt::transparent);
1224  p.fillRect(QRectF(0.0, 0.0, moonrise + fadewidth, pH), grad);
1225  grad.setStart(QPointF(moonset - fadewidth, 0.0));
1226  grad.setFinalStop(QPointF(moonset + fadewidth, 0.0));
1227  p.fillRect(QRectF(moonset - fadewidth, 0.0, pW - moonset, pH), grad);
1228  p.setOpacity(opacity);
1229  }
1230  }
1231 
1232  //draw daytime sky if the Sun rises for the current date/location
1233  if (SunMaxAlt > -18.0)
1234  {
1235  //Display centered on midnight, so need to modulate dawn/dusk by 0.5
1236  int rise = int(pW * (0.5 + SunRise));
1237  int set = int(pW * (SunSet - 0.5));
1238  int da = int(pW * (0.5 + Dawn));
1239  int du = int(pW * (Dusk - 0.5));
1240 
1241  if (SunMinAlt > 0.0)
1242  {
1243  // The sun never set and the sky is always blue
1244  p.fillRect(rect(), SkyColor);
1245  }
1246  else if (SunMaxAlt < 0.0 && SunMinAlt < -18.0)
1247  {
1248  // The sun never rise but the sky is not completely dark
1249  QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(du, 0.0));
1250 
1251  QColor gradStartColor = SkyColor;
1252  gradStartColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255);
1253 
1254  grad.setColorAt(0, gradStartColor);
1255  grad.setColorAt(1, Qt::transparent);
1256  p.fillRect(QRectF(0.0, 0.0, du, pH), grad);
1257  grad.setStart(QPointF(pW, 0.0));
1258  grad.setFinalStop(QPointF(da, 0.0));
1259  p.fillRect(QRectF(da, 0.0, pW, pH), grad);
1260  }
1261  else if (SunMaxAlt < 0.0 && SunMinAlt > -18.0)
1262  {
1263  // The sun never rise but the sky is NEVER completely dark
1264  QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(pW, 0.0));
1265 
1266  QColor gradStartEndColor = SkyColor;
1267  gradStartEndColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255);
1268  QColor gradMidColor = SkyColor;
1269  gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255);
1270 
1271  grad.setColorAt(0, gradStartEndColor);
1272  grad.setColorAt(0.5, gradMidColor);
1273  grad.setColorAt(1, gradStartEndColor);
1274  p.fillRect(QRectF(0.0, 0.0, pW, pH), grad);
1275  }
1276  else if (Dawn < 0.0)
1277  {
1278  // The sun sets and rises but the sky is never completely dark
1279  p.fillRect(0, 0, set, int(0.5 * pH), SkyColor);
1280  p.fillRect(rise, 0, pW, int(0.5 * pH), SkyColor);
1281 
1282  QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(rise, 0.0));
1283 
1284  QColor gradMidColor = SkyColor;
1285  gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255);
1286 
1287  grad.setColorAt(0, SkyColor);
1288  grad.setColorAt(0.5, gradMidColor);
1289  grad.setColorAt(1, SkyColor);
1290  p.fillRect(QRectF(set, 0.0, rise - set, pH), grad);
1291  }
1292  else
1293  {
1294  p.fillRect(0, 0, set, pH, SkyColor);
1295  p.fillRect(rise, 0, pW, pH, SkyColor);
1296 
1297  QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(du, 0.0));
1298  grad.setColorAt(0, SkyColor);
1299  grad.setColorAt(
1300  1,
1301  Qt::transparent); // FIXME?: The sky appears black well before the actual end of twilight if the gradient is too slow (eg: latitudes above arctic circle)
1302  p.fillRect(QRectF(set, 0.0, du - set, pH), grad);
1303 
1304  grad.setStart(QPointF(rise, 0.0));
1305  grad.setFinalStop(QPointF(da, 0.0));
1306  p.fillRect(QRectF(da, 0.0, rise - da, pH), grad);
1307  }
1308  }
1309 
1310  p.fillRect(0, int(0.5 * pH), pW, int(0.5 * pH), KStarsData::Instance()->colorScheme()->colorNamed("HorzColor"));
1311 
1312  p.setClipping(false);
1313 
1314  // Add vertical line indicating "now"
1315  // Convert the current system clock time to the TZ corresponding to geo
1316  QTime t = geoLoc->UTtoLT(KStarsDateTime::currentDateTimeUtc()).time();
1317  double x = 12.0 + t.hour() + t.minute() / 60.0 + t.second() / 3600.0;
1318 
1319  while (x > 24.0)
1320  x -= 24.0;
1321 
1322  // Convert to screen pixel coords
1323  int ix = int(x * pW / 24.0);
1324 
1325  p.setPen(QPen(QBrush("white"), 2.0, Qt::DotLine));
1326  p.drawLine(ix, 0, ix, pH);
1327 
1328  QFont largeFont = p.font();
1329 
1330  largeFont.setPointSize(largeFont.pointSize() + 1);
1331  // Label this vertical line with the current time
1332  p.save();
1333  p.setFont(largeFont);
1334  p.translate(ix + 15, pH - 20);
1335  p.rotate(-90);
1336  // Short format necessary to avoid false time-zone labeling
1338  p.restore();
1339  p.end();
1340 }
1341 
1343 {
1344  //convert midnight local time to UT:
1345  QDateTime lt(avtUI->DateWidget->date(), QTime());
1346  return geo->LTtoUT(KStarsDateTime(lt));
1347 }
1348 
1349 double AltVsTime::getEpoch(const QString &eName)
1350 {
1351  //If Epoch field not a double, assume J2000
1352  bool ok;
1353  double epoch = eName.toDouble(&ok);
1354  if (!ok)
1355  {
1356  qCWarning(KSTARS) << "Invalid Epoch. Assuming 2000.0.";
1357  return 2000.0;
1358  }
1359  return epoch;
1360 }
1361 
1362 void AltVsTime::setDawnDusk()
1363 {
1364  /* TODO */
1365 
1366  /*
1367  KSAlmanac almanac(getDate(), geo);
1368 
1369  avtUI->View->setDawnDuskTimes(almanac.getDawnAstronomicalTwilight(), almanac.getDuskAstronomicalTwilight());
1370  avtUI->View->setMinMaxSunAlt(almanac.getSunMinAlt(), almanac.getSunMaxAlt());
1371  */
1372 
1373  /* ... */
1374 }
1375 
1377 {
1378  QPainter p; // Our painter object
1379  QPrinter printer; // Our printer object
1380  QString str_legend; // Text legend
1381  int text_height = 200; // Height of legend text zone in points
1382  QSize plot_size; // Initial plot widget size
1383  QFont plot_font; // Initial plot widget font
1384  int plot_font_size; // Initial plot widget font size
1385 
1386  // Set printer resolution to 300 dpi
1387  printer.setResolution(300);
1388 
1389  // Open print dialog
1390  //QPointer<QPrintDialog> dialog( KdePrint::createPrintDialog( &printer, this ) );
1391  //QPointer<QPrintDialog> dialog( &printer, this );
1392  QPrintDialog dialog(&printer, this);
1393  dialog.setWindowTitle(i18nc("@title:window", "Print elevation vs time plot"));
1394  if (dialog.exec() == QDialog::Accepted)
1395  {
1396  // Change mouse cursor
1398 
1399  // Save plot widget font
1400  plot_font = avtUI->View->font();
1401  // Save plot widget font size
1402  plot_font_size = plot_font.pointSize();
1403  // Save calendar widget size
1404  plot_size = avtUI->View->size();
1405 
1406  // Set text legend
1407  str_legend = i18n("Elevation vs. Time Plot");
1408  str_legend += '\n';
1409  str_legend += geo->fullName();
1410  str_legend += " - ";
1411  str_legend += avtUI->DateWidget->date().toString("dd/MM/yyyy");
1412 
1413  // Create a rectangle for legend text zone
1414  QRect text_rect(0, 0, printer.width(), text_height);
1415 
1416  // Increase plot widget font size so it looks good in 300 dpi
1417  plot_font.setPointSize(plot_font_size * 2.5);
1418  avtUI->View->setFont(plot_font);
1419  // Increase plot widget size to fit the entire page
1420  avtUI->View->resize(printer.width(), printer.height() - text_height);
1421 
1422  // Create a pixmap and render plot widget into it
1423  QPixmap pixmap(avtUI->View->size());
1424  avtUI->View->render(&pixmap);
1425 
1426  // Begin painting on printer
1427  p.begin(&printer);
1428  // Draw legend
1429  p.drawText(text_rect, Qt::AlignLeft, str_legend);
1430  // Draw plot widget
1431  p.drawPixmap(0, text_height, pixmap);
1432  // Ending painting
1433  p.end();
1434 
1435  // Restore plot widget font size
1436  plot_font.setPointSize(plot_font_size);
1437  avtUI->View->setFont(plot_font);
1438  // Restore calendar widget size
1439  avtUI->View->resize(plot_size);
1440 
1441  // Restore mouse cursor
1443  }
1444  //delete dialog;
1445 }
1446 
1447 QString AltVsTime::getObjectName(const SkyObject *o, bool translated)
1448 {
1449  QString finalObjectName;
1450  if (o->name() == "star")
1451  {
1452  StarObject *s = (StarObject *)o;
1453 
1454  // JM: Enable HD Index stars to be added to the observing list.
1455  if (s->getHDIndex() != 0)
1456  finalObjectName = QString("HD %1").arg(QString::number(s->getHDIndex()));
1457  }
1458  else
1459  finalObjectName = translated ? o->translatedName() : o->name();
1460 
1461  return finalObjectName;
1462 }
An arbitrary pixmap.
Definition: qcustomplot.h:6730
const dms & alt() const
Definition: skypoint.h:281
void slotAddSource()
Add an object to the list of displayed objects, according to the data entered in the edit boxes.
Definition: altvstime.cpp:252
void setColorAt(qreal position, const QColor &color)
AltVsTime(QWidget *parent=nullptr)
Constructor.
Definition: altvstime.cpp:41
void showText(const QPoint &pos, const QString &text, QWidget *w)
void setFinalStop(const QPointF &stop)
void slotBrowseObject()
Launch the Find Object window to select a new object for the list of displayed objects.
Definition: altvstime.cpp:348
static KStarsDateTime currentDateTime()
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
AlignLeft
int width() const const
void onYRangeChanged(const QCPRange &range)
Update the Y axis on Zoom and Drag.
Definition: altvstime.cpp:544
void setPen(const QColor &color)
virtual int exec() override
void setGraphKey(double key)
KStarsDateTime addDays(int nd) const
Modify the Date/Time by adding a number of days.
long double djd() const
void setSize(double size)
@ msLeft
0x01 left margin
Definition: qcustomplot.h:199
QString number(int n, int base)
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
virtual void setD(const double &x)
Sets floating-point value of angle, in degrees.
Definition: dms.h:179
virtual void reject()
void rotate(qreal angle)
void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio, Qt::TransformationMode transformationMode=Qt::SmoothTransformation)
void drawEllipse(const QRectF &rectangle)
void slotComputeAltitudeByTime()
Compute the altitude for a certain time.
Definition: altvstime.cpp:668
void processObject(SkyObject *o, bool forceAdd=false)
Add a SkyObject to the display.
Definition: altvstime.cpp:360
QCPRange bounded(double lowerBound, double upperBound) const
Stores dms coordinates for a point in the sky. for converting between coordinate systems.
Definition: skypoint.h:44
void setBrush(const QBrush &brush)
a dms subclass that caches its sine and cosine values every time the angle is changed.
Definition: cachingdms.h:18
void setPointSize(int pointSize)
void slotMarkTransitTime()
Mark the transit time on the curve.
Definition: altvstime.cpp:792
void setClipRect(const QRectF &rectangle, Qt::ClipOperation operation)
void slotMarkRiseTime()
Mark the rise time on the curve.
Definition: altvstime.cpp:701
QTime time() const const
void slotMarkSetTime()
Mark the set time on the curve.
Definition: altvstime.cpp:745
int pointSize() const const
void push_back(const T &value)
QIcon fromTheme(const QString &name)
@ msTop
0x04 top margin
Definition: qcustomplot.h:201
CachingDms * lst()
Definition: kstarsdata.h:223
const CachingDms * lng() const
Definition: geolocation.h:64
SkyObject * objectNamed(const QString &name)
Find object by name.
Definition: kstarsdata.cpp:429
virtual QString name(void) const
Definition: skyobject.h:145
void fill(const QColor &color)
QString translatedName() const
Definition: skyobject.h:148
KStarsDateTime getDate()
Definition: altvstime.cpp:1342
double getEpoch(const QString &eName)
Parse a string as an epoch number.
Definition: altvstime.cpp:1349
static KStarsDateTime currentDateTimeUtc()
QTime addSecs(int s) const const
bool antialiasing() const
bool isValid() const const
RightButton
qreal opacity() const const
void setModal(bool modal)
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
void drawText(const QPointF &position, const QString &text)
void slotClearBoxes()
Clear the edit boxes for specifying a new object.
Definition: altvstime.cpp:660
void fillRect(const QRectF &rectangle, const QBrush &brush)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
A plottable representing a graph in a plot.
Definition: qcustomplot.h:5440
@ limAbove
Layer is inserted above other layer.
Definition: qcustomplot.h:3757
int height() const const
void mouseOverLine(QMouseEvent *event)
Draw the white vertical line on click.
Definition: altvstime.cpp:913
KStarsDateTime addSecs(double s) const
dms altRefracted() const
Definition: skypoint.cpp:1050
void onXRangeChanged(const QCPRange &range)
Update the X axis on Zoom and Drag.
Definition: altvstime.cpp:526
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
int getHDIndex() const
Definition: starobject.h:248
virtual SkyObject * clone() const
Create copy of object.
Definition: skyobject.cpp:50
~AltVsTime() override
Destructor.
Definition: altvstime.cpp:248
QColor backgroundColor() const
bool begin(QPaintDevice *device)
virtual void setH(const double &x)
Sets floating-point value of angle, in hours.
Definition: dms.h:210
bool setFromEpoch(double e, EpochType type)
Set the Date/Time from an epoch value, represented as a double.
void setStyle(TracerStyle style)
virtual void updateCoords(const KSNumbers *num, bool includePlanets=true, const CachingDms *lat=nullptr, const CachingDms *LST=nullptr, bool forceRecompute=false)
Determine the current coordinates (RA, Dec) from the catalog coordinates (RA0, Dec0),...
Definition: skypoint.cpp:582
void rejected()
bool end()
IgnoreAspectRatio
QString i18n(const char *text, const TYPE &arg...)
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
Definition: dms.cpp:279
void setWindowFlags(Qt::WindowFlags type)
Store several time-dependent astronomical quantities.
Definition: ksnumbers.h:42
const CachingDms & dec() const
Definition: skypoint.h:269
const CachingDms * lat() const
Definition: geolocation.h:70
char * toString(const T &value)
subclass of SkyObject specialized for stars.
Definition: starobject.h:32
QTime riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact=true) const
Determine the time at which the point will rise or set.
Definition: skyobject.cpp:93
bool isEmpty() const const
GeoLocation * geo()
Definition: kstarsdata.h:229
WaitCursor
void setWindowTitle(const QString &)
Item that sticks to QCPGraph data points.
Definition: qcustomplot.h:6799
Q_SLOT bool setLayer(QCPLayer *layer)
void slotClear()
Clear the list of displayed objects.
Definition: altvstime.cpp:621
void setAlpha(int alpha)
void setType(PositionType type)
void addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role)
virtual void updateCoordsNow(const KSNumbers *num)
updateCoordsNow Shortcut for updateCoords( const KSNumbers *num, false, nullptr, nullptr,...
Definition: skypoint.h:382
@ ptPlotCoords
Dynamic positioning at a plot coordinate defined by two axes (see setAxes).
Definition: qcustomplot.h:3605
Specialized axis ticker for time spans in units of milliseconds to days.
Definition: qcustomplot.h:1793
void setupUi(QWidget *widget)
void setLSTLimits()
Determine the limits for the sideral time axis, using the sidereal time at midnight for the current d...
Definition: altvstime.cpp:1120
void slotPrint()
Print plot widget.
Definition: altvstime.cpp:1376
void setBrush(const QBrush &brush)
int hour() const const
The abstract base class for all data representing objects in a plot.
Definition: qcustomplot.h:3440
double toDouble(bool *ok) const const
void setMouseTracking(bool enable)
void slotAdvanceFocus()
Move input keyboard focus to the next logical widget.
Definition: altvstime.cpp:607
void setDate(const QDate &d)
Assign the Date according to a QDate object.
An angle, stored as degrees, but expressible in many ways.
Definition: dms.h:37
Represents the range an axis is encompassing.
Definition: qcustomplot.h:778
void setOpacity(qreal opacity)
const KStarsDateTime & ut() const
Definition: kstarsdata.h:156
void slotUpdateDateLoc()
Update the plot to reflec new Date and Location settings.
Definition: altvstime.cpp:967
void setInterpolating(bool enabled)
void setPen(const QPen &pen)
const CachingDms & ra() const
Definition: skypoint.h:263
Implement methods to find important times in a day.
Definition: ksalmanac.h:26
void setToolTip(const QString &)
void setOverrideCursor(const QCursor &cursor)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void slotHighlight(int)
Update the plot to highlight the altitude curve of the objects which is highlighted in the listbox.
Definition: altvstime.cpp:478
int second() const const
@ msBottom
0x08 bottom margin
Definition: qcustomplot.h:202
double findAltitude(SkyPoint *p, double hour)
Determine the altitude coordinate of a SkyPoint, given an hour of the day.
Definition: altvstime.cpp:466
const double & Degrees() const
Definition: dms.h:141
@ tsCircle
A circle.
Definition: qcustomplot.h:6822
void computeSunRiseSetTimes()
Determine the time of sunset and sunrise for the current date and location settings.
Definition: altvstime.cpp:837
QDate date() const const
QString getObjectName(const SkyObject *o, bool translated=true)
get object name.
Definition: altvstime.cpp:1447
void drawLine(const QLineF &line)
bool isSolarSystem() const
Definition: skyobject.h:217
const QFont & font() const const
void hideText()
void translate(const QPointF &offset)
void setContentsMargins(int left, int top, int right, int bottom)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void restore()
QString toString(Qt::DateFormat format) const const
QTime transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const
The same iteration technique described in riseSetTime() is used here.
Definition: skyobject.cpp:239
@ tssReadability
A nicely readable tick step is prioritized over matching the requested number of ticks (see setTickCo...
Definition: qcustomplot.h:1690
void save()
void setVisible(bool on)
The abstract base class for all items in a plot.
Definition: qcustomplot.h:3658
void restoreOverrideCursor()
void setGraph(QCPGraph *graph)
void setRenderHint(QPainter::RenderHint hint, bool on)
void setLayout(QLayout *layout)
void showCurrentDate()
Set the AltVsTime Date according to the current Date in the KStars main window.
Definition: altvstime.cpp:1135
void setFont(const QFont &font)
void plotMousePress(QCPAbstractPlottable *abstractPlottable, int dataIndex, QMouseEvent *event)
Show information from the curve as a tooltip.
Definition: altvstime.cpp:550
@ iRangeZoom
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom,...
Definition: qcustomplot.h:256
void slotChooseCity()
Launch the Location dialog to choose a new location.
Definition: altvstime.cpp:1103
Information about an object in the sky.
Definition: skyobject.h:41
int minute() const const
void setClipping(bool enable)
@ iRangeDrag
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes)
Definition: qcustomplot.h:255
double Hours() const
Definition: dms.h:168
void setResolution(int dpi)
SkyPoint catalogueCoord(long double jdf)
Computes the J2000.0 catalogue coordinates for this SkyPoint using the epoch removing aberration,...
Definition: skypoint.cpp:710
Relevant data about an observing location on Earth.
Definition: geolocation.h:27
void setStart(const QPointF &start)
void setPixmap(const QPixmap &pixmap)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sun Aug 14 2022 04:13:54 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.