File indexing completed on 2024-04-28 07:32:32
0001 /* 0002 SPDX-FileCopyrightText: 2002-2003 Pablo de Vicente <vicente@oan.es> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "altvstime.h" 0008 0009 #include "avtplotwidget.h" 0010 #include "dms.h" 0011 #include "ksalmanac.h" 0012 #include "kstarsdata.h" 0013 #include "kstarsdatetime.h" 0014 #include "ksnumbers.h" 0015 #include "simclock.h" 0016 #include "kssun.h" 0017 #include "dialogs/finddialog.h" 0018 #include "dialogs/locationdialog.h" 0019 #include "geolocation.h" 0020 #include "skyobjects/skypoint.h" 0021 #include "skyobjects/skyobject.h" 0022 #include "skyobjects/starobject.h" 0023 0024 #include <KLocalizedString> 0025 #include <kplotwidget.h> 0026 0027 #include <QVBoxLayout> 0028 #include <QFrame> 0029 #include <QDialog> 0030 #include <QPainter> 0031 #include <QtPrintSupport/QPrinter> 0032 #include <QtPrintSupport/QPrintDialog> 0033 0034 #include "kstars_debug.h" 0035 0036 AltVsTimeUI::AltVsTimeUI(QWidget *p) : QFrame(p) 0037 { 0038 setupUi(this); 0039 } 0040 0041 AltVsTime::AltVsTime(QWidget *parent) : QDialog(parent) 0042 { 0043 #ifdef Q_OS_OSX 0044 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); 0045 #endif 0046 0047 setWindowTitle(i18nc("@title:window", "Altitude vs. Time")); 0048 0049 setModal(false); 0050 0051 QVBoxLayout *topLayout = new QVBoxLayout; 0052 setLayout(topLayout); 0053 topLayout->setContentsMargins(0, 0, 0, 0); 0054 avtUI = new AltVsTimeUI(this); 0055 0056 // Layers for setting up the plot's priority: the current curve should be above the other curves. 0057 // The Rise/Set/Transit markers should be on top, with highest priority. 0058 avtUI->View->addLayer("currentCurveLayer", avtUI->View->layer("main"), QCustomPlot::limAbove); 0059 avtUI->View->addLayer("markersLayer", avtUI->View->layer("currentCurveLayer"), QCustomPlot::limAbove); 0060 0061 // Set up the Graph Window: 0062 avtUI->View->setBackground(QBrush(QColor(0, 0, 0))); 0063 avtUI->View->xAxis->grid()->setVisible(false); 0064 avtUI->View->yAxis->grid()->setVisible(false); 0065 QColor axisColor(Qt::white); 0066 QPen axisPen(axisColor, 1); 0067 avtUI->View->xAxis->setBasePen(axisPen); 0068 avtUI->View->xAxis->setTickPen(axisPen); 0069 avtUI->View->xAxis->setSubTickPen(axisPen); 0070 avtUI->View->xAxis->setTickLabelColor(axisColor); 0071 avtUI->View->xAxis->setLabelColor(axisColor); 0072 0073 avtUI->View->xAxis2->setBasePen(axisPen); 0074 avtUI->View->xAxis2->setTickPen(axisPen); 0075 avtUI->View->xAxis2->setSubTickPen(axisPen); 0076 avtUI->View->xAxis2->setTickLabelColor(axisColor); 0077 avtUI->View->xAxis2->setLabelColor(axisColor); 0078 0079 avtUI->View->yAxis->setBasePen(axisPen); 0080 avtUI->View->yAxis->setTickPen(axisPen); 0081 avtUI->View->yAxis->setSubTickPen(axisPen); 0082 avtUI->View->yAxis->setTickLabelColor(axisColor); 0083 avtUI->View->yAxis->setLabelColor(axisColor); 0084 0085 avtUI->View->yAxis2->setBasePen(axisPen); 0086 avtUI->View->yAxis2->setTickPen(axisPen); 0087 avtUI->View->yAxis2->setSubTickPen(axisPen); 0088 avtUI->View->yAxis2->setTickLabelColor(axisColor); 0089 avtUI->View->yAxis2->setLabelColor(axisColor); 0090 0091 // give the axis some labels: 0092 avtUI->View->xAxis2->setLabel(i18n("Local Sidereal Time")); 0093 avtUI->View->xAxis2->setVisible(true); 0094 avtUI->View->yAxis2->setVisible(true); 0095 avtUI->View->yAxis2->setTickLength(0, 0); 0096 avtUI->View->xAxis->setLabel(i18n("Local Time")); 0097 avtUI->View->yAxis->setLabel(i18n("Altitude")); 0098 avtUI->View->xAxis->setRange(43200, 129600); 0099 avtUI->View->xAxis2->setRange(61200, 147600); 0100 0101 // configure the bottom axis to show time instead of number: 0102 QSharedPointer<QCPAxisTickerTime> xAxisTimeTicker(new QCPAxisTickerTime); 0103 xAxisTimeTicker->setTimeFormat("%h:%m"); 0104 xAxisTimeTicker->setTickCount(12); 0105 xAxisTimeTicker->setTickStepStrategy(QCPAxisTicker::tssReadability); 0106 xAxisTimeTicker->setTickOrigin(Qt::UTC); 0107 avtUI->View->xAxis->setTicker(xAxisTimeTicker); 0108 0109 // configure the top axis to show time instead of number: 0110 QSharedPointer<QCPAxisTickerTime> xAxis2TimeTicker(new QCPAxisTickerTime); 0111 xAxis2TimeTicker->setTimeFormat("%h:%m"); 0112 xAxis2TimeTicker->setTickCount(12); 0113 xAxis2TimeTicker->setTickStepStrategy(QCPAxisTicker::tssReadability); 0114 xAxis2TimeTicker->setTickOrigin(Qt::UTC); 0115 avtUI->View->xAxis2->setTicker(xAxis2TimeTicker); 0116 0117 // set up the Zoom/Pan features for the Top X Axis 0118 avtUI->View->axisRect()->setRangeDragAxes(avtUI->View->xAxis2, avtUI->View->yAxis); 0119 avtUI->View->axisRect()->setRangeZoomAxes(avtUI->View->xAxis2, avtUI->View->yAxis); 0120 0121 // set up the margins, for a nice view 0122 avtUI->View->axisRect()->setAutoMargins(QCP::msBottom | QCP::msLeft | QCP::msTop); 0123 avtUI->View->axisRect()->setMargins(QMargins(0, 0, 7, 0)); 0124 0125 // set up the interaction set: 0126 avtUI->View->setInteraction(QCP::iRangeZoom, true); 0127 avtUI->View->setInteraction(QCP::iRangeDrag, true); 0128 0129 // set up the selection tolerance for checking if a certain graph is or not selected: 0130 avtUI->View->setSelectionTolerance(5); 0131 0132 // draw the gradient: 0133 drawGradient(); 0134 0135 // set up the background image: 0136 background = new QCPItemPixmap(avtUI->View); 0137 background->setPixmap(*gradient); 0138 background->topLeft->setType(QCPItemPosition::ptPlotCoords); 0139 background->bottomRight->setType(QCPItemPosition::ptPlotCoords); 0140 background->setScaled(true, Qt::IgnoreAspectRatio); 0141 background->setLayer("background"); 0142 background->setVisible(true); 0143 0144 avtUI->raBox->setUnits(dmsBox::HOURS); 0145 avtUI->decBox->setUnits(dmsBox::DEGREES); 0146 0147 //FIXME: 0148 //Doesn't make sense to manually adjust long/lat unless we can modify TZ also 0149 avtUI->longBox->setReadOnly(true); 0150 avtUI->latBox->setReadOnly(true); 0151 0152 topLayout->addWidget(avtUI); 0153 0154 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); 0155 topLayout->addWidget(buttonBox); 0156 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); 0157 0158 QPushButton *printB = new QPushButton(QIcon::fromTheme("document-print"), i18n("&Print...")); 0159 printB->setToolTip(i18n("Print the Altitude vs. time plot")); 0160 buttonBox->addButton(printB, QDialogButtonBox::ActionRole); 0161 connect(printB, SIGNAL(clicked()), this, SLOT(slotPrint())); 0162 0163 geo = KStarsData::Instance()->geo(); 0164 0165 DayOffset = 0; 0166 // set up the initial minimum and maximum altitude 0167 minAlt = 0; 0168 maxAlt = 0; 0169 showCurrentDate(); 0170 if (getDate().time().hour() > 12) 0171 DayOffset = 1; 0172 0173 avtUI->longBox->show(geo->lng()); 0174 avtUI->latBox->show(geo->lat()); 0175 0176 computeSunRiseSetTimes(); 0177 setLSTLimits(); 0178 setDawnDusk(); 0179 0180 connect(avtUI->View->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(onYRangeChanged(QCPRange))); 0181 connect(avtUI->View->xAxis2, SIGNAL(rangeChanged(QCPRange)), this, SLOT(onXRangeChanged(QCPRange))); 0182 connect(avtUI->View, SIGNAL(plottableClick(QCPAbstractPlottable*, int, QMouseEvent*)), this, 0183 SLOT(plotMousePress(QCPAbstractPlottable*, int, QMouseEvent*))); 0184 connect(avtUI->View, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseOverLine(QMouseEvent*))); 0185 0186 connect(avtUI->browseButton, SIGNAL(clicked()), this, SLOT(slotBrowseObject())); 0187 connect(avtUI->cityButton, SIGNAL(clicked()), this, SLOT(slotChooseCity())); 0188 connect(avtUI->updateButton, SIGNAL(clicked()), this, SLOT(slotUpdateDateLoc())); 0189 connect(avtUI->clearButton, SIGNAL(clicked()), this, SLOT(slotClear())); 0190 connect(avtUI->addButton, SIGNAL(clicked()), this, SLOT(slotAddSource())); 0191 connect(avtUI->nameBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource())); 0192 connect(avtUI->raBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource())); 0193 connect(avtUI->decBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource())); 0194 connect(avtUI->clearFieldsButton, SIGNAL(clicked()), this, SLOT(slotClearBoxes())); 0195 connect(avtUI->longBox, SIGNAL(returnPressed()), this, SLOT(slotAdvanceFocus())); 0196 connect(avtUI->latBox, SIGNAL(returnPressed()), this, SLOT(slotAdvanceFocus())); 0197 connect(avtUI->PlotList, SIGNAL(currentRowChanged(int)), this, SLOT(slotHighlight(int))); 0198 connect(avtUI->computeButton, SIGNAL(clicked()), this, SLOT(slotComputeAltitudeByTime())); 0199 connect(avtUI->riseButton, SIGNAL(clicked()), this, SLOT(slotMarkRiseTime())); 0200 connect(avtUI->setButton, SIGNAL(clicked()), this, SLOT(slotMarkSetTime())); 0201 connect(avtUI->transitButton, SIGNAL(clicked()), this, SLOT(slotMarkTransitTime())); 0202 0203 // Set up the Rise/Set/Transit buttons' icons: 0204 0205 QPixmap redButton(100, 100); 0206 redButton.fill(Qt::transparent); 0207 QPainter p; 0208 p.begin(&redButton); 0209 p.setRenderHint(QPainter::Antialiasing, true); 0210 QPen pen(Qt::red, 2); 0211 p.setPen(pen); 0212 QBrush brush(Qt::red); 0213 p.setBrush(brush); 0214 p.drawEllipse(15, 15, 80, 80); 0215 p.end(); 0216 0217 QPixmap blueButton(100, 100); 0218 blueButton.fill(Qt::transparent); 0219 QPainter p1; 0220 p1.begin(&blueButton); 0221 p1.setRenderHint(QPainter::Antialiasing, true); 0222 QPen pen1(Qt::blue, 2); 0223 p1.setPen(pen1); 0224 QBrush brush1(Qt::blue); 0225 p1.setBrush(brush1); 0226 p1.drawEllipse(15, 15, 80, 80); 0227 p1.end(); 0228 0229 QPixmap greenButton(100, 100); 0230 greenButton.fill(Qt::transparent); 0231 QPainter p2; 0232 p2.begin(&greenButton); 0233 p2.setRenderHint(QPainter::Antialiasing, true); 0234 QPen pen2(Qt::green, 2); 0235 p2.setPen(pen2); 0236 QBrush brush2(Qt::green); 0237 p2.setBrush(brush2); 0238 p2.drawEllipse(15, 15, 80, 80); 0239 p2.end(); 0240 0241 avtUI->riseButton->setIcon(QIcon(redButton)); 0242 avtUI->setButton->setIcon(QIcon(blueButton)); 0243 avtUI->transitButton->setIcon(QIcon(greenButton)); 0244 0245 setMouseTracking(true); 0246 } 0247 0248 AltVsTime::~AltVsTime() 0249 { 0250 //WARNING: need to delete deleteList items! 0251 } 0252 void AltVsTime::slotAddSource() 0253 { 0254 0255 SkyObject *obj = KStarsData::Instance()->objectNamed(avtUI->nameBox->text()); 0256 if (!obj) 0257 { 0258 QString name = FindDialog::processSearchText(avtUI->nameBox->text()); 0259 obj = KStarsData::Instance()->objectNamed(name); 0260 } 0261 if (obj) 0262 { 0263 //An object with the current name exists. If the object is not already 0264 //in the avt list, add it. 0265 bool found = false; 0266 foreach (SkyObject *o, pList) 0267 { 0268 //if ( o->name() == obj->name() ) { 0269 if (getObjectName(o, false) == getObjectName(obj, false)) 0270 { 0271 found = true; 0272 break; 0273 } 0274 } 0275 if (!found) 0276 processObject(obj); 0277 } 0278 else 0279 { 0280 //Object with the current name doesn't exist. It's possible that the 0281 //user is trying to add a custom object. Assume this is the case if 0282 //the RA and Dec fields are filled in. 0283 0284 if (!avtUI->nameBox->text().isEmpty() && !avtUI->raBox->text().isEmpty() && !avtUI->decBox->text().isEmpty()) 0285 { 0286 bool okRA, okDec; 0287 dms newRA = avtUI->raBox->createDms(&okRA); 0288 dms newDec = avtUI->decBox->createDms(&okDec); 0289 if (!okRA || !okDec) 0290 return; 0291 0292 //If the epochName is blank (or any non-double), we assume J2000 0293 //Otherwise, precess to J2000. 0294 KStarsDateTime dt; 0295 dt.setFromEpoch(getEpoch(avtUI->epochName->text())); 0296 long double jd = dt.djd(); 0297 if (jd != J2000) 0298 { 0299 SkyPoint ptest(newRA, newDec); 0300 //ptest.precessFromAnyEpoch(jd, J2000); 0301 ptest.catalogueCoord(jd); 0302 newRA.setH(ptest.ra().Hours()); 0303 newDec.setD(ptest.dec().Degrees()); 0304 } 0305 0306 //make sure the coords do not already exist from another object 0307 bool found = false; 0308 foreach (SkyObject *p, pList) 0309 { 0310 //within an arcsecond? 0311 if (fabs(newRA.Degrees() - p->ra().Degrees()) < 0.0003 && 0312 fabs(newDec.Degrees() - p->dec().Degrees()) < 0.0003) 0313 { 0314 found = true; 0315 break; 0316 } 0317 } 0318 if (!found) 0319 { 0320 SkyObject *obj = new SkyObject(8, newRA, newDec, 1.0, avtUI->nameBox->text()); 0321 deleteList.append(obj); //this object will be deleted when window is destroyed 0322 processObject(obj); 0323 } 0324 } 0325 0326 //If the Ra and Dec boxes are filled, but the name field is empty, 0327 //move input focus to nameBox. If either coordinate box is empty, 0328 //move focus there 0329 if (avtUI->nameBox->text().isEmpty()) 0330 { 0331 avtUI->nameBox->QWidget::setFocus(); 0332 } 0333 if (avtUI->raBox->text().isEmpty()) 0334 { 0335 avtUI->raBox->QWidget::setFocus(); 0336 } 0337 else 0338 { 0339 if (avtUI->decBox->text().isEmpty()) 0340 avtUI->decBox->QWidget::setFocus(); 0341 } 0342 } 0343 0344 avtUI->View->update(); 0345 } 0346 0347 //Use find dialog to choose an object 0348 void AltVsTime::slotBrowseObject() 0349 { 0350 if (FindDialog::Instance()->exec() == QDialog::Accepted) 0351 { 0352 SkyObject *o = FindDialog::Instance()->targetObject(); 0353 processObject(o); 0354 } 0355 0356 avtUI->View->update(); 0357 avtUI->View->replot(); 0358 } 0359 0360 void AltVsTime::processObject(SkyObject *o, bool forceAdd) 0361 { 0362 if (!o) 0363 return; 0364 0365 KSNumbers *num = new KSNumbers(getDate().djd()); 0366 KSNumbers *oldNum = nullptr; 0367 0368 //If the object is in the solar system, recompute its position for the given epochLabel 0369 KStarsData *data = KStarsData::Instance(); 0370 if (o->isSolarSystem()) 0371 { 0372 oldNum = new KSNumbers(data->ut().djd()); 0373 o->updateCoords(num, true, geo->lat(), data->lst(), true); 0374 } 0375 0376 //precess coords to target epoch 0377 o->updateCoordsNow(num); 0378 0379 // vector used for computing the points needed for drawing the graph 0380 QVector<double> y(100), t(100); 0381 0382 //If this point is not in list already, add it to list 0383 bool found(false); 0384 foreach (SkyObject *p, pList) 0385 { 0386 if (o->ra().Degrees() == p->ra().Degrees() && o->dec().Degrees() == p->dec().Degrees()) 0387 { 0388 found = true; 0389 break; 0390 } 0391 } 0392 if (found && !forceAdd) 0393 { 0394 qCWarning(KSTARS) << "This point is already displayed; It will not be duplicated."; 0395 } 0396 else 0397 { 0398 pList.append(o); 0399 0400 // make sure existing curves are thin and red: 0401 0402 for (int i = 0; i < avtUI->View->graphCount(); i++) 0403 { 0404 if (avtUI->View->graph(i)->pen().color() == Qt::white) 0405 { 0406 avtUI->View->graph(i)->setPen(QPen(Qt::red, 2)); 0407 } 0408 } 0409 0410 // SET up the curve's name 0411 avtUI->View->addGraph()->setName(o->name()); 0412 0413 // compute the current graph: 0414 // time range: 24h 0415 0416 int offset = 3; 0417 for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++) 0418 { 0419 y[i] = findAltitude(o, h); 0420 if (y[i] > maxAlt) 0421 maxAlt = y[i]; 0422 if (y[i] < minAlt) 0423 minAlt = y[i]; 0424 t[i] = i * 900 + 43200; 0425 avtUI->View->graph(avtUI->View->graphCount() - 1)->addData(t[i], y[i]); 0426 } 0427 avtUI->View->graph(avtUI->View->graphCount() - 1)->setPen(QPen(Qt::white, 3)); 0428 0429 // Go into initial state: without Zoom/Pan 0430 avtUI->View->xAxis->setRange(43200, 129600); 0431 avtUI->View->xAxis2->setRange(61200, 147600); 0432 if (abs(minAlt) > maxAlt) 0433 maxAlt = abs(minAlt); 0434 else 0435 minAlt = -maxAlt; 0436 0437 avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset); 0438 0439 // Update background coordinates: 0440 background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper); 0441 background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower); 0442 0443 avtUI->View->replot(); 0444 0445 avtUI->PlotList->addItem(getObjectName(o)); 0446 avtUI->PlotList->setCurrentRow(avtUI->PlotList->count() - 1); 0447 avtUI->raBox->show(o->ra()); 0448 avtUI->decBox->show(o->dec()); 0449 avtUI->nameBox->setText(getObjectName(o)); 0450 0451 //Set epochName to epoch shown in date tab 0452 avtUI->epochName->setText(QString().setNum(getDate().epoch())); 0453 } 0454 //qCDebug() << "Currently, there are " << avtUI->View->graphCount() << " objects displayed."; 0455 0456 //restore original position 0457 if (o->isSolarSystem()) 0458 { 0459 o->updateCoords(oldNum, true, data->geo()->lat(), data->lst(), true); 0460 delete oldNum; 0461 } 0462 o->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0463 delete num; 0464 } 0465 0466 double AltVsTime::findAltitude(SkyPoint *p, double hour) 0467 { 0468 hour += 24.0 * DayOffset; 0469 0470 //getDate converts the user-entered local time to UT 0471 KStarsDateTime ut = getDate().addSecs(hour * 3600.0); 0472 0473 CachingDms LST = geo->GSTtoLST(ut.gst()); 0474 p->EquatorialToHorizontal(&LST, geo->lat()); 0475 return p->alt().Degrees(); 0476 } 0477 0478 void AltVsTime::slotHighlight(int row) 0479 { 0480 if (row < 0) 0481 return; 0482 0483 int rowIndex = 0; 0484 //highlight the curve of the selected object 0485 for (int i = 0; i < avtUI->View->graphCount(); i++) 0486 { 0487 if (i == row) 0488 rowIndex = row; 0489 else 0490 { 0491 avtUI->View->graph(i)->setPen(QPen(Qt::red, 2)); 0492 avtUI->View->graph(i)->setLayer("main"); 0493 } 0494 } 0495 avtUI->View->graph(rowIndex)->setPen(QPen(Qt::white, 3)); 0496 avtUI->View->graph(rowIndex)->setLayer("currentCurveLayer"); 0497 avtUI->View->update(); 0498 avtUI->View->replot(); 0499 0500 if (row >= 0 && row < pList.size()) 0501 { 0502 SkyObject *p = pList.at(row); 0503 avtUI->raBox->show(p->ra()); 0504 avtUI->decBox->show(p->dec()); 0505 avtUI->nameBox->setText(avtUI->PlotList->currentItem()->text()); 0506 } 0507 0508 SkyObject *selectedObject = KStarsData::Instance()->objectNamed(avtUI->nameBox->text()); 0509 const KStarsDateTime &ut = KStarsData::Instance()->ut(); 0510 if (selectedObject) 0511 { 0512 QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time 0513 if (rt.isValid() == false) 0514 { 0515 avtUI->riseButton->setEnabled(false); 0516 avtUI->setButton->setEnabled(false); 0517 } 0518 else 0519 { 0520 avtUI->riseButton->setEnabled(true); 0521 avtUI->setButton->setEnabled(true); 0522 } 0523 } 0524 } 0525 0526 void AltVsTime::onXRangeChanged(const QCPRange &range) 0527 { 0528 QCPRange aux = avtUI->View->xAxis2->range(); 0529 avtUI->View->xAxis->setRange(aux -= 18000); 0530 avtUI->View->xAxis2->setRange(range.bounded(61200, 147600)); 0531 // if ZOOM is detected then remove the gold lines that indicate current position: 0532 if (avtUI->View->xAxis->range().size() != 86400) 0533 { 0534 // Refresh the background: 0535 background->setScaled(false); 0536 background->setScaled(true, Qt::IgnoreAspectRatio); 0537 background->setPixmap(*gradient); 0538 0539 avtUI->View->update(); 0540 avtUI->View->replot(); 0541 } 0542 } 0543 0544 void AltVsTime::onYRangeChanged(const QCPRange &range) 0545 { 0546 int offset = 3; 0547 avtUI->View->yAxis->setRange(range.bounded(minAlt - offset, maxAlt + offset)); 0548 } 0549 0550 void AltVsTime::plotMousePress(QCPAbstractPlottable *abstractPlottable, int dataIndex, QMouseEvent *event) 0551 { 0552 //Do we need this? 0553 Q_UNUSED(dataIndex); 0554 0555 if (event->button() == Qt::RightButton) 0556 { 0557 QCPAbstractPlottable *plottable = abstractPlottable; 0558 if (plottable) 0559 { 0560 double x = avtUI->View->xAxis->pixelToCoord(event->localPos().x()); 0561 double y = avtUI->View->yAxis->pixelToCoord(event->localPos().y()); 0562 0563 QCPGraph *graph = qobject_cast<QCPGraph *>(plottable); 0564 0565 if (graph) 0566 { 0567 double yValue = y; 0568 double xValue = x; 0569 0570 // Compute time value: 0571 QTime localTime(0, 0, 0, 0); 0572 QTime localSiderealTime(5, 0, 0, 0); 0573 0574 localTime = localTime.addSecs(int(xValue)); 0575 localSiderealTime = localSiderealTime.addSecs(int(xValue)); 0576 0577 QToolTip::hideText(); 0578 QToolTip::showText(event->globalPos(), 0579 i18n("<table>" 0580 "<tr>" 0581 "<th colspan=\"2\">%1</th>" 0582 "</tr>" 0583 "<tr>" 0584 "<td>LST: </td>" 0585 "<td>%3</td>" 0586 "</tr>" 0587 "<tr>" 0588 "<td>LT: </td>" 0589 "<td>%2</td>" 0590 "</tr>" 0591 "<tr>" 0592 "<td>Altitude: </td>" 0593 "<td>%4</td>" 0594 "</tr>" 0595 "</table>", 0596 graph->name().isEmpty() ? "???" : graph->name(), 0597 localTime.toString(), 0598 localSiderealTime.toString(), 0599 QString::number(yValue, 'f', 2) + ' ' + QChar(176)), 0600 avtUI->View, avtUI->View->rect()); 0601 } 0602 } 0603 } 0604 } 0605 0606 //move input focus to the next logical widget 0607 void AltVsTime::slotAdvanceFocus() 0608 { 0609 if (sender()->objectName() == QString("nameBox")) 0610 avtUI->addButton->setFocus(); 0611 if (sender()->objectName() == QString("raBox")) 0612 avtUI->decBox->setFocus(); 0613 if (sender()->objectName() == QString("decbox")) 0614 avtUI->addButton->setFocus(); 0615 if (sender()->objectName() == QString("longBox")) 0616 avtUI->latBox->setFocus(); 0617 if (sender()->objectName() == QString("latBox")) 0618 avtUI->updateButton->setFocus(); 0619 } 0620 0621 void AltVsTime::slotClear() 0622 { 0623 pList.clear(); 0624 //Need to delete the pointers in deleteList 0625 while (!deleteList.isEmpty()) 0626 delete deleteList.takeFirst(); 0627 0628 avtUI->PlotList->clear(); 0629 avtUI->nameBox->clear(); 0630 avtUI->raBox->clear(); 0631 avtUI->decBox->clear(); 0632 avtUI->epochName->clear(); 0633 // remove all graphs from the plot: 0634 avtUI->View->clearGraphs(); 0635 // we remove all the dots (rise/set/transit) from the chart 0636 // without removing the background image 0637 int indexItem = 0, noItems = avtUI->View->itemCount(); 0638 QCPAbstractItem *item; 0639 QCPItemPixmap *background; 0640 // remove every item at a time: 0641 while (noItems > 1 && indexItem < noItems) 0642 { 0643 // test if the current item is the background: 0644 item = avtUI->View->item(indexItem); 0645 background = qobject_cast<QCPItemPixmap *>(item); 0646 if (background) 0647 indexItem++; 0648 else 0649 { 0650 // if not, then remove this item: 0651 avtUI->View->removeItem(indexItem); 0652 noItems--; 0653 } 0654 } 0655 // update & replot the chart: 0656 avtUI->View->update(); 0657 avtUI->View->replot(); 0658 } 0659 0660 void AltVsTime::slotClearBoxes() 0661 { 0662 avtUI->nameBox->clear(); 0663 avtUI->raBox->clear(); 0664 avtUI->decBox->clear(); 0665 avtUI->epochName->clear(); 0666 } 0667 0668 void AltVsTime::slotComputeAltitudeByTime() 0669 { 0670 if (avtUI->PlotList->currentRow() < 0) 0671 return; 0672 0673 SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow()); 0674 if (selectedObject == nullptr) 0675 { 0676 if (avtUI->PlotList->currentItem()) 0677 qCWarning(KSTARS) << "slotComputeAltitudeByTime: Unable to find" << avtUI->PlotList->currentItem()->text(); 0678 else 0679 { 0680 qCWarning(KSTARS) << "slotComputeAltitudeByTime: Unable to find item"; 0681 } 0682 return; 0683 } 0684 0685 // Get Local Date & Time 0686 KStarsDateTime lt = KStarsDateTime(avtUI->DateWidget->date(), avtUI->timeSpin->time(), Qt::LocalTime); 0687 // Convert to UT 0688 KStarsDateTime ut = geo->LTtoUT(lt); 0689 // Get LST from GST 0690 CachingDms LST = geo->GSTtoLST(ut.gst()); 0691 SkyObject *tempObject = selectedObject->clone(); 0692 // Update coords 0693 KSNumbers num(ut.djd()); 0694 tempObject->updateCoords(&num, true, geo->lat(), &LST); 0695 // Find Horizontal coordinates from LST & Latitude 0696 selectedObject->EquatorialToHorizontal(&LST, geo->lat()); 0697 0698 // Set altitude 0699 avtUI->altitudeBox->setText(selectedObject->altRefracted().toDMSString(true)); 0700 0701 delete (tempObject); 0702 } 0703 0704 void AltVsTime::slotMarkRiseTime() 0705 { 0706 if (avtUI->PlotList->currentRow() < 0) 0707 return; 0708 0709 const KStarsDateTime &ut = KStarsData::Instance()->ut(); 0710 SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow()); 0711 if (selectedObject == nullptr) 0712 { 0713 qCWarning(KSTARS) << "Mark Rise Time: Unable to find" << avtUI->PlotList->currentItem()->text(); 0714 return; 0715 } 0716 0717 QCPItemTracer *riseTimeTracer; 0718 // check if at least one graph exists in the plot 0719 if (avtUI->View->graphCount() > 0) 0720 { 0721 double time = 0; 0722 double hours, minutes; 0723 0724 QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow()); 0725 0726 QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time 0727 // mark the Rise time with a solid red circle 0728 if (rt.isValid() && selectedGraph) 0729 { 0730 hours = rt.hour(); 0731 minutes = rt.minute(); 0732 if (hours < 12) 0733 hours += 24; 0734 time = hours * 3600 + minutes * 60; 0735 riseTimeTracer = new QCPItemTracer(avtUI->View); 0736 riseTimeTracer->setLayer("markersLayer"); 0737 riseTimeTracer->setGraph(selectedGraph); 0738 riseTimeTracer->setInterpolating(true); 0739 riseTimeTracer->setStyle(QCPItemTracer::tsCircle); 0740 riseTimeTracer->setPen(QPen(Qt::red)); 0741 riseTimeTracer->setBrush(Qt::red); 0742 riseTimeTracer->setSize(10); 0743 riseTimeTracer->setGraphKey(time); 0744 riseTimeTracer->setVisible(true); 0745 avtUI->View->update(); 0746 avtUI->View->replot(); 0747 } 0748 } 0749 } 0750 0751 void AltVsTime::slotMarkSetTime() 0752 { 0753 if (avtUI->PlotList->currentRow() < 0) 0754 return; 0755 0756 const KStarsDateTime &ut = KStarsData::Instance()->ut(); 0757 SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow()); 0758 if (selectedObject == nullptr) 0759 { 0760 qCWarning(KSTARS) << "Mark Set Time: Unable to find" << avtUI->PlotList->currentItem()->text(); 0761 return; 0762 } 0763 QCPItemTracer *setTimeTracer; 0764 // check if at least one graph exists in the plot 0765 if (avtUI->View->graphCount() > 0) 0766 { 0767 double time = 0; 0768 double hours, minutes; 0769 0770 QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow()); 0771 0772 QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time 0773 //If set time is before rise time, use set time for tomorrow 0774 QTime st = selectedObject->riseSetTime(ut, geo, false); //false = use set time 0775 if (st < rt) 0776 st = selectedObject->riseSetTime(ut.addDays(1), geo, false); //false = use set time 0777 // mark the Set time with a solid blue circle 0778 if (rt.isValid()) 0779 { 0780 hours = st.hour(); 0781 minutes = st.minute(); 0782 if (hours < 12) 0783 hours += 24; 0784 time = hours * 3600 + minutes * 60; 0785 setTimeTracer = new QCPItemTracer(avtUI->View); 0786 setTimeTracer->setLayer("markersLayer"); 0787 setTimeTracer->setGraph(selectedGraph); 0788 setTimeTracer->setInterpolating(true); 0789 setTimeTracer->setStyle(QCPItemTracer::tsCircle); 0790 setTimeTracer->setPen(QPen(Qt::blue)); 0791 setTimeTracer->setBrush(Qt::blue); 0792 setTimeTracer->setSize(10); 0793 setTimeTracer->setGraphKey(time); 0794 setTimeTracer->setVisible(true); 0795 avtUI->View->update(); 0796 avtUI->View->replot(); 0797 } 0798 } 0799 } 0800 0801 void AltVsTime::slotMarkTransitTime() 0802 { 0803 if (avtUI->PlotList->currentRow() < 0) 0804 return; 0805 0806 const KStarsDateTime &ut = KStarsData::Instance()->ut(); 0807 SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow()); 0808 if (selectedObject == nullptr) 0809 { 0810 qCWarning(KSTARS) << "Mark Transit Time: Unable to find" << avtUI->PlotList->currentItem()->text(); 0811 return; 0812 } 0813 QCPItemTracer *transitTimeTracer; 0814 // check if at least one graph exists in the plot 0815 if (avtUI->View->graphCount() > 0) 0816 { 0817 double time = 0; 0818 double hours, minutes; 0819 0820 QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow()); 0821 0822 QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time 0823 //If transit time is before rise time, use transit time for tomorrow 0824 QTime tt = selectedObject->transitTime(ut, geo); 0825 0826 if (tt < rt) 0827 tt = selectedObject->transitTime(ut.addDays(1), geo); 0828 // mark the Transit time with a solid green circle 0829 hours = tt.hour(); 0830 minutes = tt.minute(); 0831 if (hours < 12) 0832 hours += 24; 0833 time = hours * 3600 + minutes * 60; 0834 transitTimeTracer = new QCPItemTracer(avtUI->View); 0835 transitTimeTracer->setLayer("markersLayer"); 0836 transitTimeTracer->setGraph(selectedGraph); 0837 transitTimeTracer->setInterpolating(true); 0838 transitTimeTracer->setStyle(QCPItemTracer::tsCircle); 0839 transitTimeTracer->setPen(QPen(Qt::green)); 0840 transitTimeTracer->setBrush(Qt::green); 0841 transitTimeTracer->setSize(10); 0842 transitTimeTracer->setGraphKey(time); 0843 transitTimeTracer->setVisible(true); 0844 avtUI->View->update(); 0845 avtUI->View->replot(); 0846 } 0847 } 0848 0849 void AltVsTime::computeSunRiseSetTimes() 0850 { 0851 //Determine the time of sunset and sunrise for the desired date and location 0852 //expressed as doubles, the fraction of a full day. 0853 0854 /* KSAlmanac ksal(getDate(), geo); */ 0855 0856 /* ... */ 0857 } 0858 0859 //FIXME 0860 /* 0861 void AltVsTime::mouseOverLine(QMouseEvent *event){ 0862 // Get the mouse position's coordinates relative to axes: 0863 double x = avtUI->View->xAxis->pixelToCoord(event->pos().x()); 0864 double y = avtUI->View->yAxis->pixelToCoord(event->pos().y()); 0865 // Save the actual values: 0866 double yValue = y; 0867 double xValue = x; 0868 // The offset used for the Y axis: top/bottom 0869 int offset = 3; 0870 // Compute the Y axis maximum value: 0871 int yAxisMaxValue = maxAlt + offset; 0872 // Compute the X axis minimum and maximum values: 0873 int xAxisMinValue = 43200; 0874 int xAxisMaxValue = 129600; 0875 // Ignore the upper and left margins: 0876 y = yAxisMaxValue - y; 0877 x -= xAxisMinValue; 0878 // We make a copy to gradient background in order to have one set of lines at a time: 0879 // Otherwise, the chart would have been covered by lines 0880 QPixmap copy = gradient->copy(gradient->rect()); 0881 // If ZOOM is not active, then draw the gold lines that indicate current mouse pisition: 0882 if(avtUI->View->xAxis->range().size() == 86400){ 0883 QPainter p; 0884 0885 p.begin(©); 0886 p.setPen( QPen( QBrush("gold"), 2, Qt::SolidLine ) ); 0887 0888 // Get the gradient background's width and height: 0889 int pW = gradient->rect().width(); 0890 int pH = gradient->rect().height(); 0891 0892 // Compute the real coordinates within the chart: 0893 y = (y*pH/2)/yAxisMaxValue; 0894 x = (x*pW)/(xAxisMaxValue-xAxisMinValue); 0895 0896 // Draw the horizontal line (altitude): 0897 p.drawLine( QLineF( 0.5, y, avtUI->View->rect().width()-0.5,y ) ); 0898 // Draw the altitude value: 0899 p.setPen( QPen( QBrush("gold"), 3, Qt::SolidLine ) ); 0900 p.drawText( 25, y + 15, QString::number(yValue,'f',2) + QChar(176) ); 0901 p.setPen( QPen( QBrush("gold"), 1, Qt::SolidLine ) ); 0902 // Draw the vertical line (time): 0903 p.drawLine( QLineF( x, 0.5, x, avtUI->View->rect().height()-0.5 ) ); 0904 // Compute and draw the time value: 0905 QTime localTime(0,0,0,0); 0906 localTime = localTime.addSecs(int(xValue)); 0907 p.save(); 0908 p.translate( x + 10, pH - 20 ); 0909 p.rotate(-90); 0910 p.setPen( QPen( QBrush("gold"), 3, Qt::SolidLine ) ); 0911 p.drawText( 5, 5, QLocale().toString( localTime, QLocale::ShortFormat ) ); // short format necessary to avoid false time-zone labeling 0912 p.restore(); 0913 p.end(); 0914 } 0915 // Refresh the background: 0916 background->setScaled(false); 0917 background->setScaled(true, Qt::IgnoreAspectRatio); 0918 background->setPixmap(copy); 0919 0920 avtUI->View->update(); 0921 avtUI->View->replot(); 0922 } 0923 */ 0924 0925 void AltVsTime::mouseOverLine(QMouseEvent *event) 0926 { 0927 double x = avtUI->View->xAxis->pixelToCoord(event->localPos().x()); 0928 double y = avtUI->View->yAxis->pixelToCoord(event->localPos().y()); 0929 QCPAbstractPlottable *abstractGraph = avtUI->View->plottableAt(event->pos(), false); 0930 QCPGraph *graph = qobject_cast<QCPGraph *>(abstractGraph); 0931 0932 if (x > avtUI->View->xAxis->range().lower && x < avtUI->View->xAxis->range().upper) 0933 if (y > avtUI->View->yAxis->range().lower && y < avtUI->View->yAxis->range().upper) 0934 { 0935 if (graph) 0936 { 0937 double yValue = y; 0938 double xValue = x; 0939 0940 // Compute time value: 0941 QTime localTime(0, 0, 0, 0); 0942 QTime localSiderealTime(5, 0, 0, 0); 0943 0944 localTime = localTime.addSecs(int(xValue)); 0945 localSiderealTime = localSiderealTime.addSecs(int(xValue)); 0946 0947 QToolTip::hideText(); 0948 QToolTip::showText(event->globalPos(), 0949 i18n("<table>" 0950 "<tr>" 0951 "<th colspan=\"2\">%1</th>" 0952 "</tr>" 0953 "<tr>" 0954 "<td>LST: </td>" 0955 "<td>%3</td>" 0956 "</tr>" 0957 "<tr>" 0958 "<td>LT: </td>" 0959 "<td>%2</td>" 0960 "</tr>" 0961 "<tr>" 0962 "<td>Altitude: </td>" 0963 "<td>%4</td>" 0964 "</tr>" 0965 "</table>", 0966 graph->name().isEmpty() ? "???" : graph->name(), 0967 localTime.toString(), localSiderealTime.toString(), 0968 QString::number(yValue, 'f', 2) + ' ' + QChar(176)), 0969 avtUI->View, avtUI->View->rect()); 0970 } 0971 else 0972 QToolTip::hideText(); 0973 } 0974 0975 avtUI->View->update(); 0976 avtUI->View->replot(); 0977 } 0978 0979 void AltVsTime::slotUpdateDateLoc() 0980 { 0981 KStarsData *data = KStarsData::Instance(); 0982 KStarsDateTime today = getDate(); 0983 KSNumbers *num = new KSNumbers(today.djd()); 0984 KSNumbers *oldNum = nullptr; 0985 CachingDms LST = geo->GSTtoLST(today.gst()); 0986 0987 //First determine time of sunset and sunrise 0988 computeSunRiseSetTimes(); 0989 // Determine dawn/dusk time and min/max sun elevation 0990 setDawnDusk(); 0991 0992 for (int i = 0; i < pList.count(); ++i) 0993 { 0994 SkyObject *o = pList.at(i); 0995 if (o) 0996 { 0997 //If the object is in the solar system, recompute its position for the given date 0998 if (o->isSolarSystem()) 0999 { 1000 oldNum = new KSNumbers(data->ut().djd()); 1001 o->updateCoords(num, true, geo->lat(), &LST, true); 1002 } 1003 1004 //precess coords to target epoch 1005 o->updateCoordsNow(num); 1006 1007 //update pList entry 1008 pList.replace(i, o); 1009 1010 // We are creating a new data set (time, altitude) for the new date: 1011 QVector<double> time_dataSet, altitude_dataSet; 1012 double point_altitudeValue, point_timeValue; 1013 // compute the new graph values: 1014 // time range: 24h 1015 int offset = 3; 1016 for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++) 1017 { 1018 point_altitudeValue = findAltitude(o, h); 1019 altitude_dataSet.push_back(point_altitudeValue); 1020 if (point_altitudeValue > maxAlt) 1021 maxAlt = point_altitudeValue; 1022 if (point_altitudeValue < minAlt) 1023 minAlt = point_altitudeValue; 1024 point_timeValue = i * 900 + 43200; 1025 time_dataSet.push_back(point_timeValue); 1026 } 1027 1028 // Replace graph data set: 1029 avtUI->View->graph(i)->setData(time_dataSet, altitude_dataSet); 1030 1031 // Go into initial state: without Zoom/Pan 1032 avtUI->View->xAxis->setRange(43200, 129600); 1033 avtUI->View->xAxis2->setRange(61200, 147600); 1034 1035 // Center the altitude axis in 0 value: 1036 if (abs(minAlt) > maxAlt) 1037 maxAlt = abs(minAlt); 1038 else 1039 minAlt = -maxAlt; 1040 avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset); 1041 1042 // Update background coordinates: 1043 background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper); 1044 background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower); 1045 1046 // Redraw the plot: 1047 avtUI->View->replot(); 1048 1049 //restore original position 1050 if (o->isSolarSystem()) 1051 { 1052 o->updateCoords(oldNum, true, data->geo()->lat(), data->lst()); 1053 delete oldNum; 1054 oldNum = nullptr; 1055 } 1056 o->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 1057 } 1058 else //assume unfound object is a custom object 1059 { 1060 pList.at(i)->updateCoordsNow(num); //precess to desired epoch 1061 1062 // We are creating a new data set (time, altitude) for the new date: 1063 QVector<double> time_dataSet, altitude_dataSet; 1064 double point_altitudeValue, point_timeValue; 1065 // compute the new graph values: 1066 // time range: 24h 1067 int offset = 3; 1068 for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++) 1069 { 1070 point_altitudeValue = findAltitude(pList.at(i), h); 1071 altitude_dataSet.push_back(point_altitudeValue); 1072 if (point_altitudeValue > maxAlt) 1073 maxAlt = point_altitudeValue; 1074 if (point_altitudeValue < minAlt) 1075 minAlt = point_altitudeValue; 1076 point_timeValue = i * 900 + 43200; 1077 time_dataSet.push_back(point_timeValue); 1078 } 1079 1080 // Replace graph data set: 1081 avtUI->View->graph(i)->setData(time_dataSet, altitude_dataSet); 1082 1083 // Go into initial state: without Zoom/Pan 1084 avtUI->View->xAxis->setRange(43200, 129600); 1085 avtUI->View->xAxis2->setRange(61200, 147600); 1086 1087 // Center the altitude axis in 0 value: 1088 if (abs(minAlt) > maxAlt) 1089 maxAlt = abs(minAlt); 1090 else 1091 minAlt = -maxAlt; 1092 avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset); 1093 1094 // Update background coordinates: 1095 background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper); 1096 background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower); 1097 1098 // Redraw the plot: 1099 avtUI->View->replot(); 1100 } 1101 } 1102 1103 if (getDate().time().hour() > 12) 1104 DayOffset = 1; 1105 else 1106 DayOffset = 0; 1107 1108 setLSTLimits(); 1109 slotHighlight(avtUI->PlotList->currentRow()); 1110 avtUI->View->update(); 1111 1112 delete num; 1113 } 1114 1115 void AltVsTime::slotChooseCity() 1116 { 1117 QPointer<LocationDialog> ld = new LocationDialog(this); 1118 if (ld->exec() == QDialog::Accepted) 1119 { 1120 GeoLocation *newGeo = ld->selectedCity(); 1121 if (newGeo) 1122 { 1123 geo = newGeo; 1124 avtUI->latBox->show(geo->lat()); 1125 avtUI->longBox->show(geo->lng()); 1126 } 1127 } 1128 delete ld; 1129 } 1130 1131 // FIXME: should we remove this method? 1132 void AltVsTime::setLSTLimits() 1133 { 1134 /* 1135 //UT at noon on target date 1136 KStarsDateTime ut = getDate().addSecs(((double)DayOffset + 0.5)*86400.); 1137 1138 dms lst = geo->GSTtoLST(ut.gst()); 1139 double h1 = lst.Hours(); 1140 if(h1 > 12.0) 1141 h1 -= 24.0; 1142 double h2 = h1 + 24.0; 1143 avtUI->View->setSecondaryLimits(h1, h2, -90.0, 90.0); 1144 */ 1145 } 1146 1147 void AltVsTime::showCurrentDate() 1148 { 1149 KStarsDateTime dt = KStarsDateTime::currentDateTime(); 1150 if (dt.time() > QTime(12, 0, 0)) 1151 dt = dt.addDays(1); 1152 avtUI->DateWidget->setDate(dt.date()); 1153 } 1154 1155 void AltVsTime::drawGradient() 1156 { 1157 // Things needed for Gradient: 1158 KStarsDateTime dtt = KStarsDateTime::currentDateTime(); 1159 GeoLocation *geoLoc = KStarsData::Instance()->geo(); 1160 QDateTime midnight = QDateTime(dtt.date(), QTime()); 1161 KStarsDateTime const utt = geoLoc->LTtoUT(KStarsDateTime(midnight)); 1162 1163 // Variables needed for Gradient: 1164 double SunRise, SunSet, Dawn, Dusk, SunMinAlt, SunMaxAlt; 1165 double MoonRise, MoonSet, MoonIllum; 1166 1167 KSAlmanac ksal(utt, geoLoc); 1168 1169 // Get the values: 1170 SunRise = ksal.getSunRise(); 1171 SunSet = ksal.getSunSet(); 1172 SunMaxAlt = ksal.getSunMaxAlt(); 1173 SunMinAlt = ksal.getSunMinAlt(); 1174 MoonRise = ksal.getMoonRise(); 1175 MoonSet = ksal.getMoonSet(); 1176 MoonIllum = ksal.getMoonIllum(); 1177 Dawn = ksal.getDawnAstronomicalTwilight(); 1178 Dusk = ksal.getDuskAstronomicalTwilight(); 1179 1180 gradient = new QPixmap(avtUI->View->rect().width(), avtUI->View->rect().height()); 1181 1182 QPainter p; 1183 1184 p.begin(gradient); 1185 KPlotWidget *kPW = new KPlotWidget; 1186 p.setRenderHint(QPainter::Antialiasing, kPW->antialiasing()); 1187 p.fillRect(gradient->rect(), kPW->backgroundColor()); 1188 1189 p.setClipRect(gradient->rect()); 1190 p.setClipping(true); 1191 1192 int pW = gradient->rect().width(); 1193 int pH = gradient->rect().height(); 1194 1195 QColor SkyColor(0, 100, 200); 1196 // TODO 1197 // if( Options::darkAppColors() ) 1198 // SkyColor = QColor( 200, 0, 0 ); // use something red, visible through a red filter 1199 1200 // Draw gradient representing lunar interference in the sky 1201 if (MoonIllum > 0.01) // do this only if Moon illumination is reasonable so it's important 1202 { 1203 int moonrise = int(pW * (0.5 + MoonRise)); 1204 int moonset = int(pW * (MoonSet - 0.5)); 1205 if (moonset < 0) 1206 moonset += pW; 1207 if (moonrise > pW) 1208 moonrise -= pW; 1209 int moonalpha = int(10 + MoonIllum * 130); 1210 int fadewidth = 1211 pW * 1212 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. 1213 QColor MoonColor(255, 255, 255, moonalpha); 1214 1215 if (moonset < moonrise) 1216 { 1217 QLinearGradient grad = 1218 QLinearGradient(QPointF(moonset - fadewidth, 0.0), QPointF(moonset + fadewidth, 0.0)); 1219 grad.setColorAt(0, MoonColor); 1220 grad.setColorAt(1, Qt::transparent); 1221 p.fillRect(QRectF(0.0, 0.0, moonset + fadewidth, pH), 1222 grad); // gradient should be padded until moonset - fadewidth (see QLinearGradient docs) 1223 grad.setStart(QPointF(moonrise + fadewidth, 0.0)); 1224 grad.setFinalStop(QPointF(moonrise - fadewidth, 0.0)); 1225 p.fillRect(QRectF(moonrise - fadewidth, 0.0, pW - moonrise + fadewidth, pH), grad); 1226 } 1227 else 1228 { 1229 qreal opacity = p.opacity(); 1230 p.setOpacity(opacity / 4); 1231 p.fillRect(QRectF(moonrise + fadewidth, 0.0, moonset - moonrise - 2 * fadewidth, pH), MoonColor); 1232 QLinearGradient grad = 1233 QLinearGradient(QPointF(moonrise + fadewidth, 0.0), QPointF(moonrise - fadewidth, 0.0)); 1234 grad.setColorAt(0, MoonColor); 1235 grad.setColorAt(1, Qt::transparent); 1236 p.fillRect(QRectF(0.0, 0.0, moonrise + fadewidth, pH), grad); 1237 grad.setStart(QPointF(moonset - fadewidth, 0.0)); 1238 grad.setFinalStop(QPointF(moonset + fadewidth, 0.0)); 1239 p.fillRect(QRectF(moonset - fadewidth, 0.0, pW - moonset, pH), grad); 1240 p.setOpacity(opacity); 1241 } 1242 } 1243 1244 //draw daytime sky if the Sun rises for the current date/location 1245 if (SunMaxAlt > -18.0) 1246 { 1247 //Display centered on midnight, so need to modulate dawn/dusk by 0.5 1248 int rise = int(pW * (0.5 + SunRise)); 1249 int set = int(pW * (SunSet - 0.5)); 1250 int da = int(pW * (0.5 + Dawn)); 1251 int du = int(pW * (Dusk - 0.5)); 1252 1253 if (SunMinAlt > 0.0) 1254 { 1255 // The sun never set and the sky is always blue 1256 p.fillRect(rect(), SkyColor); 1257 } 1258 else if (SunMaxAlt < 0.0 && SunMinAlt < -18.0) 1259 { 1260 // The sun never rise but the sky is not completely dark 1261 QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(du, 0.0)); 1262 1263 QColor gradStartColor = SkyColor; 1264 gradStartColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255); 1265 1266 grad.setColorAt(0, gradStartColor); 1267 grad.setColorAt(1, Qt::transparent); 1268 p.fillRect(QRectF(0.0, 0.0, du, pH), grad); 1269 grad.setStart(QPointF(pW, 0.0)); 1270 grad.setFinalStop(QPointF(da, 0.0)); 1271 p.fillRect(QRectF(da, 0.0, pW, pH), grad); 1272 } 1273 else if (SunMaxAlt < 0.0 && SunMinAlt > -18.0) 1274 { 1275 // The sun never rise but the sky is NEVER completely dark 1276 QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(pW, 0.0)); 1277 1278 QColor gradStartEndColor = SkyColor; 1279 gradStartEndColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255); 1280 QColor gradMidColor = SkyColor; 1281 gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255); 1282 1283 grad.setColorAt(0, gradStartEndColor); 1284 grad.setColorAt(0.5, gradMidColor); 1285 grad.setColorAt(1, gradStartEndColor); 1286 p.fillRect(QRectF(0.0, 0.0, pW, pH), grad); 1287 } 1288 else if (Dawn < 0.0) 1289 { 1290 // The sun sets and rises but the sky is never completely dark 1291 p.fillRect(0, 0, set, int(0.5 * pH), SkyColor); 1292 p.fillRect(rise, 0, pW, int(0.5 * pH), SkyColor); 1293 1294 QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(rise, 0.0)); 1295 1296 QColor gradMidColor = SkyColor; 1297 gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255); 1298 1299 grad.setColorAt(0, SkyColor); 1300 grad.setColorAt(0.5, gradMidColor); 1301 grad.setColorAt(1, SkyColor); 1302 p.fillRect(QRectF(set, 0.0, rise - set, pH), grad); 1303 } 1304 else 1305 { 1306 p.fillRect(0, 0, set, pH, SkyColor); 1307 p.fillRect(rise, 0, pW, pH, SkyColor); 1308 1309 QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(du, 0.0)); 1310 grad.setColorAt(0, SkyColor); 1311 grad.setColorAt( 1312 1, 1313 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) 1314 p.fillRect(QRectF(set, 0.0, du - set, pH), grad); 1315 1316 grad.setStart(QPointF(rise, 0.0)); 1317 grad.setFinalStop(QPointF(da, 0.0)); 1318 p.fillRect(QRectF(da, 0.0, rise - da, pH), grad); 1319 } 1320 } 1321 1322 p.fillRect(0, int(0.5 * pH), pW, int(0.5 * pH), KStarsData::Instance()->colorScheme()->colorNamed("HorzColor")); 1323 1324 p.setClipping(false); 1325 1326 // Add vertical line indicating "now" 1327 // Convert the current system clock time to the TZ corresponding to geo 1328 QTime t = geoLoc->UTtoLT(KStarsDateTime::currentDateTimeUtc()).time(); 1329 double x = 12.0 + t.hour() + t.minute() / 60.0 + t.second() / 3600.0; 1330 1331 while (x > 24.0) 1332 x -= 24.0; 1333 1334 // Convert to screen pixel coords 1335 int ix = int(x * pW / 24.0); 1336 1337 p.setPen(QPen(QBrush("white"), 2.0, Qt::DotLine)); 1338 p.drawLine(ix, 0, ix, pH); 1339 1340 QFont largeFont = p.font(); 1341 1342 largeFont.setPointSize(largeFont.pointSize() + 1); 1343 // Label this vertical line with the current time 1344 p.save(); 1345 p.setFont(largeFont); 1346 p.translate(ix + 15, pH - 20); 1347 p.rotate(-90); 1348 // Short format necessary to avoid false time-zone labeling 1349 p.drawText(0, 0, QLocale().toString(t, QLocale::ShortFormat)); 1350 p.restore(); 1351 p.end(); 1352 } 1353 1354 KStarsDateTime AltVsTime::getDate() 1355 { 1356 //convert midnight local time to UT: 1357 QDateTime lt(avtUI->DateWidget->date(), QTime()); 1358 return geo->LTtoUT(KStarsDateTime(lt)); 1359 } 1360 1361 double AltVsTime::getEpoch(const QString &eName) 1362 { 1363 //If Epoch field not a double, assume J2000 1364 bool ok; 1365 double epoch = eName.toDouble(&ok); 1366 if (!ok) 1367 { 1368 qCWarning(KSTARS) << "Invalid Epoch. Assuming 2000.0."; 1369 return 2000.0; 1370 } 1371 return epoch; 1372 } 1373 1374 void AltVsTime::setDawnDusk() 1375 { 1376 /* TODO */ 1377 1378 /* 1379 KSAlmanac almanac(getDate(), geo); 1380 1381 avtUI->View->setDawnDuskTimes(almanac.getDawnAstronomicalTwilight(), almanac.getDuskAstronomicalTwilight()); 1382 avtUI->View->setMinMaxSunAlt(almanac.getSunMinAlt(), almanac.getSunMaxAlt()); 1383 */ 1384 1385 /* ... */ 1386 } 1387 1388 void AltVsTime::slotPrint() 1389 { 1390 QPainter p; // Our painter object 1391 QPrinter printer; // Our printer object 1392 QString str_legend; // Text legend 1393 int text_height = 200; // Height of legend text zone in points 1394 QSize plot_size; // Initial plot widget size 1395 QFont plot_font; // Initial plot widget font 1396 int plot_font_size; // Initial plot widget font size 1397 1398 // Set printer resolution to 300 dpi 1399 printer.setResolution(300); 1400 1401 // Open print dialog 1402 //QPointer<QPrintDialog> dialog( KdePrint::createPrintDialog( &printer, this ) ); 1403 //QPointer<QPrintDialog> dialog( &printer, this ); 1404 QPrintDialog dialog(&printer, this); 1405 dialog.setWindowTitle(i18nc("@title:window", "Print elevation vs time plot")); 1406 if (dialog.exec() == QDialog::Accepted) 1407 { 1408 // Change mouse cursor 1409 QApplication::setOverrideCursor(Qt::WaitCursor); 1410 1411 // Save plot widget font 1412 plot_font = avtUI->View->font(); 1413 // Save plot widget font size 1414 plot_font_size = plot_font.pointSize(); 1415 // Save calendar widget size 1416 plot_size = avtUI->View->size(); 1417 1418 // Set text legend 1419 str_legend = i18n("Elevation vs. Time Plot"); 1420 str_legend += '\n'; 1421 str_legend += geo->fullName(); 1422 str_legend += " - "; 1423 str_legend += avtUI->DateWidget->date().toString("dd/MM/yyyy"); 1424 1425 // Create a rectangle for legend text zone 1426 QRect text_rect(0, 0, printer.width(), text_height); 1427 1428 // Increase plot widget font size so it looks good in 300 dpi 1429 plot_font.setPointSize(plot_font_size * 2.5); 1430 avtUI->View->setFont(plot_font); 1431 // Increase plot widget size to fit the entire page 1432 avtUI->View->resize(printer.width(), printer.height() - text_height); 1433 1434 // Create a pixmap and render plot widget into it 1435 QPixmap pixmap(avtUI->View->size()); 1436 avtUI->View->render(&pixmap); 1437 1438 // Begin painting on printer 1439 p.begin(&printer); 1440 // Draw legend 1441 p.drawText(text_rect, Qt::AlignLeft, str_legend); 1442 // Draw plot widget 1443 p.drawPixmap(0, text_height, pixmap); 1444 // Ending painting 1445 p.end(); 1446 1447 // Restore plot widget font size 1448 plot_font.setPointSize(plot_font_size); 1449 avtUI->View->setFont(plot_font); 1450 // Restore calendar widget size 1451 avtUI->View->resize(plot_size); 1452 1453 // Restore mouse cursor 1454 QApplication::restoreOverrideCursor(); 1455 } 1456 //delete dialog; 1457 } 1458 1459 QString AltVsTime::getObjectName(const SkyObject *o, bool translated) 1460 { 1461 QString finalObjectName; 1462 if (o->name() == "star") 1463 { 1464 StarObject *s = (StarObject *)o; 1465 1466 // JM: Enable HD Index stars to be added to the observing list. 1467 if (s->getHDIndex() != 0) 1468 finalObjectName = QString("HD %1").arg(QString::number(s->getHDIndex())); 1469 } 1470 else 1471 finalObjectName = translated ? o->translatedName() : o->name(); 1472 1473 return finalObjectName; 1474 }