1 /*
2 SPDX-FileCopyrightText: 2002-2003 Pablo de Vicente <vicente@oan.es>
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
AltVsTimeUI(QWidget * p)36 AltVsTimeUI::AltVsTimeUI(QWidget *p) : QFrame(p)
37 {
38 setupUi(this);
39 }
40
AltVsTime(QWidget * parent)41 AltVsTime::AltVsTime(QWidget *parent) : QDialog(parent)
42 {
43 #ifdef Q_OS_OSX
44 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
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:
102 QSharedPointer<QCPAxisTickerTime> xAxisTimeTicker(new QCPAxisTickerTime);
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:
110 QSharedPointer<QCPAxisTickerTime> xAxis2TimeTicker(new QCPAxisTickerTime);
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->setDegType(false);
145 avtUI->decBox->setDegType(true);
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
154 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
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
176 computeSunRiseSetTimes();
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,
183 SLOT(plotMousePress(QCPAbstractPlottable*,int,QMouseEvent*)));
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);
209 p.setRenderHint(QPainter::Antialiasing, true);
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);
221 p1.setRenderHint(QPainter::Antialiasing, true);
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);
233 p2.setRenderHint(QPainter::Antialiasing, true);
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
~AltVsTime()248 AltVsTime::~AltVsTime()
249 {
250 //WARNING: need to delete deleteList items!
251 }
slotAddSource()252 void AltVsTime::slotAddSource()
253 {
254 SkyObject *obj = KStarsData::Instance()->objectNamed(avtUI->nameBox->text());
255
256 if (obj)
257 {
258 //An object with the current name exists. If the object is not already
259 //in the avt list, add it.
260 bool found = false;
261 foreach (SkyObject *o, pList)
262 {
263 //if ( o->name() == obj->name() ) {
264 if (getObjectName(o, false) == getObjectName(obj, false))
265 {
266 found = true;
267 break;
268 }
269 }
270 if (!found)
271 processObject(obj);
272 }
273 else
274 {
275 //Object with the current name doesn't exist. It's possible that the
276 //user is trying to add a custom object. Assume this is the case if
277 //the RA and Dec fields are filled in.
278
279 if (!avtUI->nameBox->text().isEmpty() && !avtUI->raBox->text().isEmpty() && !avtUI->decBox->text().isEmpty())
280 {
281 bool okRA, okDec;
282 dms newRA = avtUI->raBox->createDms(false, &okRA);
283 dms newDec = avtUI->decBox->createDms(true, &okDec);
284 if (!okRA || !okDec)
285 return;
286
287 //If the epochName is blank (or any non-double), we assume J2000
288 //Otherwise, precess to J2000.
289 KStarsDateTime dt;
290 dt.setFromEpoch(getEpoch(avtUI->epochName->text()));
291 long double jd = dt.djd();
292 if (jd != J2000)
293 {
294 SkyPoint ptest(newRA, newDec);
295 //ptest.precessFromAnyEpoch(jd, J2000);
296 ptest.catalogueCoord(jd);
297 newRA.setH(ptest.ra().Hours());
298 newDec.setD(ptest.dec().Degrees());
299 }
300
301 //make sure the coords do not already exist from another object
302 bool found = false;
303 foreach (SkyObject *p, pList)
304 {
305 //within an arcsecond?
306 if (fabs(newRA.Degrees() - p->ra().Degrees()) < 0.0003 &&
307 fabs(newDec.Degrees() - p->dec().Degrees()) < 0.0003)
308 {
309 found = true;
310 break;
311 }
312 }
313 if (!found)
314 {
315 SkyObject *obj = new SkyObject(8, newRA, newDec, 1.0, avtUI->nameBox->text());
316 deleteList.append(obj); //this object will be deleted when window is destroyed
317 processObject(obj);
318 }
319 }
320
321 //If the Ra and Dec boxes are filled, but the name field is empty,
322 //move input focus to nameBox. If either coordinate box is empty,
323 //move focus there
324 if (avtUI->nameBox->text().isEmpty())
325 {
326 avtUI->nameBox->QWidget::setFocus();
327 }
328 if (avtUI->raBox->text().isEmpty())
329 {
330 avtUI->raBox->QWidget::setFocus();
331 }
332 else
333 {
334 if (avtUI->decBox->text().isEmpty())
335 avtUI->decBox->QWidget::setFocus();
336 }
337 }
338
339 avtUI->View->update();
340 }
341
342 //Use find dialog to choose an object
slotBrowseObject()343 void AltVsTime::slotBrowseObject()
344 {
345 if (FindDialog::Instance()->exec() == QDialog::Accepted)
346 {
347 SkyObject *o = FindDialog::Instance()->targetObject();
348 processObject(o);
349 }
350
351 avtUI->View->update();
352 avtUI->View->replot();
353 }
354
processObject(SkyObject * o,bool forceAdd)355 void AltVsTime::processObject(SkyObject *o, bool forceAdd)
356 {
357 if (!o)
358 return;
359
360 KSNumbers *num = new KSNumbers(getDate().djd());
361 KSNumbers *oldNum = nullptr;
362
363 //If the object is in the solar system, recompute its position for the given epochLabel
364 KStarsData *data = KStarsData::Instance();
365 if (o->isSolarSystem())
366 {
367 oldNum = new KSNumbers(data->ut().djd());
368 o->updateCoords(num, true, geo->lat(), data->lst(), true);
369 }
370
371 //precess coords to target epoch
372 o->updateCoordsNow(num);
373
374 // vector used for computing the points needed for drawing the graph
375 QVector<double> y(100), t(100);
376
377 //If this point is not in list already, add it to list
378 bool found(false);
379 foreach (SkyObject *p, pList)
380 {
381 if (o->ra().Degrees() == p->ra().Degrees() && o->dec().Degrees() == p->dec().Degrees())
382 {
383 found = true;
384 break;
385 }
386 }
387 if (found && !forceAdd)
388 {
389 qCWarning(KSTARS) << "This point is already displayed; It will not be duplicated.";
390 }
391 else
392 {
393 pList.append(o);
394
395 // make sure existing curves are thin and red:
396
397 for (int i = 0; i < avtUI->View->graphCount(); i++)
398 {
399 if (avtUI->View->graph(i)->pen().color() == Qt::white)
400 {
401 avtUI->View->graph(i)->setPen(QPen(Qt::red, 2));
402 }
403 }
404
405 // SET up the curve's name
406 avtUI->View->addGraph()->setName(o->name());
407
408 // compute the current graph:
409 // time range: 24h
410
411 int offset = 3;
412 for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
413 {
414 y[i] = findAltitude(o, h);
415 if (y[i] > maxAlt)
416 maxAlt = y[i];
417 if (y[i] < minAlt)
418 minAlt = y[i];
419 t[i] = i * 900 + 43200;
420 avtUI->View->graph(avtUI->View->graphCount() - 1)->addData(t[i], y[i]);
421 }
422 avtUI->View->graph(avtUI->View->graphCount() - 1)->setPen(QPen(Qt::white, 3));
423
424 // Go into initial state: without Zoom/Pan
425 avtUI->View->xAxis->setRange(43200, 129600);
426 avtUI->View->xAxis2->setRange(61200, 147600);
427 if (abs(minAlt) > maxAlt)
428 maxAlt = abs(minAlt);
429 else
430 minAlt = -maxAlt;
431
432 avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);
433
434 // Update background coordinates:
435 background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
436 background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);
437
438 avtUI->View->replot();
439
440 avtUI->PlotList->addItem(getObjectName(o));
441 avtUI->PlotList->setCurrentRow(avtUI->PlotList->count() - 1);
442 avtUI->raBox->showInHours(o->ra());
443 avtUI->decBox->showInDegrees(o->dec());
444 avtUI->nameBox->setText(getObjectName(o));
445
446 //Set epochName to epoch shown in date tab
447 avtUI->epochName->setText(QString().setNum(getDate().epoch()));
448 }
449 //qCDebug() << "Currently, there are " << avtUI->View->graphCount() << " objects displayed.";
450
451 //restore original position
452 if (o->isSolarSystem())
453 {
454 o->updateCoords(oldNum, true, data->geo()->lat(), data->lst(), true);
455 delete oldNum;
456 }
457 o->EquatorialToHorizontal(data->lst(), data->geo()->lat());
458 delete num;
459 }
460
findAltitude(SkyPoint * p,double hour)461 double AltVsTime::findAltitude(SkyPoint *p, double hour)
462 {
463 hour += 24.0 * DayOffset;
464
465 //getDate converts the user-entered local time to UT
466 KStarsDateTime ut = getDate().addSecs(hour * 3600.0);
467
468 CachingDms LST = geo->GSTtoLST(ut.gst());
469 p->EquatorialToHorizontal(&LST, geo->lat());
470 return p->alt().Degrees();
471 }
472
slotHighlight(int row)473 void AltVsTime::slotHighlight(int row)
474 {
475 if (row < 0)
476 return;
477
478 int rowIndex = 0;
479 //highlight the curve of the selected object
480 for (int i = 0; i < avtUI->View->graphCount(); i++)
481 {
482 if (i == row)
483 rowIndex = row;
484 else
485 {
486 avtUI->View->graph(i)->setPen(QPen(Qt::red, 2));
487 avtUI->View->graph(i)->setLayer("main");
488 }
489 }
490 avtUI->View->graph(rowIndex)->setPen(QPen(Qt::white, 3));
491 avtUI->View->graph(rowIndex)->setLayer("currentCurveLayer");
492 avtUI->View->update();
493 avtUI->View->replot();
494
495 if (row >= 0 && row < pList.size())
496 {
497 SkyObject *p = pList.at(row);
498 avtUI->raBox->showInHours(p->ra());
499 avtUI->decBox->showInDegrees(p->dec());
500 avtUI->nameBox->setText(avtUI->PlotList->currentItem()->text());
501 }
502
503 SkyObject *selectedObject = KStarsData::Instance()->objectNamed(avtUI->nameBox->text());
504 const KStarsDateTime &ut = KStarsData::Instance()->ut();
505 if (selectedObject)
506 {
507 QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
508 if (rt.isValid() == false)
509 {
510 avtUI->riseButton->setEnabled(false);
511 avtUI->setButton->setEnabled(false);
512 }
513 else
514 {
515 avtUI->riseButton->setEnabled(true);
516 avtUI->setButton->setEnabled(true);
517 }
518 }
519 }
520
onXRangeChanged(const QCPRange & range)521 void AltVsTime::onXRangeChanged(const QCPRange &range)
522 {
523 QCPRange aux = avtUI->View->xAxis2->range();
524 avtUI->View->xAxis->setRange(aux -= 18000);
525 avtUI->View->xAxis2->setRange(range.bounded(61200, 147600));
526 // if ZOOM is detected then remove the gold lines that indicate current position:
527 if (avtUI->View->xAxis->range().size() != 86400)
528 {
529 // Refresh the background:
530 background->setScaled(false);
531 background->setScaled(true, Qt::IgnoreAspectRatio);
532 background->setPixmap(*gradient);
533
534 avtUI->View->update();
535 avtUI->View->replot();
536 }
537 }
538
onYRangeChanged(const QCPRange & range)539 void AltVsTime::onYRangeChanged(const QCPRange &range)
540 {
541 int offset = 3;
542 avtUI->View->yAxis->setRange(range.bounded(minAlt - offset, maxAlt + offset));
543 }
544
plotMousePress(QCPAbstractPlottable * abstractPlottable,int dataIndex,QMouseEvent * event)545 void AltVsTime::plotMousePress(QCPAbstractPlottable *abstractPlottable, int dataIndex, QMouseEvent *event)
546 {
547 //Do we need this?
548 Q_UNUSED(dataIndex);
549
550 if (event->button() == Qt::RightButton)
551 {
552 QCPAbstractPlottable *plottable = abstractPlottable;
553 if (plottable)
554 {
555 double x = avtUI->View->xAxis->pixelToCoord(event->localPos().x());
556 double y = avtUI->View->yAxis->pixelToCoord(event->localPos().y());
557
558 QCPGraph *graph = qobject_cast<QCPGraph *>(plottable);
559
560 if (graph)
561 {
562 double yValue = y;
563 double xValue = x;
564
565 // Compute time value:
566 QTime localTime(0, 0, 0, 0);
567 QTime localSiderealTime(5, 0, 0, 0);
568
569 localTime = localTime.addSecs(int(xValue));
570 localSiderealTime = localSiderealTime.addSecs(int(xValue));
571
572 QToolTip::hideText();
573 QToolTip::showText(event->globalPos(),
574 i18n("<table>"
575 "<tr>"
576 "<th colspan=\"2\">%1</th>"
577 "</tr>"
578 "<tr>"
579 "<td>LST: </td>"
580 "<td>%3</td>"
581 "</tr>"
582 "<tr>"
583 "<td>LT: </td>"
584 "<td>%2</td>"
585 "</tr>"
586 "<tr>"
587 "<td>Altitude: </td>"
588 "<td>%4</td>"
589 "</tr>"
590 "</table>",
591 graph->name().isEmpty() ? "???" : graph->name(),
592 localTime.toString(),
593 localSiderealTime.toString(),
594 QString::number(yValue, 'f', 2) + ' ' + QChar(176)),
595 avtUI->View, avtUI->View->rect());
596 }
597 }
598 }
599 }
600
601 //move input focus to the next logical widget
slotAdvanceFocus()602 void AltVsTime::slotAdvanceFocus()
603 {
604 if (sender()->objectName() == QString("nameBox"))
605 avtUI->addButton->setFocus();
606 if (sender()->objectName() == QString("raBox"))
607 avtUI->decBox->setFocus();
608 if (sender()->objectName() == QString("decbox"))
609 avtUI->addButton->setFocus();
610 if (sender()->objectName() == QString("longBox"))
611 avtUI->latBox->setFocus();
612 if (sender()->objectName() == QString("latBox"))
613 avtUI->updateButton->setFocus();
614 }
615
slotClear()616 void AltVsTime::slotClear()
617 {
618 pList.clear();
619 //Need to delete the pointers in deleteList
620 while (!deleteList.isEmpty())
621 delete deleteList.takeFirst();
622
623 avtUI->PlotList->clear();
624 avtUI->nameBox->clear();
625 avtUI->raBox->clear();
626 avtUI->decBox->clear();
627 avtUI->epochName->clear();
628 // remove all graphs from the plot:
629 avtUI->View->clearGraphs();
630 // we remove all the dots (rise/set/transit) from the chart
631 // without removing the background image
632 int indexItem = 0, noItems = avtUI->View->itemCount();
633 QCPAbstractItem *item;
634 QCPItemPixmap *background;
635 // remove every item at a time:
636 while (noItems > 1 && indexItem < noItems)
637 {
638 // test if the current item is the background:
639 item = avtUI->View->item(indexItem);
640 background = qobject_cast<QCPItemPixmap *>(item);
641 if (background)
642 indexItem++;
643 else
644 {
645 // if not, then remove this item:
646 avtUI->View->removeItem(indexItem);
647 noItems--;
648 }
649 }
650 // update & replot the chart:
651 avtUI->View->update();
652 avtUI->View->replot();
653 }
654
slotClearBoxes()655 void AltVsTime::slotClearBoxes()
656 {
657 avtUI->nameBox->clear();
658 avtUI->raBox->clear();
659 avtUI->decBox->clear();
660 avtUI->epochName->clear();
661 }
662
slotComputeAltitudeByTime()663 void AltVsTime::slotComputeAltitudeByTime()
664 {
665 SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
666 if (selectedObject == nullptr)
667 {
668 qCWarning(KSTARS) << "slotComputeAltitudeByTime: Unable to find" << avtUI->PlotList->currentItem()->text();
669 return;
670 }
671
672 // Get Local Date & Time
673 KStarsDateTime lt = KStarsDateTime(avtUI->DateWidget->date(), avtUI->timeSpin->time(), Qt::LocalTime);
674 // Convert to UT
675 KStarsDateTime ut = geo->LTtoUT(lt);
676 // Get LST from GST
677 CachingDms LST = geo->GSTtoLST(ut.gst());
678 SkyObject *tempObject = selectedObject->clone();
679 // Update coords
680 KSNumbers num(ut.djd());
681 tempObject->updateCoords(&num, true, geo->lat(), &LST);
682 // Find Horizontal coordinates from LST & Latitude
683 selectedObject->EquatorialToHorizontal(&LST, geo->lat());
684
685 // Set altitude
686 avtUI->altitudeBox->setText(selectedObject->altRefracted().toDMSString(true));
687
688 delete (tempObject);
689 }
690
slotMarkRiseTime()691 void AltVsTime::slotMarkRiseTime()
692 {
693 const KStarsDateTime &ut = KStarsData::Instance()->ut();
694 SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
695 if (selectedObject == nullptr)
696 {
697 qCWarning(KSTARS) << "Mark Rise Time: Unable to find" << avtUI->PlotList->currentItem()->text();
698 return;
699 }
700
701 QCPItemTracer *riseTimeTracer;
702 // check if at least one graph exists in the plot
703 if (avtUI->View->graphCount() > 0)
704 {
705 double time = 0;
706 double hours, minutes;
707
708 QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow());
709
710 QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
711 // mark the Rise time with a solid red circle
712 if (rt.isValid() && selectedGraph)
713 {
714 hours = rt.hour();
715 minutes = rt.minute();
716 if (hours < 12)
717 hours += 24;
718 time = hours * 3600 + minutes * 60;
719 riseTimeTracer = new QCPItemTracer(avtUI->View);
720 riseTimeTracer->setLayer("markersLayer");
721 riseTimeTracer->setGraph(selectedGraph);
722 riseTimeTracer->setInterpolating(true);
723 riseTimeTracer->setStyle(QCPItemTracer::tsCircle);
724 riseTimeTracer->setPen(QPen(Qt::red));
725 riseTimeTracer->setBrush(Qt::red);
726 riseTimeTracer->setSize(10);
727 riseTimeTracer->setGraphKey(time);
728 riseTimeTracer->setVisible(true);
729 avtUI->View->update();
730 avtUI->View->replot();
731 }
732 }
733 }
734
slotMarkSetTime()735 void AltVsTime::slotMarkSetTime()
736 {
737 const KStarsDateTime &ut = KStarsData::Instance()->ut();
738 SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
739 if (selectedObject == nullptr)
740 {
741 qCWarning(KSTARS) << "Mark Set Time: Unable to find" << avtUI->PlotList->currentItem()->text();
742 return;
743 }
744 QCPItemTracer *setTimeTracer;
745 // check if at least one graph exists in the plot
746 if (avtUI->View->graphCount() > 0)
747 {
748 double time = 0;
749 double hours, minutes;
750
751 QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow());
752
753 QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
754 //If set time is before rise time, use set time for tomorrow
755 QTime st = selectedObject->riseSetTime(ut, geo, false); //false = use set time
756 if (st < rt)
757 st = selectedObject->riseSetTime(ut.addDays(1), geo, false); //false = use set time
758 // mark the Set time with a solid blue circle
759 if (rt.isValid())
760 {
761 hours = st.hour();
762 minutes = st.minute();
763 if (hours < 12)
764 hours += 24;
765 time = hours * 3600 + minutes * 60;
766 setTimeTracer = new QCPItemTracer(avtUI->View);
767 setTimeTracer->setLayer("markersLayer");
768 setTimeTracer->setGraph(selectedGraph);
769 setTimeTracer->setInterpolating(true);
770 setTimeTracer->setStyle(QCPItemTracer::tsCircle);
771 setTimeTracer->setPen(QPen(Qt::blue));
772 setTimeTracer->setBrush(Qt::blue);
773 setTimeTracer->setSize(10);
774 setTimeTracer->setGraphKey(time);
775 setTimeTracer->setVisible(true);
776 avtUI->View->update();
777 avtUI->View->replot();
778 }
779 }
780 }
781
slotMarkTransitTime()782 void AltVsTime::slotMarkTransitTime()
783 {
784 const KStarsDateTime &ut = KStarsData::Instance()->ut();
785 SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
786 if (selectedObject == nullptr)
787 {
788 qCWarning(KSTARS) << "Mark Transit Time: Unable to find" << avtUI->PlotList->currentItem()->text();
789 return;
790 }
791 QCPItemTracer *transitTimeTracer;
792 // check if at least one graph exists in the plot
793 if (avtUI->View->graphCount() > 0)
794 {
795 double time = 0;
796 double hours, minutes;
797
798 QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow());
799
800 QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
801 //If transit time is before rise time, use transit time for tomorrow
802 QTime tt = selectedObject->transitTime(ut, geo);
803
804 if (tt < rt)
805 tt = selectedObject->transitTime(ut.addDays(1), geo);
806 // mark the Transit time with a solid green circle
807 hours = tt.hour();
808 minutes = tt.minute();
809 if (hours < 12)
810 hours += 24;
811 time = hours * 3600 + minutes * 60;
812 transitTimeTracer = new QCPItemTracer(avtUI->View);
813 transitTimeTracer->setLayer("markersLayer");
814 transitTimeTracer->setGraph(selectedGraph);
815 transitTimeTracer->setInterpolating(true);
816 transitTimeTracer->setStyle(QCPItemTracer::tsCircle);
817 transitTimeTracer->setPen(QPen(Qt::green));
818 transitTimeTracer->setBrush(Qt::green);
819 transitTimeTracer->setSize(10);
820 transitTimeTracer->setGraphKey(time);
821 transitTimeTracer->setVisible(true);
822 avtUI->View->update();
823 avtUI->View->replot();
824 }
825 }
826
computeSunRiseSetTimes()827 void AltVsTime::computeSunRiseSetTimes()
828 {
829 //Determine the time of sunset and sunrise for the desired date and location
830 //expressed as doubles, the fraction of a full day.
831
832 /* KSAlmanac ksal(getDate(), geo); */
833
834 /* ... */
835 }
836
837 //FIXME
838 /*
839 void AltVsTime::mouseOverLine(QMouseEvent *event){
840 // Get the mouse position's coordinates relative to axes:
841 double x = avtUI->View->xAxis->pixelToCoord(event->pos().x());
842 double y = avtUI->View->yAxis->pixelToCoord(event->pos().y());
843 // Save the actual values:
844 double yValue = y;
845 double xValue = x;
846 // The offset used for the Y axis: top/bottom
847 int offset = 3;
848 // Compute the Y axis maximum value:
849 int yAxisMaxValue = maxAlt + offset;
850 // Compute the X axis minimum and maximum values:
851 int xAxisMinValue = 43200;
852 int xAxisMaxValue = 129600;
853 // Ignore the upper and left margins:
854 y = yAxisMaxValue - y;
855 x -= xAxisMinValue;
856 // We make a copy to gradient background in order to have one set of lines at a time:
857 // Otherwise, the chart would have been covered by lines
858 QPixmap copy = gradient->copy(gradient->rect());
859 // If ZOOM is not active, then draw the gold lines that indicate current mouse pisition:
860 if(avtUI->View->xAxis->range().size() == 86400){
861 QPainter p;
862
863 p.begin(©);
864 p.setPen( QPen( QBrush("gold"), 2, Qt::SolidLine ) );
865
866 // Get the gradient background's width and height:
867 int pW = gradient->rect().width();
868 int pH = gradient->rect().height();
869
870 // Compute the real coordinates within the chart:
871 y = (y*pH/2)/yAxisMaxValue;
872 x = (x*pW)/(xAxisMaxValue-xAxisMinValue);
873
874 // Draw the horizontal line (altitude):
875 p.drawLine( QLineF( 0.5, y, avtUI->View->rect().width()-0.5,y ) );
876 // Draw the altitude value:
877 p.setPen( QPen( QBrush("gold"), 3, Qt::SolidLine ) );
878 p.drawText( 25, y + 15, QString::number(yValue,'f',2) + QChar(176) );
879 p.setPen( QPen( QBrush("gold"), 1, Qt::SolidLine ) );
880 // Draw the vertical line (time):
881 p.drawLine( QLineF( x, 0.5, x, avtUI->View->rect().height()-0.5 ) );
882 // Compute and draw the time value:
883 QTime localTime(0,0,0,0);
884 localTime = localTime.addSecs(int(xValue));
885 p.save();
886 p.translate( x + 10, pH - 20 );
887 p.rotate(-90);
888 p.setPen( QPen( QBrush("gold"), 3, Qt::SolidLine ) );
889 p.drawText( 5, 5, QLocale().toString( localTime, QLocale::ShortFormat ) ); // short format necessary to avoid false time-zone labeling
890 p.restore();
891 p.end();
892 }
893 // Refresh the background:
894 background->setScaled(false);
895 background->setScaled(true, Qt::IgnoreAspectRatio);
896 background->setPixmap(copy);
897
898 avtUI->View->update();
899 avtUI->View->replot();
900 }
901 */
902
mouseOverLine(QMouseEvent * event)903 void AltVsTime::mouseOverLine(QMouseEvent *event)
904 {
905 double x = avtUI->View->xAxis->pixelToCoord(event->localPos().x());
906 double y = avtUI->View->yAxis->pixelToCoord(event->localPos().y());
907 QCPAbstractPlottable *abstractGraph = avtUI->View->plottableAt(event->pos(), false);
908 QCPGraph *graph = qobject_cast<QCPGraph *>(abstractGraph);
909
910 if (x > avtUI->View->xAxis->range().lower && x < avtUI->View->xAxis->range().upper)
911 if (y > avtUI->View->yAxis->range().lower && y < avtUI->View->yAxis->range().upper)
912 {
913 if (graph)
914 {
915 double yValue = y;
916 double xValue = x;
917
918 // Compute time value:
919 QTime localTime(0, 0, 0, 0);
920 QTime localSiderealTime(5, 0, 0, 0);
921
922 localTime = localTime.addSecs(int(xValue));
923 localSiderealTime = localSiderealTime.addSecs(int(xValue));
924
925 QToolTip::hideText();
926 QToolTip::showText(event->globalPos(),
927 i18n("<table>"
928 "<tr>"
929 "<th colspan=\"2\">%1</th>"
930 "</tr>"
931 "<tr>"
932 "<td>LST: </td>"
933 "<td>%3</td>"
934 "</tr>"
935 "<tr>"
936 "<td>LT: </td>"
937 "<td>%2</td>"
938 "</tr>"
939 "<tr>"
940 "<td>Altitude: </td>"
941 "<td>%4</td>"
942 "</tr>"
943 "</table>",
944 graph->name().isEmpty() ? "???" : graph->name(),
945 localTime.toString(), localSiderealTime.toString(),
946 QString::number(yValue, 'f', 2) + ' ' + QChar(176)),
947 avtUI->View, avtUI->View->rect());
948 }
949 else
950 QToolTip::hideText();
951 }
952
953 avtUI->View->update();
954 avtUI->View->replot();
955 }
956
slotUpdateDateLoc()957 void AltVsTime::slotUpdateDateLoc()
958 {
959 KStarsData *data = KStarsData::Instance();
960 KStarsDateTime today = getDate();
961 KSNumbers *num = new KSNumbers(today.djd());
962 KSNumbers *oldNum = nullptr;
963 CachingDms LST = geo->GSTtoLST(today.gst());
964
965 //First determine time of sunset and sunrise
966 computeSunRiseSetTimes();
967 // Determine dawn/dusk time and min/max sun elevation
968 setDawnDusk();
969
970 for (int i = 0; i < pList.count(); ++i)
971 {
972 SkyObject *o = pList.at(i);
973 if (o)
974 {
975 //If the object is in the solar system, recompute its position for the given date
976 if (o->isSolarSystem())
977 {
978 oldNum = new KSNumbers(data->ut().djd());
979 o->updateCoords(num, true, geo->lat(), &LST, true);
980 }
981
982 //precess coords to target epoch
983 o->updateCoordsNow(num);
984
985 //update pList entry
986 pList.replace(i, o);
987
988 // We are creating a new data set (time, altitude) for the new date:
989 QVector<double> time_dataSet, altitude_dataSet;
990 double point_altitudeValue, point_timeValue;
991 // compute the new graph values:
992 // time range: 24h
993 int offset = 3;
994 for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
995 {
996 point_altitudeValue = findAltitude(o, h);
997 altitude_dataSet.push_back(point_altitudeValue);
998 if (point_altitudeValue > maxAlt)
999 maxAlt = point_altitudeValue;
1000 if (point_altitudeValue < minAlt)
1001 minAlt = point_altitudeValue;
1002 point_timeValue = i * 900 + 43200;
1003 time_dataSet.push_back(point_timeValue);
1004 }
1005
1006 // Replace graph data set:
1007 avtUI->View->graph(i)->setData(time_dataSet, altitude_dataSet);
1008
1009 // Go into initial state: without Zoom/Pan
1010 avtUI->View->xAxis->setRange(43200, 129600);
1011 avtUI->View->xAxis2->setRange(61200, 147600);
1012
1013 // Center the altitude axis in 0 value:
1014 if (abs(minAlt) > maxAlt)
1015 maxAlt = abs(minAlt);
1016 else
1017 minAlt = -maxAlt;
1018 avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);
1019
1020 // Update background coordinates:
1021 background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
1022 background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);
1023
1024 // Redraw the plot:
1025 avtUI->View->replot();
1026
1027 //restore original position
1028 if (o->isSolarSystem())
1029 {
1030 o->updateCoords(oldNum, true, data->geo()->lat(), data->lst());
1031 delete oldNum;
1032 oldNum = nullptr;
1033 }
1034 o->EquatorialToHorizontal(data->lst(), data->geo()->lat());
1035 }
1036 else //assume unfound object is a custom object
1037 {
1038 pList.at(i)->updateCoordsNow(num); //precess to desired epoch
1039
1040 // We are creating a new data set (time, altitude) for the new date:
1041 QVector<double> time_dataSet, altitude_dataSet;
1042 double point_altitudeValue, point_timeValue;
1043 // compute the new graph values:
1044 // time range: 24h
1045 int offset = 3;
1046 for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
1047 {
1048 point_altitudeValue = findAltitude(pList.at(i), h);
1049 altitude_dataSet.push_back(point_altitudeValue);
1050 if (point_altitudeValue > maxAlt)
1051 maxAlt = point_altitudeValue;
1052 if (point_altitudeValue < minAlt)
1053 minAlt = point_altitudeValue;
1054 point_timeValue = i * 900 + 43200;
1055 time_dataSet.push_back(point_timeValue);
1056 }
1057
1058 // Replace graph data set:
1059 avtUI->View->graph(i)->setData(time_dataSet, altitude_dataSet);
1060
1061 // Go into initial state: without Zoom/Pan
1062 avtUI->View->xAxis->setRange(43200, 129600);
1063 avtUI->View->xAxis2->setRange(61200, 147600);
1064
1065 // Center the altitude axis in 0 value:
1066 if (abs(minAlt) > maxAlt)
1067 maxAlt = abs(minAlt);
1068 else
1069 minAlt = -maxAlt;
1070 avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);
1071
1072 // Update background coordinates:
1073 background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
1074 background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);
1075
1076 // Redraw the plot:
1077 avtUI->View->replot();
1078 }
1079 }
1080
1081 if (getDate().time().hour() > 12)
1082 DayOffset = 1;
1083 else
1084 DayOffset = 0;
1085
1086 setLSTLimits();
1087 slotHighlight(avtUI->PlotList->currentRow());
1088 avtUI->View->update();
1089
1090 delete num;
1091 }
1092
slotChooseCity()1093 void AltVsTime::slotChooseCity()
1094 {
1095 QPointer<LocationDialog> ld = new LocationDialog(this);
1096 if (ld->exec() == QDialog::Accepted)
1097 {
1098 GeoLocation *newGeo = ld->selectedCity();
1099 if (newGeo)
1100 {
1101 geo = newGeo;
1102 avtUI->latBox->showInDegrees(geo->lat());
1103 avtUI->longBox->showInDegrees(geo->lng());
1104 }
1105 }
1106 delete ld;
1107 }
1108
1109 // FIXME: should we remove this method?
setLSTLimits()1110 void AltVsTime::setLSTLimits()
1111 {
1112 /*
1113 //UT at noon on target date
1114 KStarsDateTime ut = getDate().addSecs(((double)DayOffset + 0.5)*86400.);
1115
1116 dms lst = geo->GSTtoLST(ut.gst());
1117 double h1 = lst.Hours();
1118 if(h1 > 12.0)
1119 h1 -= 24.0;
1120 double h2 = h1 + 24.0;
1121 avtUI->View->setSecondaryLimits(h1, h2, -90.0, 90.0);
1122 */
1123 }
1124
showCurrentDate()1125 void AltVsTime::showCurrentDate()
1126 {
1127 KStarsDateTime dt = KStarsDateTime::currentDateTime();
1128 if (dt.time() > QTime(12, 0, 0))
1129 dt = dt.addDays(1);
1130 avtUI->DateWidget->setDate(dt.date());
1131 }
1132
drawGradient()1133 void AltVsTime::drawGradient()
1134 {
1135 // Things needed for Gradient:
1136 KStarsDateTime dtt = KStarsDateTime::currentDateTime();
1137 GeoLocation *geoLoc = KStarsData::Instance()->geo();
1138 QDateTime midnight = QDateTime(dtt.date(), QTime());
1139 KStarsDateTime const utt = geoLoc->LTtoUT(KStarsDateTime(midnight));
1140
1141 // Variables needed for Gradient:
1142 double SunRise, SunSet, Dawn, Dusk, SunMinAlt, SunMaxAlt;
1143 double MoonRise, MoonSet, MoonIllum;
1144
1145 KSAlmanac ksal(utt, geoLoc);
1146
1147 // Get the values:
1148 SunRise = ksal.getSunRise();
1149 SunSet = ksal.getSunSet();
1150 SunMaxAlt = ksal.getSunMaxAlt();
1151 SunMinAlt = ksal.getSunMinAlt();
1152 MoonRise = ksal.getMoonRise();
1153 MoonSet = ksal.getMoonSet();
1154 MoonIllum = ksal.getMoonIllum();
1155 Dawn = ksal.getDawnAstronomicalTwilight();
1156 Dusk = ksal.getDuskAstronomicalTwilight();
1157
1158 gradient = new QPixmap(avtUI->View->rect().width(), avtUI->View->rect().height());
1159
1160 QPainter p;
1161
1162 p.begin(gradient);
1163 KPlotWidget *kPW = new KPlotWidget;
1164 p.setRenderHint(QPainter::Antialiasing, kPW->antialiasing());
1165 p.fillRect(gradient->rect(), kPW->backgroundColor());
1166
1167 p.setClipRect(gradient->rect());
1168 p.setClipping(true);
1169
1170 int pW = gradient->rect().width();
1171 int pH = gradient->rect().height();
1172
1173 QColor SkyColor(0, 100, 200);
1174 // TODO
1175 // if( Options::darkAppColors() )
1176 // SkyColor = QColor( 200, 0, 0 ); // use something red, visible through a red filter
1177
1178 // Draw gradient representing lunar interference in the sky
1179 if (MoonIllum > 0.01) // do this only if Moon illumination is reasonable so it's important
1180 {
1181 int moonrise = int(pW * (0.5 + MoonRise));
1182 int moonset = int(pW * (MoonSet - 0.5));
1183 if (moonset < 0)
1184 moonset += pW;
1185 if (moonrise > pW)
1186 moonrise -= pW;
1187 int moonalpha = int(10 + MoonIllum * 130);
1188 int fadewidth =
1189 pW *
1190 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.
1191 QColor MoonColor(255, 255, 255, moonalpha);
1192
1193 if (moonset < moonrise)
1194 {
1195 QLinearGradient grad =
1196 QLinearGradient(QPointF(moonset - fadewidth, 0.0), QPointF(moonset + fadewidth, 0.0));
1197 grad.setColorAt(0, MoonColor);
1198 grad.setColorAt(1, Qt::transparent);
1199 p.fillRect(QRectF(0.0, 0.0, moonset + fadewidth, pH),
1200 grad); // gradient should be padded until moonset - fadewidth (see QLinearGradient docs)
1201 grad.setStart(QPointF(moonrise + fadewidth, 0.0));
1202 grad.setFinalStop(QPointF(moonrise - fadewidth, 0.0));
1203 p.fillRect(QRectF(moonrise - fadewidth, 0.0, pW - moonrise + fadewidth, pH), grad);
1204 }
1205 else
1206 {
1207 qreal opacity = p.opacity();
1208 p.setOpacity(opacity / 4);
1209 p.fillRect(QRectF(moonrise + fadewidth, 0.0, moonset - moonrise - 2 * fadewidth, pH), MoonColor);
1210 QLinearGradient grad =
1211 QLinearGradient(QPointF(moonrise + fadewidth, 0.0), QPointF(moonrise - fadewidth, 0.0));
1212 grad.setColorAt(0, MoonColor);
1213 grad.setColorAt(1, Qt::transparent);
1214 p.fillRect(QRectF(0.0, 0.0, moonrise + fadewidth, pH), grad);
1215 grad.setStart(QPointF(moonset - fadewidth, 0.0));
1216 grad.setFinalStop(QPointF(moonset + fadewidth, 0.0));
1217 p.fillRect(QRectF(moonset - fadewidth, 0.0, pW - moonset, pH), grad);
1218 p.setOpacity(opacity);
1219 }
1220 }
1221
1222 //draw daytime sky if the Sun rises for the current date/location
1223 if (SunMaxAlt > -18.0)
1224 {
1225 //Display centered on midnight, so need to modulate dawn/dusk by 0.5
1226 int rise = int(pW * (0.5 + SunRise));
1227 int set = int(pW * (SunSet - 0.5));
1228 int da = int(pW * (0.5 + Dawn));
1229 int du = int(pW * (Dusk - 0.5));
1230
1231 if (SunMinAlt > 0.0)
1232 {
1233 // The sun never set and the sky is always blue
1234 p.fillRect(rect(), SkyColor);
1235 }
1236 else if (SunMaxAlt < 0.0 && SunMinAlt < -18.0)
1237 {
1238 // The sun never rise but the sky is not completely dark
1239 QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(du, 0.0));
1240
1241 QColor gradStartColor = SkyColor;
1242 gradStartColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255);
1243
1244 grad.setColorAt(0, gradStartColor);
1245 grad.setColorAt(1, Qt::transparent);
1246 p.fillRect(QRectF(0.0, 0.0, du, pH), grad);
1247 grad.setStart(QPointF(pW, 0.0));
1248 grad.setFinalStop(QPointF(da, 0.0));
1249 p.fillRect(QRectF(da, 0.0, pW, pH), grad);
1250 }
1251 else if (SunMaxAlt < 0.0 && SunMinAlt > -18.0)
1252 {
1253 // The sun never rise but the sky is NEVER completely dark
1254 QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(pW, 0.0));
1255
1256 QColor gradStartEndColor = SkyColor;
1257 gradStartEndColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255);
1258 QColor gradMidColor = SkyColor;
1259 gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255);
1260
1261 grad.setColorAt(0, gradStartEndColor);
1262 grad.setColorAt(0.5, gradMidColor);
1263 grad.setColorAt(1, gradStartEndColor);
1264 p.fillRect(QRectF(0.0, 0.0, pW, pH), grad);
1265 }
1266 else if (Dawn < 0.0)
1267 {
1268 // The sun sets and rises but the sky is never completely dark
1269 p.fillRect(0, 0, set, int(0.5 * pH), SkyColor);
1270 p.fillRect(rise, 0, pW, int(0.5 * pH), SkyColor);
1271
1272 QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(rise, 0.0));
1273
1274 QColor gradMidColor = SkyColor;
1275 gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255);
1276
1277 grad.setColorAt(0, SkyColor);
1278 grad.setColorAt(0.5, gradMidColor);
1279 grad.setColorAt(1, SkyColor);
1280 p.fillRect(QRectF(set, 0.0, rise - set, pH), grad);
1281 }
1282 else
1283 {
1284 p.fillRect(0, 0, set, pH, SkyColor);
1285 p.fillRect(rise, 0, pW, pH, SkyColor);
1286
1287 QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(du, 0.0));
1288 grad.setColorAt(0, SkyColor);
1289 grad.setColorAt(
1290 1,
1291 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)
1292 p.fillRect(QRectF(set, 0.0, du - set, pH), grad);
1293
1294 grad.setStart(QPointF(rise, 0.0));
1295 grad.setFinalStop(QPointF(da, 0.0));
1296 p.fillRect(QRectF(da, 0.0, rise - da, pH), grad);
1297 }
1298 }
1299
1300 p.fillRect(0, int(0.5 * pH), pW, int(0.5 * pH), KStarsData::Instance()->colorScheme()->colorNamed("HorzColor"));
1301
1302 p.setClipping(false);
1303
1304 // Add vertical line indicating "now"
1305 // Convert the current system clock time to the TZ corresponding to geo
1306 QTime t = geoLoc->UTtoLT(KStarsDateTime::currentDateTimeUtc()).time();
1307 double x = 12.0 + t.hour() + t.minute() / 60.0 + t.second() / 3600.0;
1308
1309 while (x > 24.0)
1310 x -= 24.0;
1311
1312 // Convert to screen pixel coords
1313 int ix = int(x * pW / 24.0);
1314
1315 p.setPen(QPen(QBrush("white"), 2.0, Qt::DotLine));
1316 p.drawLine(ix, 0, ix, pH);
1317
1318 QFont largeFont = p.font();
1319
1320 largeFont.setPointSize(largeFont.pointSize() + 1);
1321 // Label this vertical line with the current time
1322 p.save();
1323 p.setFont(largeFont);
1324 p.translate(ix + 15, pH - 20);
1325 p.rotate(-90);
1326 // Short format necessary to avoid false time-zone labeling
1327 p.drawText(0, 0, QLocale().toString(t, QLocale::ShortFormat));
1328 p.restore();
1329 p.end();
1330 }
1331
getDate()1332 KStarsDateTime AltVsTime::getDate()
1333 {
1334 //convert midnight local time to UT:
1335 QDateTime lt(avtUI->DateWidget->date(), QTime());
1336 return geo->LTtoUT(KStarsDateTime(lt));
1337 }
1338
getEpoch(const QString & eName)1339 double AltVsTime::getEpoch(const QString &eName)
1340 {
1341 //If Epoch field not a double, assume J2000
1342 bool ok;
1343 double epoch = eName.toDouble(&ok);
1344 if (!ok)
1345 {
1346 qCWarning(KSTARS) << "Invalid Epoch. Assuming 2000.0.";
1347 return 2000.0;
1348 }
1349 return epoch;
1350 }
1351
setDawnDusk()1352 void AltVsTime::setDawnDusk()
1353 {
1354 /* TODO */
1355
1356 /*
1357 KSAlmanac almanac(getDate(), geo);
1358
1359 avtUI->View->setDawnDuskTimes(almanac.getDawnAstronomicalTwilight(), almanac.getDuskAstronomicalTwilight());
1360 avtUI->View->setMinMaxSunAlt(almanac.getSunMinAlt(), almanac.getSunMaxAlt());
1361 */
1362
1363 /* ... */
1364 }
1365
slotPrint()1366 void AltVsTime::slotPrint()
1367 {
1368 QPainter p; // Our painter object
1369 QPrinter printer; // Our printer object
1370 QString str_legend; // Text legend
1371 int text_height = 200; // Height of legend text zone in points
1372 QSize plot_size; // Initial plot widget size
1373 QFont plot_font; // Initial plot widget font
1374 int plot_font_size; // Initial plot widget font size
1375
1376 // Set printer resolution to 300 dpi
1377 printer.setResolution(300);
1378
1379 // Open print dialog
1380 //QPointer<QPrintDialog> dialog( KdePrint::createPrintDialog( &printer, this ) );
1381 //QPointer<QPrintDialog> dialog( &printer, this );
1382 QPrintDialog dialog(&printer, this);
1383 dialog.setWindowTitle(i18nc("@title:window", "Print elevation vs time plot"));
1384 if (dialog.exec() == QDialog::Accepted)
1385 {
1386 // Change mouse cursor
1387 QApplication::setOverrideCursor(Qt::WaitCursor);
1388
1389 // Save plot widget font
1390 plot_font = avtUI->View->font();
1391 // Save plot widget font size
1392 plot_font_size = plot_font.pointSize();
1393 // Save calendar widget size
1394 plot_size = avtUI->View->size();
1395
1396 // Set text legend
1397 str_legend = i18n("Elevation vs. Time Plot");
1398 str_legend += '\n';
1399 str_legend += geo->fullName();
1400 str_legend += " - ";
1401 str_legend += avtUI->DateWidget->date().toString("dd/MM/yyyy");
1402
1403 // Create a rectangle for legend text zone
1404 QRect text_rect(0, 0, printer.width(), text_height);
1405
1406 // Increase plot widget font size so it looks good in 300 dpi
1407 plot_font.setPointSize(plot_font_size * 2.5);
1408 avtUI->View->setFont(plot_font);
1409 // Increase plot widget size to fit the entire page
1410 avtUI->View->resize(printer.width(), printer.height() - text_height);
1411
1412 // Create a pixmap and render plot widget into it
1413 QPixmap pixmap(avtUI->View->size());
1414 avtUI->View->render(&pixmap);
1415
1416 // Begin painting on printer
1417 p.begin(&printer);
1418 // Draw legend
1419 p.drawText(text_rect, Qt::AlignLeft, str_legend);
1420 // Draw plot widget
1421 p.drawPixmap(0, text_height, pixmap);
1422 // Ending painting
1423 p.end();
1424
1425 // Restore plot widget font size
1426 plot_font.setPointSize(plot_font_size);
1427 avtUI->View->setFont(plot_font);
1428 // Restore calendar widget size
1429 avtUI->View->resize(plot_size);
1430
1431 // Restore mouse cursor
1432 QApplication::restoreOverrideCursor();
1433 }
1434 //delete dialog;
1435 }
1436
getObjectName(const SkyObject * o,bool translated)1437 QString AltVsTime::getObjectName(const SkyObject *o, bool translated)
1438 {
1439 QString finalObjectName;
1440 if (o->name() == "star")
1441 {
1442 StarObject *s = (StarObject *)o;
1443
1444 // JM: Enable HD Index stars to be added to the observing list.
1445 if (s->getHDIndex() != 0)
1446 finalObjectName = QString("HD %1").arg(QString::number(s->getHDIndex()));
1447 }
1448 else
1449 finalObjectName = translated ? o->translatedName() : o->name();
1450
1451 return finalObjectName;
1452 }
1453