Kstars

skycalendar.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Jason Harris <kstars@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "skycalendar.h"
8
9#include "geolocation.h"
10#include "ksplanetbase.h"
11#include "kstarsdata.h"
12#include "dialogs/locationdialog.h"
13#include "skycomponents/skymapcomposite.h"
14
15#include <KPlotObject>
16
17#include <QPainter>
18#include <QPixmap>
19#include <QPrintDialog>
20#include <QPrinter>
21#include <QPushButton>
22#include <QScreen>
23#include <QtConcurrent>
24
25SkyCalendarUI::SkyCalendarUI(QWidget *parent) : QFrame(parent)
26{
27 setupUi(this);
28}
29
30SkyCalendar::SkyCalendar(QWidget *parent) : QDialog(parent)
31{
32#ifdef Q_OS_OSX
33 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
34#endif
35
36 scUI = new SkyCalendarUI(this);
37
38 QVBoxLayout *mainLayout = new QVBoxLayout;
39
40 mainLayout->addWidget(scUI);
41 setLayout(mainLayout);
42
43 geo = KStarsData::Instance()->geo();
44
45 setWindowTitle(i18nc("@title:window", "Sky Calendar"));
46
48 mainLayout->addWidget(buttonBox);
49 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
50
51 QPushButton *printB = new QPushButton(QIcon::fromTheme("document-print"), i18n("&Print..."));
52 printB->setToolTip(i18n("Print the Sky Calendar"));
53 buttonBox->addButton(printB, QDialogButtonBox::ActionRole);
54 connect(printB, SIGNAL(clicked()), this, SLOT(slotPrint()));
55
56 setModal(false);
57
58 //Adjust minimum size for small screens:
59 if (QGuiApplication::primaryScreen()->geometry().height() <= scUI->CalendarView->height())
60 {
61 scUI->CalendarView->setMinimumSize(400, 600);
62 }
63
64 scUI->CalendarView->setShowGrid(false);
65 scUI->Year->setValue(KStarsData::Instance()->lt().date().year());
66
67 scUI->LocationButton->setText(geo->fullName());
68
69 scUI->CalendarView->setHorizon();
70
71 plotButtonText = scUI->CreateButton->text();
72 connect(scUI->CreateButton, &QPushButton::clicked, this, [this]()
73 {
74 scUI->CreateButton->setText(i18n("Please Wait") + "...");
75 slotFillCalendar();
76 });
77
78 connect(scUI->LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation()));
79}
80
81int SkyCalendar::year()
82{
83 return scUI->Year->value();
84}
85
86void SkyCalendar::slotFillCalendar()
87{
88 scUI->CreateButton->setEnabled(false);
89
90 scUI->CalendarView->resetPlot();
91 scUI->CalendarView->setHorizon();
92
93 if (scUI->checkBox_Mercury->isChecked())
94 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::MERCURY);
95 if (scUI->checkBox_Venus->isChecked())
96 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::VENUS);
97 if (scUI->checkBox_Mars->isChecked())
98 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::MARS);
99 if (scUI->checkBox_Jupiter->isChecked())
100 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::JUPITER);
101 if (scUI->checkBox_Saturn->isChecked())
102 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::SATURN);
103 if (scUI->checkBox_Uranus->isChecked())
104 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::URANUS);
105 if (scUI->checkBox_Neptune->isChecked())
106 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::NEPTUNE);
107
108 scUI->CreateButton->setText(i18n("Plot Planetary Almanac"));
109 scUI->CreateButton->setEnabled(true);
110}
111
112#if 0
113void SkyCalendar::slotCalculating()
114{
115 while (calculating)
116 {
117 //QMutexLocker locker(&calculationMutex);
118
119 if (!calculating)
120 continue;
121
122 scUI->CreateButton->setText(i18n("Please Wait") + ". ");
123 scUI->CreateButton->repaint();
124 QThread::msleep(200);
125 scUI->CreateButton->setText(i18n("Please Wait") + ".. ");
126 scUI->CreateButton->repaint();
127 QThread::msleep(200);
128
129 scUI->CreateButton->repaint();
130 QThread::msleep(200);
131 }
132 scUI->CreateButton->setText(plotButtonText);
133}
134#endif
135
136// FIXME: For the time being, adjust with dirty, cluttering labels that don't align to the line
137/*
138void SkyCalendar::drawEventLabel( float x1, float y1, float x2, float y2, QString LabelText ) {
139 QFont origFont = p->font();
140 p->setFont( QFont( "Bitstream Vera", 10 ) );
141
142 int textFlags = Qt::AlignCenter; // TODO: See if Qt::SingleLine flag works better
143 QFontMetricsF fm( p->font(), p->device() );
144
145 QRectF LabelRect = fm.boundingRect( QRectF(0,0,1,1), textFlags, LabelText );
146 QPointF LabelPoint = scUI->CalendarView->mapToWidget( QPointF( x, y ) );
147
148 float LabelAngle = atan2( y2 - y1, x2 - x1 )/dms::DegToRad;
149
150 p->save();
151 p->translate( LabelPoint );
152 p->rotate( LabelAngle );
153 p->drawText( LabelRect, textFlags, LabelText );
154 p->restore();
155
156 p->setFont( origFont );
157}
158*/
159
160void SkyCalendar::addPlanetEvents(int nPlanet)
161{
162 KSPlanetBase *ksp = KStarsData::Instance()->skyComposite()->planet(nPlanet);
163 QColor pColor = ksp->color();
164 //QVector<QPointF> vRise, vSet, vTransit;
165 std::vector<QPointF> vRise, vSet, vTransit;
166
167 for (KStarsDateTime kdt(QDate(year(), 1, 1), QTime(12, 0, 0)); kdt.date().year() == year();
168 kdt = kdt.addDays(scUI->spinBox_Interval->value()))
169 {
170 float rTime, sTime, tTime;
171
172 //Compute rise/set/transit times. If they occur before noon,
173 //recompute for the following day
174 QTime tmp_rTime = ksp->riseSetTime(kdt, geo, true, true); //rise time, exact
175 QTime tmp_sTime = ksp->riseSetTime(kdt, geo, false, true); //set time, exact
176 QTime tmp_tTime = ksp->transitTime(kdt, geo);
177 QTime midday(12, 0, 0);
178
179 // NOTE: riseSetTime should be fix now, this test is no longer necessary
180 if (tmp_rTime == tmp_sTime)
181 {
182 tmp_rTime = QTime();
183 tmp_sTime = QTime();
184 }
185
186 if (tmp_rTime.isValid() && tmp_sTime.isValid())
187 {
188 rTime = tmp_rTime.secsTo(midday) * 24.0 / 86400.0;
189 sTime = tmp_sTime.secsTo(midday) * 24.0 / 86400.0;
190
191 if (tmp_rTime <= midday)
192 rTime = 12.0 - rTime;
193 else
194 rTime = -12.0 - rTime;
195
196 if (tmp_sTime <= midday)
197 sTime = 12.0 - sTime;
198 else
199 sTime = -12.0 - sTime;
200 }
201 else
202 {
203 if (ksp->transitAltitude(kdt, geo).degree() > 0)
204 {
205 rTime = -24.0;
206 sTime = 24.0;
207 }
208 else
209 {
210 rTime = 24.0;
211 sTime = -24.0;
212 }
213 }
214
215 tTime = tmp_tTime.secsTo(midday) * 24.0 / 86400.0;
216 if (tmp_tTime <= midday)
217 tTime = 12.0 - tTime;
218 else
219 tTime = -12.0 - tTime;
220
221 float dy = kdt.date().daysInYear() - kdt.date().dayOfYear();
222 vRise.push_back(QPointF(rTime, dy));
223 vSet.push_back(QPointF(sTime, dy));
224 vTransit.push_back(QPointF(tTime, dy));
225 }
226
227 //Now, find continuous segments in each QVector and add each segment
228 //as a separate KPlotObject
229
230 KPlotObject *oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
231 KPlotObject *oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
232 KPlotObject *oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
233
234 float defaultSetTime = -10.0;
235 float defaultRiseTime = 8.0;
237 bool initialRise = true;
238 bool initialSet = true;
239 bool initialTransit = true;
240 bool extraCheckRise = true;
241 bool extraCheckSet = true;
242 bool extraCheckTransit = true;
243 bool needRiseLabel = false;
244 bool needSetLabel = false;
245 bool needTransertLabel = false;
246
247 float maxRiseTime = 0.0;
248 for (auto &item : vRise)
249 {
250 if (item.x() >= maxRiseTime)
251 maxRiseTime = item.x();
252 }
253 maxRiseTime = qFloor(maxRiseTime) - 1.0;
254
255 for (uint32_t i = 0; i < vRise.size(); ++i)
256 {
257 if (initialRise && (vRise.at(i).x() > defaultSetTime && vRise.at(i).x() < defaultRiseTime))
258 {
259 needRiseLabel = true;
260 initialRise = false;
261 }
262 else if (vRise.at(i).x() < defaultSetTime || vRise.at(i).x() > defaultRiseTime)
263 {
264 initialRise = true;
265 needRiseLabel = false;
266 }
267 else
268 needRiseLabel = false;
269
270 if (extraCheckRise && vRise.at(i).x() > defaultRiseTime && vRise.at(i).x() < maxRiseTime)
271 {
272 needRiseLabel = true;
273 extraCheckRise = false;
274 }
275
276 if (initialSet && (vSet.at(i).x() > defaultSetTime && vSet.at(i).x() < defaultRiseTime))
277 {
278 needSetLabel = true;
279 initialSet = false;
280 }
281 else if (vSet.at(i).x() < defaultSetTime || vSet.at(i).x() > defaultRiseTime)
282 {
283 initialSet = true;
284 needSetLabel = false;
285 }
286 else
287 needSetLabel = false;
288
289 if (extraCheckSet && vSet.at(i).x() > defaultRiseTime && vSet.at(i).x() < maxRiseTime)
290 {
291 needSetLabel = true;
292 extraCheckSet = false;
293 }
294
295 if (initialTransit && (vTransit.at(i).x() > defaultSetTime && vTransit.at(i).x() < defaultRiseTime))
296 {
297 needTransertLabel = true;
298 initialTransit = false;
299 }
300 else if (vTransit.at(i).x() < defaultSetTime || vTransit.at(i).x() > defaultRiseTime)
301 {
302 initialTransit = true;
303 needTransertLabel = false;
304 ;
305 }
306 else
307 needTransertLabel = false;
308
309 if (extraCheckTransit && vTransit.at(i).x() > defaultRiseTime && vTransit.at(i).x() < maxRiseTime)
310 {
311 needTransertLabel = true;
312 extraCheckTransit = false;
313 }
314
315 if (vRise.at(i).x() > -23.0 && vRise.at(i).x() < 23.0)
316 {
317 if (i > 0 && fabs(vRise.at(i).x() - vRise.at(i - 1).x()) > 6.0)
318 {
319 scUI->CalendarView->addPlotObject(oRise);
320 oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
321 extraCheckRise = true;
322 }
323
324 if (needRiseLabel)
325 label = i18nc("A planet rises from the horizon", "%1 rises", ksp->name());
326 else
327 label = QString();
328 // Add the current point to KPlotObject
329 oRise->addPoint(vRise.at(i), label);
330 }
331 else
332 {
333 scUI->CalendarView->addPlotObject(oRise);
334 oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
335 }
336
337 if (vSet.at(i).x() > -23.0 && vSet.at(i).x() < 23.0)
338 {
339 if (i > 0 && fabs(vSet.at(i).x() - vSet.at(i - 1).x()) > 6.0)
340 {
341 scUI->CalendarView->addPlotObject(oSet);
342 oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
343 extraCheckSet = true;
344 }
345
346 if (needSetLabel)
347 label = i18nc("A planet sets from the horizon", "%1 sets", ksp->name());
348 else
349 label = QString();
350
351 oSet->addPoint(vSet.at(i), label);
352 }
353 else
354 {
355 scUI->CalendarView->addPlotObject(oSet);
356 oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
357 }
358
359 if (vTransit.at(i).x() > -23.0 && vTransit.at(i).x() < 23.0)
360 {
361 if (i > 0 && fabs(vTransit.at(i).x() - vTransit.at(i - 1).x()) > 6.0)
362 {
363 scUI->CalendarView->addPlotObject(oTransit);
364 oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
365 extraCheckTransit = true;
366 }
367
368 if (needTransertLabel)
369 label = i18nc("A planet transits across the meridian", "%1 transits", ksp->name());
370 else
371 label = QString();
372
373 oTransit->addPoint(vTransit.at(i), label);
374 }
375 else
376 {
377 scUI->CalendarView->addPlotObject(oTransit);
378 oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
379 }
380 }
381
382 // Add the last points
383 scUI->CalendarView->addPlotObject(oRise);
384 scUI->CalendarView->addPlotObject(oSet);
385 scUI->CalendarView->addPlotObject(oTransit);
386}
387
388void SkyCalendar::slotPrint()
389{
390 QPainter p; // Our painter object
391 QPrinter printer; // Our printer object
392 QString str_legend; // Text legend
393 QString str_year; // Calendar's year
394 int text_height = 200; // Height of legend text zone in points
395 QSize calendar_size; // Initial calendar widget size
396 QFont calendar_font; // Initial calendar font
397 int calendar_font_size; // Initial calendar font size
398
399 // Set printer resolution to 300 dpi
400 printer.setResolution(300);
401
402 // Open print dialog
403 //NOTE Changed from pointer to statically allocated object, what effect will it have?
404 //QPointer<QPrintDialog> dialog( KdePrint::createPrintDialog( &printer, this ) );
405 QPrintDialog dialog(&printer, this);
406 dialog.setWindowTitle(i18nc("@title:window", "Print sky calendar"));
407 if (dialog.exec() == QDialog::Accepted)
408 {
409 // Change mouse cursor
411
412 // Save calendar widget font
413 calendar_font = scUI->CalendarView->font();
414 // Save calendar widget font size
415 calendar_font_size = calendar_font.pointSize();
416 // Save calendar widget size
417 calendar_size = scUI->CalendarView->size();
418
419 // Set text legend
420 str_year.setNum(year());
421 str_legend = i18n("Sky Calendar");
422 str_legend += '\n';
423 str_legend += geo->fullName();
424 str_legend += " - ";
425 str_legend += str_year;
426
427 // Create a rectangle for legend text zone
428 QRect text_rect(0, 0, printer.width(), text_height);
429
430 // Increase calendar widget font size so it looks good in 300 dpi
431 calendar_font.setPointSize(calendar_font_size * 3);
432 scUI->CalendarView->setFont(calendar_font);
433 // Increase calendar widget size to fit the entire page
434 scUI->CalendarView->resize(printer.width(), printer.height() - text_height);
435
436 // Create a pixmap and render calendar widget into it
437 QPixmap pixmap(scUI->CalendarView->size());
438 scUI->CalendarView->render(&pixmap);
439
440 // Begin painting on printer
441 p.begin(&printer);
442 // Draw legend
443 p.drawText(text_rect, Qt::AlignLeft, str_legend);
444 // Draw calendar
445 p.drawPixmap(0, text_height, pixmap);
446 // Ending painting
447 p.end();
448
449 // Restore calendar widget font size
450 calendar_font.setPointSize(calendar_font_size);
451 scUI->CalendarView->setFont(calendar_font);
452 // Restore calendar widget size
453 scUI->CalendarView->resize(calendar_size);
454
455 // Restore mouse cursor
457 }
458 //delete dialog;
459}
460
461void SkyCalendar::slotLocation()
462{
464 if (ld->exec() == QDialog::Accepted)
465 {
466 GeoLocation *newGeo = ld->selectedCity();
467 if (newGeo)
468 {
469 geo = newGeo;
470 scUI->LocationButton->setText(geo->fullName());
471 }
472 }
473 delete ld;
474
475 scUI->CalendarView->setHorizon();
476 slotFillCalendar();
477}
478
479GeoLocation *SkyCalendar::get_geo()
480{
481 return geo;
482}
Contains all relevant information for specifying a location on Earth: City Name, State/Province name,...
Definition geolocation.h:28
QString fullName() const
void addPoint(const QPointF &p, const QString &label=QString(), double barWidth=0.0)
A subclass of TrailObject that provides additional information needed for most solar system objects.
QColor & color()
GeoLocation * geo()
Definition kstarsdata.h:230
SkyMapComposite * skyComposite()
Definition kstarsdata.h:166
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
Dialog for changing the geographic location of the observer.
virtual QString name(void) const
Definition skyobject.h:145
dms transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) const
QTime transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const
The same iteration technique described in riseSetTime() is used here.
QTime riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact=true) const
Determine the time at which the point will rise or set.
Definition skyobject.cpp:93
int degree() const
Definition dms.h:116
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
GeoCoordinates geo(const QVariant &location)
QString label(StandardShortcut id)
void clicked(bool checked)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QDate date() const const
QPushButton * addButton(StandardButton button)
int pointSize() const const
void setPointSize(int pointSize)
void restoreOverrideCursor()
void setOverrideCursor(const QCursor &cursor)
QIcon fromTheme(const QString &name)
int height() const const
int width() const const
bool begin(QPaintDevice *device)
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
void drawText(const QPoint &position, const QString &text)
bool end()
void setResolution(int dpi)
QString & setNum(double n, char format, int precision)
AlignLeft
WaitCursor
QFuture< T > run(Function function,...)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void msleep(unsigned long msecs)
bool isValid(int h, int m, int s, int ms)
int secsTo(QTime t) const const
void setEnabled(bool)
void render(QPaintDevice *target, const QPoint &targetOffset, const QRegion &sourceRegion, RenderFlags renderFlags)
void repaint()
void setToolTip(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 19 2024 11:51:35 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.